我正在尝试解析末尾带有文字'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'
,因此数据中没有时区,因此您必须手动指定一个时区:
OffsetDateTime time = LocalDateTime.from(acc).atOffset(ZoneOffset.UTC);
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时间,只需通过ZoneOffseet.UTC
传递给now
方法告诉OffsetDateTime
:
String string = OffsetDateTime.now(ZoneOffset.UTC).toString();
System.out.println(string);
2020-01-19T05:43:06.700402Z
解析回像以前一样工作。