Java 8 日期和时间:解析 ISO 8601 字符串,偏移量中不带冒号
Java 8 Date and Time: parse ISO 8601 string without colon in offset
我们尝试解析以下带有时区偏移的 ISO 8601 日期时间字符串:
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: Text '2022-03-17T23:00:00.000+0000' could not be parsed at index 23
但根据 Wikipedia 时区偏移量有 4 种有效格式:
<time>Z
<time>±hh:mm
<time>±hhmm
<time>±hh
其他 frameworks/languages 可以毫无问题地解析此字符串,例如Javascript Date()
或 Jacksons ISO8601Utils
(他们讨论这个问题 here)
现在我们可以用复杂的 RegEx 编写我们自己的 DateTimeFormatter
,但在我看来 java.time
库应该能够默认解析这个有效的 ISO 8601 字符串,因为它是一个有效的字符串.
现在我们使用 Jacksons ISO8601DateFormat
,但我们更愿意使用官方 date.time
库来处理。你会用什么方法来解决这个问题?
您不需要编写复杂的正则表达式 - 您可以构建一个可以轻松处理该格式的 DateTimeFormatter
:
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ROOT);
OffsetDateTime odt = OffsetDateTime.parse(input, formatter);
这也将接受 "Z" 而不是“0000”。它会 not 接受“+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-图书馆,但仍然 ;-)
既然没有冒号,能不能用自己的格式字符串:
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);
如果要解析所有有效格式的偏移量(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("yyyy-MM-dd'T'HH:mm:ss.SSS[xxx][xx][X]");
此格式化程序也适用于所有情况,就像上面的先前格式化程序一样。检查 javadoc 以获取有关每个模式的更多详细信息。
备注:
- 带有上述可选部分的格式化程序适用于解析,但不适用于格式化。格式化时,它会打印 all 可选部分,这意味着它会多次打印偏移量。因此,要格式化日期,只需使用另一个格式化程序即可。
- 第二个格式化程序接受小数点后的 3 位数字(因为
.SSS
)。另一方面,ISO_LOCAL_DATE_TIME
更灵活:秒和纳秒是可选的,它也接受小数点后 0 到 9 位数字。选择最适合您的输入数据的那个。
我们尝试解析以下带有时区偏移的 ISO 8601 日期时间字符串:
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: Text '2022-03-17T23:00:00.000+0000' could not be parsed at index 23
但根据 Wikipedia 时区偏移量有 4 种有效格式:
<time>Z
<time>±hh:mm
<time>±hhmm
<time>±hh
其他 frameworks/languages 可以毫无问题地解析此字符串,例如Javascript Date()
或 Jacksons ISO8601Utils
(他们讨论这个问题 here)
现在我们可以用复杂的 RegEx 编写我们自己的 DateTimeFormatter
,但在我看来 java.time
库应该能够默认解析这个有效的 ISO 8601 字符串,因为它是一个有效的字符串.
现在我们使用 Jacksons ISO8601DateFormat
,但我们更愿意使用官方 date.time
库来处理。你会用什么方法来解决这个问题?
您不需要编写复杂的正则表达式 - 您可以构建一个可以轻松处理该格式的 DateTimeFormatter
:
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ROOT);
OffsetDateTime odt = OffsetDateTime.parse(input, formatter);
这也将接受 "Z" 而不是“0000”。它会 not 接受“+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-图书馆,但仍然 ;-)
既然没有冒号,能不能用自己的格式字符串:
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);
如果要解析所有有效格式的偏移量(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("yyyy-MM-dd'T'HH:mm:ss.SSS[xxx][xx][X]");
此格式化程序也适用于所有情况,就像上面的先前格式化程序一样。检查 javadoc 以获取有关每个模式的更多详细信息。
备注:
- 带有上述可选部分的格式化程序适用于解析,但不适用于格式化。格式化时,它会打印 all 可选部分,这意味着它会多次打印偏移量。因此,要格式化日期,只需使用另一个格式化程序即可。
- 第二个格式化程序接受小数点后的 3 位数字(因为
.SSS
)。另一方面,ISO_LOCAL_DATE_TIME
更灵活:秒和纳秒是可选的,它也接受小数点后 0 到 9 位数字。选择最适合您的输入数据的那个。