convert from millis since Epoch to LocalDateTime in Java 10 vs Java 8

Viewed 1408

I have an application where I use milliseconds since Epoch to convert to LocalDateTime and back, the application was working well on Java 8 but I tried to update to Java 10 and found the following issue

This is the output in Java 8

 ✘ magg@MacBook-Pro-de-Miguel  ~/Desktop/CODE  java TimeTest
2018-09-06T20:13:30.253
1536290010253
2018-09-06T20:13:30.253
 ✘ magg@MacBook-Pro-de-Miguel  ~/Desktop/CODE  java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

This is the output in Java 10, if you see the format is 6 digits long at the end not three like in Java 8 and this breaks the conversion from milliseconds since Epoch to LocalDateTime

✘ magg@MacBook-Pro-de-Miguel  ~/Desktop/CODE  java TimeTest
2018-09-06T20:13:18.568414
1536289998568
2018-09-06T20:13:18.568
 ✘ magg@MacBook-Pro-de-Miguel  ~/Desktop/CODE  java -version
java version "10.0.2" 2018-07-17
Java(TM) SE Runtime Environment 18.3 (build 10.0.2+13)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10.0.2+13, mixed mode)

Here is the source code

public class TimeTest{


public static void main(String[] args)
{

TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));

LocalDateTime issueTime = LocalDateTime.now();

System.out.println(issueTime.toString());

ZonedDateTime zdt = issueTime.atZone(TimeZone.getDefault().toZoneId());
long timeInMillis = zdt.toInstant().toEpochMilli();
System.out.println(timeInMillis );

LocalDateTime date = LocalDateTime.ofInstant(Instant.ofEpochMilli(timeInMillis), TimeZone.getDefault().toZoneId());

System.out.println(date.toString());
}
}

How can I fix this in Java 10?

1 Answers

First, the output from LocalDateTime.toString() is not of fixed length. From its Javadoc (which is unchanged from 8 to 10):

public String toString()
Outputs this date-time as a String, such as 2007-12-03T10:15:30.
The output will be one of the following ISO-8601 formats:

uuuu-MM-dd'T'HH:mm
uuuu-MM-dd'T'HH:mm:ss
uuuu-MM-dd'T'HH:mm:ss.SSS
uuuu-MM-dd'T'HH:mm:ss.SSSSSS
uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS
The format used will be the shortest that outputs the full value of the time where the omitted parts are implied to be zero.

Your Java 10 run happened to print the seconds value with six decimal places, but it might have printed it with three decimal places or nine decimal places depending on a couple of factors:

  • The computer's clock, which may or may or not be capable of nano precision.
  • The actual value returned by now().

It's statistically unlikely, but definitely possible, that it could be expressed using just three decimal places rather than six. And it's far more likely that nine decimal places rather than six would be needed if your hardware supports that. When I ran your code under Java 10 there were usually nine decimal places printed, but occasionally only six were required.

Second, in Java 9 "the system clocks returned by java.time.Clock" were changed "so that they offer at least the same precision than the underlying clock available on the system". See JDK-8068730 Increase the precision of the implementation of java.time.Clock.systemUTC() which explicitly addresses your situation:

An application making the assumption that the clock returned by these system factory methods will always have milliseconds precision and actively depends on it, may therefore need to be updated in order to take into account the possibility of a greater resolution...

With regard to your question "How can I fix this in Java 10?", I'm not sure what you would want to fix, since I don't know the details of your application's requirements.

A comment from teppic already explained how you can constrain the precision to just three decimal places using LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS), but I don't see the benefit of doing that unless you have an application requirement for a fixed precision of three decimal places. An alternative approach might be to use nano precision, and zero pad to nine digits if necessary; then you would obtain the benefit of greater precision, yet still have fixed length values.

Related