【问题标题】:Java 8 - DateTimeFormatter and ISO_INSTANT issues with ZonedDateTimeJava 8 - ZonedDateTime 的 DateTimeFormatter 和 ISO_INSTANT 问题
【发布时间】:2014-09-01 19:45:47
【问题描述】:

所以我希望这段代码可以在新的 Java 8 日期/时间包下工作,因为它所做的只是使用相同的内置 DateTimeFormatter 实例 (ISO_INSTANT) 将给定的 ZonedDateTime 转换为字符串并返回:

ZonedDateTime now = ZonedDateTime.now();
System.out.println(ZonedDateTime.parse(
    now.format(DateTimeFormatter.ISO_INSTANT),
    DateTimeFormatter.ISO_INSTANT));

但显然不是:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2014-09-01T19:37:48.549Z' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {MilliOfSecond=549, NanoOfSecond=549000000, MicroOfSecond=549000, InstantSeconds=1409600268},ISO of type java.time.format.Parsed
    at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1918)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1853)
    at java.time.ZonedDateTime.parse(ZonedDateTime.java:597)

我已经看过这个条目,但它对我没有帮助,因为需要 ZonedDateTime 对象而不是本地对象,还因为我已经安装了 8u20:Unable to obtain ZonedDateTime from TemporalAccessor using DateTimeFormatter and ZonedDateTime in Java 8

有人知道这里发生了什么吗?

【问题讨论】:

    标签: java java-8 java-time


    【解决方案1】:

    ISO_INSTANT 格式化程序记录在 here - “这是一种特殊情况格式化程序,旨在允许即时的人类可读形式”。因此,此格式化程序旨在用于 Instant 而不是 ZonedDateTime

    格式化

    格式化时,ISO_INSTANT 可以格式化任何可以提供ChronoField.INSTANT_SECONDSChronoField.NANO_OF_SECOND 的时间对象。 InstantZonedDateTime 都可以提供这两个字段,因此都可以工作:

    // 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
    

    解析

    解析时,ISO_INSTANT 只会产生ChronoField.INSTANT_SECONDSChronoField.NANO_OF_SECOND。可以从这两个字段构建 Instant,但 ZonedDateTime 也需要 ZoneId

    要解析ZonedDateTime,必须存在ZoneId 时区。时区可以 (a) 从字符串中解析,或 (b) 指定给格式化程序(使用 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);
    

    请参阅ISO_ZONED_DATE_TIMEISO_OFFSET_DATE_TIMEISO_DATE_TIME 的文档(这三个中的任何一个都可用于解析ZonedDateTime 而无需指定withZone())。

    总结

    ISO_INSTANT 格式化程序是一种特殊的格式化程序,旨在与 Instant 一起使用。如果您使用的是ZonedDateTime,则应使用不同的格式化程序,例如ISO_DATE_TIMEISO_ZONED_DATE_TIME

    【讨论】:

    • 奇怪的是生成的字符串最后的 Z 是 UTC 的时区指示符,所以字符串确实包含时区指示符 (en.wikipedia.org/wiki/ISO_8601#Time_zone_designators)。但是,文档确实声明这只会在瞬间起作用,所以你是绝对正确的。谢谢。
    【解决方案2】:

    我不确定,但这可能是 Java 8 中的一个错误。也许它本来打算以这种方式运行,但我认为我要向您建议的解决方法应该是默认行为(当没有指定ZoneId,取系统默认):

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

    OpenJDK 中修复了一个类似的错误:JDK-8033662 - 但它只是相似,并不完全相同。

    【讨论】:

      【解决方案3】:

      我不知道这是否是预期的行为(可能是),但从技术上讲,ISO_INSTANT 格式化程序不包括时区。如果您尝试使用DateTimeFormatter.ISO_ZONED_DATE_TIME 格式化程序,您将得到您所期望的。

      【讨论】:

        【解决方案4】:

        如果您使用的是 Java 1.8 中的 org.threeten.bp,这里有一个 备忘单,介绍如何处理 String to Date 以及反之亦然。

        日期转字符串

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

        字符串到日期

        val dateString = "2020-03-04T09:04:43.835Z"
        val dateInstant = Instant.from(DateTimeFormatter.ISO_INSTANT.parse(dateString))
        DateTimeUtils.toDate(dateInstant)
        

        【讨论】:

          猜你喜欢
          • 2014-06-29
          • 1970-01-01
          • 2019-01-21
          • 1970-01-01
          • 2018-01-22
          • 2021-11-05
          • 1970-01-01
          • 1970-01-01
          • 2018-07-26
          相关资源
          最近更新 更多