Confusion with Java Time parsing UTC

2019-06-17 18:49发布

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.

2条回答
男人必须洒脱
2楼-- · 2019-06-17 19:40

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());
查看更多
冷血范
3楼-- · 2019-06-17 19:50

tl;dr

Example:

Instant.parse( "2016-09-12T13:15:17.309Z" )
       .atZone( ZoneId.of( "Europe/London" ) )
       .toString();

2016-09-12T14:15:17.309+01:00[Europe/London]

Run in IdeOne.com.

Details

The Answer by Krüske is correct. You misunderstand the meaning of LocalDateTime class. It does not represent the date-time of a particular locality. Just the opposite, it does not represent an actual moment at all.

I suggest thinking of Instant as your basic building-block class in java.time. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Your input string complies with the ISO 8601 format used by default in the Instant class for both parsing and generating string representations. The Z on the end is short for Zulu and means UTC. No need to specify a formatting pattern.

Instant instant = Instant.parse( "2016-09-12T13:15:17.309Z" );

As a programmer you should learn to think and work in UTC primarily. Forget about your own time zone. Think of UTC as the One True Time. Apply a time zone as a variation, and only as needed.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as BST or EST or IST as they are not true time zones, not standardized, and not even unique(!). If by BST you meant British Summer Time, then the actual time zone name would be Europe/London. The java.time classes will determine how to adjust for any anomalies including Daylight Saving Time (DST).

ZoneId z = ZoneId.of( "Europe/London" );
ZonedDateTime zdt = instant.atZone( z );

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to java.time.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

  • Java SE 8 and SE 9 and later
    • Built-in.
    • Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
  • Java SE 6 and SE 7
    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

查看更多
登录 后发表回答