【问题标题】:DateTimeFormatter cannot parse string produced by itselfDateTimeFormatter 无法解析自己产生的字符串
【发布时间】:2017-03-27 13:34:32
【问题描述】:

我在解析以下日期格式时遇到问题:

2017-03-27T08:27:43.326TGMT-05:00

我有代码,其中DateTimeFormatterZonedDateTime 生成字符串,下一步我试图再次解析这个字符串并创建ZonedDateTime 实例,但是它没有按我的预期工作:

String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'T'ZZZZ";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String dateStr = ZonedDateTime.now().format(formatter);

System.out.println(dateStr);

ZonedDateTime dateParsed = ZonedDateTime.parse(dateStr, formatter);
System.out.println(dateParsed);

上面的代码产生:

2017-03-27T08:27:43.326TGMT-05:00
java.time.format.DateTimeParseException: Text '2017-03-27T08:27:43.326TGMT-05:00' could not be parsed: String index out of range: 33

    at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)

寻找有关如何提出能够从 2017-03-27T08:27:43.326TGMT-05:00 中创建 ZonedDateTime 实例的工作模式的任何见解

【问题讨论】:

  • 将您的模式更改为:String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'T'ZZZZZ";(多一个 Z)并且它可以工作
  • 似乎也只适用于三个 Z。
  • @BrianClapper 但三个 Z 导致输出与 OP 的 ..512T-0500 而不是 ..512TGMT-05:00 略有不同
  • 同意。我只是添加了另一个数据点。 ZZZ 和 ZZZZZ 有效,但 ZZZZ 无效。对我来说,这似乎是 JDK 中的一个错误。无论如何,OP 现在有一些可用的选项。
  • 查看文档(向下滚动)以查看四个和五个 Z 之间的区别,并注意模式字母 x - docs.oracle.com/javase/8/docs/api/java/time/format/…

标签: java datetime-format java-time


【解决方案1】:

为了了解问题所在,我查看了引发异常的DateTimeFormatterBuilder.java:3563 的源代码。

我看到有一段代码甚至可以解析GMT-05:00 之后的秒部分。

所以只有添加秒部分才会起作用...

ZonedDateTime dateParsed = ZonedDateTime.parse(dateStr+":00", formatter);

【讨论】:

  • 谢谢,它是这样工作的,但这仍然是一种奇怪的情况,我想尝试找出更好的解决方案
【解决方案2】:

要了解ZZZZ 模式的作用,请查看JDK docs,您可以从中了解其含义:

Pattern  Count  Equivalent builder methods
-------  -----  --------------------------
ZZZZ     4      appendLocalizedOffset(TextStyle.FULL);

所以,ZZZZ 等价于 appendLocalizedOffset(TextStyle.FULL)。如果你看appendLocalizedOffset方法:

将本地化区域偏移量(例如 'GMT+01:00')附加到格式化程序。 这会将本地化区域偏移量附加到构建器,本地化偏移量的格式由该方法指定的样式控制:

  • full - 具有本地化偏移文本的格式,例如 'GMT,2 位数的小时和分钟字段,可选的第二个字段(如果非零),和冒号。

并注意它在解析和格式化时有不同的行为:

格式化期间,使用等同于使用 TemporalQueries.offset() 查询时间的机制获得偏移量。如果无法获得偏移量,则抛出异常,除非格式化程序的部分是可选的。

解析期间,使用上面定义的格式解析偏移量。如果无法解析偏移量,则抛出异常,除非格式化程序的部分是可选的。

因此,ZZZZ 模式似乎在格式化时添加了GMT-05:00(当它为零时没有秒数),但在解析时,它会尝试解析秒数(即使它们不存在)。 这就是为什么在解析时需要在最后添加:00(就像@freedev 所做的in his answer)。

由于ZZZZ 似乎是唯一在偏移前添加GMT 的模式(所有其他模式只是添加没有GMT 的偏移),我认为唯一的解决方案是手动添加它。您可以使用java.time.format.DateTimeFormatterBuilder 类:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // append the yyyy-MM-dd'T'HH:mm:ss.SSS pattern
    .appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
    // append "T" and "GMT"
    .appendLiteral("TGMT")
    // append the offset (-05:00)
    .appendOffsetId()
    .toFormatter();

使用此格式化程序,您的代码将按预期工作:

String dateStr = ZonedDateTime.now().format(formatter);
System.out.println(dateStr);

ZonedDateTime dateParsed = ZonedDateTime.parse(dateStr, formatter);
System.out.println(dateParsed);

输出:

2017-05-25T08:42:17.799TGMT-05:00

2017-05-25T08:42:17.799-05:00


使用您的输入进行测试:

String input = "2017-03-27T08:27:43.326TGMT-05:00";
System.out.println(input);
System.out.println(ZonedDateTime.parse(input, formatter));

输出:

2017-03-27T08:27:43.326TGMT-05:00

2017-03-27T08:27:43.326-05:00


PS:在这种情况下,我更喜欢使用DateTimeFormatterBuilder,但您也可以直接使用DateTimeFormatter.ofPattern

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'TGMT'xxx");

这完全一样。

【讨论】:

    猜你喜欢
    • 2020-02-27
    • 2020-04-24
    • 2019-09-30
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 2018-10-02
    • 1970-01-01
    • 2021-09-06
    相关资源
    最近更新 更多