2

I've been trying to read the binary file with Java, and the binary file is written in C#. And some of those data is contain a DateTime data.

When DateTime data will be written into the file (in binary), it using DateTime.ToBinary(); on C#.

For reading the DateTime data, it will convert first from bytes into long data, using BitConverter.ToInt64(byte[], 0), and then convert it again from long into DateTime data using DateTime.FromBinary(long). (All of those are written in C#).

Let's say the long data after converting from bytes is = -8586803256090942249, and when convert it into DateTime, it will return = 3/17/2018 5:07:56 PM

Now, I'm trying to read that binary file with Java. And for converting the bytes data into long data, I'm using this code : ByteBuffer.wrap(byte[]).order(ByteOrder.LITTLE_ENDIAN).getLong().

It will return the exact long data value as C# did. But when I try to convert it from long data into DateTime in Java, using Date date = new Date(long), it will return = Sun May 06 19:04:17 WIB 272097407 instead.

Can you help me what is the correct solution for this ? Is there any equivalent for DateTime.FromBinary() from C# in Java ? Or is my code wrong ? All of yours answers is really appreciated.

imbagila
  • 513
  • 1
  • 8
  • 26
  • Seems there's no equivalent of `DateTime.FromBinary()` in Java libraries. However, sounds interesting to use [`DateTimeOffset.ToUnixTimeMilliseconds()`](https://msdn.microsoft.com/en-us/library/dn823277(v=vs.110).aspx) and put its generated value in Java's `Date()` argument (because C# `DateTime` starts from `0001/01/01` while Java's `Date` follows Unix epoch format, i.e. from `1970/01/01`). – Tetsuya Yamamoto Jul 16 '18 at 06:54
  • Can I do the opposite instead ? I mean is it possible to do with Java to convert the long data into DateTime that start from `0001/01/01` ? – imbagila Jul 16 '18 at 07:23
  • Is 3/17/2018 5:07:56 PM in Western Indonesian Time too? – Ole V.V. Jul 16 '18 at 07:34
  • @imbaglia The C# part can also convert from Unix time format to `DateTime`: [`DateTimeOffset.FromUnixTimeMilliseconds(millis).LocalDateTime`](https://msdn.microsoft.com/en-us/library/system.datetimeoffset.fromunixtimemilliseconds(v=vs.110).aspx). Make sure the Java code returns Unix epoch format first, then use that function to `DateTime` conversion in C# side. – Tetsuya Yamamoto Jul 16 '18 at 08:00
  • @OleV.V. yes 3/17/2018 5:07:56 PM in Western Indonesian Time as well – imbagila Jul 16 '18 at 08:14

1 Answers1

3

In Java:

    long fromBytes = -8586803256090942249L;

    // Mask out kind and ticks
    int kind = Math.toIntExact((fromBytes >> 62) & 0x3);
    long ticks = fromBytes & 0x3FFF_FFFF_FFFF_FFFFL;
    LocalDateTime cSharpEpoch = LocalDate.of(1, Month.JANUARY, 1).atStartOfDay();
    // 100 nanosecond units or 10^-7 seconds
    final int unitsPerSecond = 10_000_000;
    long seconds = ticks / unitsPerSecond;
    long nanos = (ticks % unitsPerSecond) * 100;
    LocalDateTime ldt = cSharpEpoch.plusSeconds(seconds).plusNanos(nanos);

    switch (kind) {
    case 0: // Unspecified
    case 2: // Local time
        System.out.println("Result LocalDateTime: " + ldt);
        break;

    case 1: // UTC
        OffsetDateTime utcDateTime = ldt.atOffset(ZoneOffset.UTC);
        System.out.println("Result OffsetDateTime in UTC: " + utcDateTime);
        break;

    default:
        System.out.println("Not a valid DateTimeKind: " + kind);
        break;
    }

Output:

Result LocalDateTime: 2018-03-17T10:07:56.383355900

Edit: The number is

A 64-bit signed integer that encodes the Kind property in a 2-bit field and the Ticks property in a 62-bit field.

Tetsuya Yamamoto was correct so far as the ticks property denotes number of 100-nanosecond intervals elapsed since 0001/01/01 at start of day (midnight). The kind is either 0 for unspecified, 1 for UTC or 2 for local time. So I am masking the kind and the ticks out separately.

Even though the kind is 2 in your case, which should be for local time, it seems that the time is indeed in UTC. It’s the only way that the time printed could agree with your expected 5:07:56 PM Western Indonesian Time. Maybe the number was generated on a computer with its time zone set to UTC.

To get the time in your time zone:

    ZoneId targetZone = ZoneId.of("Asia/Jakarta");
    ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC).withZoneSameInstant(targetZone);
    System.out.println("Converted to target time zone: " + zdt);

Converted to target time zone: 2018-03-17T17:07:56.383355900+07:00[Asia/Jakarta]

This agrees with what you said you got on the C# side.

PS Avoid the Date class in Java if you can, it is long outdated and poorly designed and was replaced many years ago now by java.time, the modern Java date and time API (which I am of course using in the above). If you do need a Date for a legacy API that you cannot change or don’t want to change just now, as you already noted in a comment, the conversion is like this:

    Instant inst = ldt.atOffset(ZoneOffset.UTC).toInstant();
    Date date = Date.from(inst);
    System.out.println(date);

Output on a JVM with default time zone Asia/Jakarta:

Sat Mar 17 17:07:56 WIB 2018

Acknowledgement: Andreas in an answer (link below) explained the structure of the 64 bits number and gave the link to the documentation. I have taken them from there.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Oh rarely using that package. What does result instant means ? It means that's date time on GMT+0 ? – imbagila Jul 16 '18 at 08:23
  • Yes, the time 10:07:56 in the first line of output is in UTC. The `Z` at the end means just that (or offset zero) (the format of the output is ISO 8601). – Ole V.V. Jul 16 '18 at 08:24
  • 1
    Oh finally I can convert it that `Instant` data into date with `Date date = Date.from(instantDate);`. Thanks a lot @OleV.V. – imbagila Jul 16 '18 at 09:19
  • @imbagila Yes, you can convert `Instant` to a `java.util.Date`, but do so only if you must, such as interfacing with old code not yet updated to the *java.time*. Otherwise avoid `Date`, `Calendar` and the other old legacy classes. They are entirely supplanted by the modern *java.time* classes. – Basil Bourque Jul 16 '18 at 15:30