Java 8 - DateTimeFormatter and ISO_INSTANT issues with ZonedDateTime

后端 未结 4 1306
故里飘歌
故里飘歌 2020-12-13 23:59

So I would expect this code to work under the new Java 8 date/time package since all it does is to convert a given ZonedDateTime to string and back using the same built-in D

相关标签:
4条回答
  • 2020-12-14 00:07

    The ISO_INSTANT formatter is documented here - "This is a special case formatter intended to allow a human readable form of an Instant". As such, this formatter is intended for use with an Instant not a ZonedDateTime.

    Formatting

    When formatting, ISO_INSTANT can format any temporal object that can provide ChronoField.INSTANT_SECONDS and ChronoField.NANO_OF_SECOND. Both Instant and ZonedDateTime can provide these two fields, thus both work:

    // works with Instant
    Instant instant = Instant.now();
    System.out.println(DateTimeFormatter.ISO_INSTANT.format(instant));
    
    // works with ZonedDateTime 
    ZonedDateTime zdt = ZonedDateTime.now();
    System.out.println(zdt.format(DateTimeFormatter.ISO_INSTANT));
    
    // example output
    2014-09-02T08:05:23.653Z
    

    Parsing

    When parsing, ISO_INSTANT will only produce ChronoField.INSTANT_SECONDS and ChronoField.NANO_OF_SECOND. An Instant can be built from those two fields, but ZonedDateTime requires a ZoneId as well:

    To parse a ZonedDateTime it is essential that a time-zone ZoneId is present. The time-zone can be (a) parsed from the string, or (b) specified to the formatter (using JDK 8u20):

    // option a - parsed from the string
    DateTimeFormatter f = DateTimeFormatter.ISO_DATE_TIME;
    ZonedDateTime zdt = ZonedDateTime.parse("2014-09-02T08:05:23.653Z", f);
    
    // option b - specified in the formatter - REQUIRES JDK 8u20 !!!
    DateTimeFormatter f = DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.systemDefault());
    ZonedDateTime zdt = ZonedDateTime.parse("2014-09-02T08:05:23.653Z", f);
    

    See documentation for ISO_ZONED_DATE_TIME, ISO_OFFSET_DATE_TIME and ISO_DATE_TIME (any of these three can be used to parse a ZonedDateTime without specifying withZone()).

    Summary

    The ISO_INSTANT formatter is a special case formatter designed to work with Instant. If you are using a ZonedDateTime you should use a different formatter, such as ISO_DATE_TIME or ISO_ZONED_DATE_TIME.

    0 讨论(0)
  • 2020-12-14 00:09

    If you're using org.threeten.bp from Java 1.8, here's a Cheat Sheet on dealing with String to Date and vice versa.

    Date to String

    val date = Date()
    val calendar = Calendar.getInstance().apply { time = date }
    val zonedDateTime = DateTimeUtils.toZonedDateTime(calendar)
    val formattedDate = DateTimeFormatter.ISO_INSTANT.format(zonedDateTime)
    

    String to Date

    val dateString = "2020-03-04T09:04:43.835Z"
    val dateInstant = Instant.from(DateTimeFormatter.ISO_INSTANT.parse(dateString))
    DateTimeUtils.toDate(dateInstant)
    
    0 讨论(0)
  • 2020-12-14 00:20

    I don't know if it is the expected behaviour or not (it probably is) but technically the ISO_INSTANT formatter does not include a time zone. If you try with a DateTimeFormatter.ISO_ZONED_DATE_TIME formatter instead you will get what you expect.

    0 讨论(0)
  • 2020-12-14 00:21

    I'm not sure, but this might be a bug in Java 8. Maybe it was intended to behave in this way, but I think that the workaround I'm going to propose to you should be the default behavior (when no ZoneId is specified, just take system default):

    ZonedDateTime now = ZonedDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT
        .withZone(ZoneId.systemDefault());
    System.out
        .println(ZonedDateTime.parse(now.format(formatter), formatter));
    

    There's a similar bug which was fixed in OpenJDK: JDK-8033662 - but it's only similar, not exactly the same.

    0 讨论(0)
提交回复
热议问题