Search code examples
javaspring-bootdatejava-11

Not able to get correct Nano Second after convert Date to ZoneDateTime in JDK 11


Here is the Code snippet

Date is from java.util.Date

ZonedDateTime zonedDateTime = 
        ZonedDateTime.now(ZoneOffset.UTC);
System.out.println("ZonedDateTime before converting from Date, "
                   + "ZonedDateTime Nano is :"+ zonedDateTime.getNano());
Date zonedDateTimeToDate = 
        Date.from(zonedDateTime.toInstant());
System.out.println("Date which is converting from zonedDateTime:"
                   + zonedDateTimeToDate.toString());
ZonedDateTime afterConversionFromDateToZonedDateTime =
        ZonedDateTime.ofInstant(zonedDateTimeToDate.toInstant(),
                                ZoneOffset.UTC);
System.out.println("ZonedDateTime after converting from Date, "
                   + " ZonedDateTime Nano is :"
                   + afterConversionFromDateToZonedDateTime.getNano());

Here is the result of above code

ZonedDateTime before converting from Date, ZonedDateTime Nano is : 221953000
Date which is converting from zonedDateTime:Tue Mar 21 16:00:31 IST 2023
ZonedDateTime after converting from Date, ZonedDateTime Nano is :221000000

you can clearly observe from above output the Nanosecond last 6 digit is different.

Is anyone got this kind of issue. Any help is much appreciated.


Solution

  • The documentation for Date says (emphasis mine):

    The class Date represents a specific instant in time, with millisecond precision.

    whereas the documentation for ZonedDateTime says:

    ZonedDateTime is an immutable representation of a date-time with a time-zone. This class stores all date and time fields, to a precision of nanoseconds, and a time-zone, with a zone offset used to handle ambiguous local date-times.

    Therefore, converting a Date to ZonedDateTime and then back is kind of like converting a double to float and then back - you'd sometimes not get the original value back, because information was lost when converting to float/Date.

    double x = 1.234567891234;
    System.out.println(x); // 1.234567891234
    System.out.println((float)x); // 1.2345679
    System.out.println((double)(float)x); // 1.2345678806304932
    

    Of course, sometimes you do get the original value back, like with the double value 1, which can be represented exactly with float. Or, in this case, with ZonedDateTimes that land exactly on the millisecond. Though, if your system clock has a higher resolution than milliseconds, the chance of getting that from now is pretty low.

    Anyway, if you can, try not using Date at all.