2

I'm writing code that reads a data file which contains a Unix timestamp stored in an unsigned 8 byte int that has microsecond accuracy. I'm trying to take this unsigned 8 byte int and turn it into a human readable date accurate to the microsecond.

My issue is, given that Java does not support unsigned longs, I'm struggling to wrap my mind around how to actually implement this. Unfortunately I have to entirely implement this in Java and I cannot sacrifice timestamp accuracy.

bedbug
  • 23
  • 1
  • 4

2 Answers2

3

Endianness

One important piece of information that you didn't provide is the byte endianness of the binary number. I will assume that it's little endian, as in most systems.

Extracting seconds and nanoseconds

Instant in Java 8 supports times specified with seconds since epoch as a long (63 bits without counting the sign), in addition to the nanoseconds.

So the good news is that we are able to print all the dates that could be stored as nanoseconds in the 8-byte unsigned integer.

Since Java only has a signed long 8 byte primitive data type, we need to be careful to not handle the case where the most significant bit is 1 as a negative 2's complement number.

Possible solution

// constants
long nanosPerSecond = 1000_000_000L;
String pattern = "yyyy-MM-dd HH:mm:ss.n";

// assuming the bytes in the file are in little endian byte order
byte[] arr = Files.readAllBytes(path);

// transform to big endian (required by BigInteger)
ArrayUtils.reverse(arr);
// always positive
BigInteger number = new BigInteger(1, arr);

// get seconds since epoch and nano adjustment
long nanoAdjustment = number.mod(BigInteger.valueOf(nanosPerSecond))
  .longValueExact();
long epochSecond = number.divide(BigInteger.valueOf(nanosPerSecond))
  .longValueExact();

Instant instant = Instant.ofEpochSecond(epochSecond, nanoAdjustment);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern).withZone(ZoneId.systemDefault());
System.out.println(formatter.format(instant));

So the latest date we can show, represented by FF FF FF FF FF FF FF FF corresponds to the string 2554-07-22 01:34:33.709551615.

00 00 00 00 00 00 00 00 will be epoch.

Duarte Meneses
  • 2,868
  • 19
  • 22
1

Java doesn't support neither unsigned ints nor 8 byte ints. An integer has a fixed length of 4 bytes = 32 bits. But you can apply unsigned arithmetic operations on both, longs and ints since JDK 8 (Go to Long (or Integer) javadoc page and search for "unsigned").

Quaffel
  • 1,463
  • 9
  • 19
  • I understand that but the binary data file was generated in C, which supports both (an unsigned long long). I'm just trying to figure out how to convert this into something workable in Java. – bedbug Nov 07 '17 at 14:37
  • You could read the file and its content into one long. Then, you apply the unsigned arithmetic operations on it. As you want microsecond-accuracy, the long will no longer have a one as its first bit, so you can proceed with signed behaviour (as the first negative number is 1000...0 in binary representation which equals -10^31-1 in decimal representation). – Quaffel Nov 07 '17 at 14:48