日期时间格式化程序可以设置日期格式,但不能读取其自己的格式



我正在尝试解析末尾带有文字'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

解析回像以前一样工作。

最新更新