Java 8日期和时间:解析偏移中没有冒号的ISO 8601字符串
我们尝试使用时区偏移量解析以下ISO 8601 DateTime字符串:
final String input = "2022-03-17T23:00:00.000+0000"; OffsetDateTime.parse(input); LocalDateTime.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
由于时区偏移中的冒号,两种方法都失败(这有意义,因为OffsetDateTime
也使用DateTimeFormatter.ISO_OFFSET_DATE_TIME
)。
java.time.format.DateTimeParseException:无法在索引23处解析文本’2022-03-17T23:00:00.000 + 0000′
但根据维基百科 ,有4种有效的时区偏移格式:
Z ±hh:mm ±hhmm ±hh
其他框架/语言可以解析这个字符串而没有任何问题,例如Javascript Date()
或Jacksons ISO8601Utils
(他们在这里讨论这个问题)
现在我们可以使用复杂的RegEx编写自己的DateTimeFormatter
,但在我看来, java.time
库应该能够默认解析这个有效的ISO 8601字符串,因为它是有效的。
目前我们使用Jacksons ISO8601DateFormat
,但我们更愿意使用官方的date.time
库来使用。 你有什么方法可以解决这个问题?
如果你想解析所有有效的偏移格式( Z
, ±hh:mm
, ±hhmm
和±hh
),一种方法是使用带有可选模式的java.time.format.DateTimeFormatterBuilder
(不幸的是,似乎没有单一的模式字母匹配他们所有):
DateTimeFormatter formatter = new DateTimeFormatterBuilder() // date/time .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) // offset (hh:mm - "+00:00" when it's zero) .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd() // offset (hhmm - "+0000" when it's zero) .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd() // offset (hh - "Z" when it's zero) .optionalStart().appendOffset("+HH", "Z").optionalEnd() // create formatter .toFormatter(); System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+0000", formatter)); System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00", formatter)); System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00:00", formatter)); System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000Z", formatter));
以上四种情况都将解析为2022-03-17T23:00Z
。
如果需要,还可以定义单个字符串模式,使用[]
分隔可选部分:
// formatter with all possible offset patterns DateTimeFormatter formatter = DateTimeFormatter .ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS[xxx][xx][X]");
此格式化程序也适用于所有情况,就像上面的格式化程序一样。 检查javadoc以获取有关每个模式的更多详细信息。
笔记:
- 具有上述可选部分的格式化程序适用于解析,但不适用于格式化。 格式化时,它将打印所有可选部分,这意味着它将多次打印偏移。 因此,要格式化日期,只需使用另一个格式化程序。
- 第二个格式化程序在小数点后正好接受3位数(因为
.SSS
)。 另一方面,ISO_LOCAL_DATE_TIME
更灵活:秒和纳秒是可选的,它也接受小数点后的0到9位数。 选择最适合您输入数据的那个。
您不需要编写复杂的正则表达式 – 您可以构建一个可以轻松使用该格式的DateTimeFormatter
:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ROOT); OffsetDateTime odt = OffsetDateTime.parse(input, formatter);
这也将接受“Z”而不是“0000”。 它不会接受“+00:00”(使用冒号或类似物。鉴于文档,这是令人惊讶的,但如果您的值始终具有没有冒号的UTC偏移量,那么它应该没问题。
我不会称之为解决方案,而是一种解决方法。 SimpleDateFormat的Z
模板支持您显示的时区语法,因此您可以执行以下操作:
final String input = "2022-03-17T23:00:00.000+0000"; try { OffsetDateTime.parse(input); LocalDateTime.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME); } catch (DateTimeParseException e) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SZ", Locale.GERMANY); sdf.parse(input); }
您仍在使用JVM附带的官方库。 一个不是date.time-library的一部分,但仍然;-)
由于它没有冒号,你可以使用自己的格式字符串:
final String input = "2022-03-17T23:00:00.000+0000"; DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); Date parsed = df.parse(input); System.out.println(parsed);