Confusion with Java Time parsing UTC

pandaadb picture pandaadb · Sep 12, 2016 · Viewed 9.6k times · Source

I am confused with time handling in java time. I so long worked under the assumption that if a timestamp is specified as a zulu time, java would take care of the offset with regards to local time.

To illustrate. I am currently in BST which has an offset of UTC +1. With that in mind, I would expect this zulu time:

2016-09-12T13:15:17.309Z

to be

2016-09-12T14:15:17.309 

LocalDateTime after parsing it. This is because my default systemtime is set to BST and the above timestamp (zulu time) specifies that it is a UTC time.

Instead however consider this sample:

        String ts = "2016-09-12T13:15:17.309Z";
        LocalDateTime parse = LocalDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println(parse);

This will print:

2016-09-12T13:15:17.309

So the timestamp, parsed as a LocalDateTime, is not recognised as UTC time and instead treated as localtime directly. So I thought, maybe I need to parse it as a ZonedDateTime and convert it to LocalDateTime specifically in order to get the correct local time. With this test:

        String ts = "2016-09-12T13:15:17.309Z";
        ZonedDateTime parse = ZonedDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println(parse);
        System.out.println(parse.toLocalDateTime());

I get the outputs:

2016-09-12T13:15:17.309Z
2016-09-12T13:15:17.309

Same output for both dates.

The only way to correctly parse this that I could find, is:

    String ts = "2016-09-12T13:15:17.309Z";
    Instant instant = Instant.parse(ts); // parses UTC
    LocalDateTime ofInstant = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    System.out.println(instant);
    System.out.println(ofInstant);

This prints:

2016-09-12T13:15:17.309Z
2016-09-12T14:15:17.309

Which is correct.

So the question(s) are:

  • Shouldn't java time recognise a UTC timestamp and parse it to the correct system default?
  • How can I use the LocalDateTime#parse approach to get the correct result?
  • Should I use Instant for everything now and discard the parsing?

The issue is that jersey/jackson's java time modules parse the timestamps using the ISO format and the regular LocalDateTime#parse methods. I realised that my times are no off since they are being treated as LocalTime while in fact they are in Zulu time.

Answer

Michaela Maura Elschner picture Michaela Maura Elschner · Sep 12, 2016

You are misunderstanding the purpose of LocalDateTime.

To quote the class documentation:

A date-time without a time-zone in the ISO-8601 calendar system, such as {@code 2007-12-03T10:15:30}.

This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, combined with the local time as seen on a wall clock. It cannot represent an instant on the time-line without additional information such as an offset or time-zone.

So it's explicit purpose is just to represent a date and time without a time-zone. It's porpose is not to represent a date and time in the local time zone.

Therefore each conversion just strips the time zone.

So for your purposes you need a ZonedDateTime with ZoneId.systemDefault() as you already used in your third example.

For your second example this could be:

String ts = "2016-09-12T13:15:17.309Z";
ZonedDateTime parse = 
    ZonedDateTime.parse(ts, DateTimeFormatter.ISO_DATE_TIME)
        .withZoneSameInstant(ZoneId.systemDefault());
System.out.println(parse);
System.out.println(parse.toLocalDateTime());