【问题标题】:DateTimeFormatter can format a date but can't read its own formatDateTimeFormatter 可以格式化日期但不能读取自​​己的格式
【发布时间】:2020-01-19 03:12:04
【问题描述】:

我正在尝试用文字 'Z' 解析 ISO-8601 日期。

这将正确格式化日期:

String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String string = formatter.format(OffsetDateTime.now());
System.out.println(string);

打印:

2020-01-19T03:06:58.090Z

然后,尝试立即将其读回:

TemporalAccessor acc = formatter.parse(string);
OffsetDateTime time = OffsetDateTime.from(acc);
System.out.println(time);

失败:

Exception in thread "main" java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {MinuteOfHour=6, MilliOfSecond=90, MicroOfSecond=90000, HourOfAmPm=3, NanoOfSecond=90000000, SecondOfMinute=58},ISO resolved to 2020-01-19 of type java.time.format.Parsed
    at java.time.OffsetDateTime.from(OffsetDateTime.java:370)
    at HelloWorld.main(HelloWorld.java:27)
Caused by: java.time.DateTimeException: Unable to obtain ZoneOffset from TemporalAccessor: {MinuteOfHour=6, MilliOfSecond=90, MicroOfSecond=90000, HourOfAmPm=3, NanoOfSecond=90000000, SecondOfMinute=58},ISO resolved to 2020-01-19 of type java.time.format.Parsed
    at java.time.ZoneOffset.from(ZoneOffset.java:348)
    at java.time.OffsetDateTime.from(OffsetDateTime.java:359)
    ... 1 more

我无法将模式更改为使用非文字'Z',但我注意到将其更改为Z,因此它可以成功读取末尾带有+0000 的日期。我也不能更改使用TemporalAccessor 读取日期,因为它来自第三方(杰克逊)。有什么想法吗?

【问题讨论】:

  • 你为什么要将文字 Z 附加到可能具有非 UTC 偏移量的 OffsetDateTime 上?
  • 如果您不控制输入、格式或代码,我们怎么可能提供帮助?
  • 我希望以 UTC 显示日期。我不应该使用OffsetDateTime 吗?奇怪的是,当我打印 System.out.println(OffsetDateTime.now()); 时,我最后得到了一个文字 Z2020-01-19T03:24:13.778Z
  • @shmosel - 我控制格式,但由于客户端 API 要求,格式必须为 2020-01-19T03:24:13.778Z,而不是 2020-01-19T03:24:13.778+0000。代码来自杰克逊,我试图弄清楚为什么它无法解析我的一个对象中的OffsetDateTime field。我做了这个小例子来分享。
  • 我的意思是模式字符串。

标签: java datetime java-8 jackson


【解决方案1】:

由于您使用的是文字 'Z',因此数据中没有时区,因此您必须手动指定一个:

OffsetDateTime time = LocalDateTime.from(acc).atOffset(ZoneOffset.UTC);

【讨论】:

  • 这突出了我的问题,我应该使用 LocalDateTime 而不是 OffsetDateTime。谢谢
  • ZonedDateTime,格式为yyyy-MM-dd'T'HH:mm:ss.SSSz。或Instant。这将有助于包括更多的上下文。但是,如果LocalDateTime 完成了这项工作,无论如何。
  • 错误答案,或者至少不理想。您应该使用OffsetDateTimeInstant。您不需要任何格式化程序,如果您使用格式化程序,则绝不能将Z 硬编码为文字。使用模式字母大写X 进行格式化和解析。
  • 恭喜你 40 k。
【解决方案2】:

Z 不是文字

ISO 8601 格式中的Z 与 UTC(也称为祖鲁时区)的偏移量为零。您需要对其进行格式化和解析,否则您将得到不正确的结果。 OffsetDateTime.now() 可能并且通常确实返回具有非零偏移量的日期时间。当您使用 Z 作为偏移量打印它时,您打印的信息不正确,是不同的时间点。

我无法更改模式以使用非文字“Z”,

我希望我误解了这部分。在我看来,您遇到了无法修复的错误。

您不需要格式化程序

OffsetDateTime 和 java.time 中的其他日期和时间类从它们的 toString 方法打印 ISO 8601 格式并解析相同的格式。所以你不需要明确指定任何格式化程序。

Instant 类可能比 OffsetDateTime 更符合您的要求。 Instant 是与时区和偏移量无关的时间点。它的toString 方法在UTC 中生成一个ISO 8601 字符串,因此最后总是带有Z

    String string = Instant.now().toString();
    System.out.println(string);

    // Parse back
    Instant time = Instant.parse(string);
    System.out.println(time);

我刚才运行这个的时候,输出是:

2020-01-19T05:37:29.630135Z
2020-01-19T05:37:29.630135Z

如果您可以要求传入的日期时间字符串始终具有 Z 作为偏移量,我应该说这是适合您的解决方案。如果需要,您始终可以在解析后将Instant 转换为不同的类型。

如果你喜欢OffsetDateTime:

    String string = OffsetDateTime.now().toString();
    System.out.println(string);

    // Parse back
    OffsetDateTime time = OffsetDateTime.parse(string);
    System.out.println(time);

我所在时区的示例输出:

2020-01-19T06:37:29.721666+01:00
2020-01-19T06:37:29.721666+01:00

您注意到我的时区的正确偏移量+01:00 打印出来而不是Z,这很好地说明了Z 是不正确的。如果您想要 UTC 时间,只需告诉 OffsetDateTime,因此通过将 ZoneOffseet.UTC 传递给 now 方法:

        String string = OffsetDateTime.now(ZoneOffset.UTC).toString();
        System.out.println(string);
2020-01-19T05:43:06.700402Z

解析回像以前一样工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    • 2020-11-30
    • 2013-10-09
    • 2014-05-30
    • 1970-01-01
    • 1970-01-01
    • 2021-01-20
    相关资源
    最近更新 更多