Java 8 解析 ISO-8601 日期,忽略时区的存在(或不存在)
Java 8 Parse ISO-8601 date ignoring presence (or absence) of timezone
我的应用程序应该能够解析忽略时区的日期(我总是确定它是 UTC)。问题是日期可能有以下两种形式 -
2017-09-11T12:44:07.793Z
0001-01-01T00:00:00
我可以使用 LocalDateTime
解析第一个,使用 Instant
class 解析第二个。有没有办法使用单一机制来做到这一点?
P.S。我试图避免在输入字符串
的末尾硬编码 Z
你可以"parseBest",像这样:
DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[Z]");
Temporal parsed = parser.parseBest(inputString, Instant::from, LocalDateTime::from);
然后你应该检查解析了什么,并采取相应的行动。
parseBest
方法适用于任何类型的 TemporalQuery
,包括 java.time
类 上可用的大多数 from
方法。例如,您可以使用 LocalDate.from
来延长该列表。
您还可以使用该方法和 lambda 将解析结果强制转换为您想要的类型,而无需 instanceof
外部结果解析检查(尽管并非没有一次转换):
Instant parsed = (Instant) parser.parseBest(inputString,
Instant::from,
interResult -> LocalDateTime.from(interResult).atZone(ZoneOffset.UTC).toInstant())
请注意,第二个选项使用 lambda 将 LocalDateTime
转换为 ZonedDateTime
,然后再转换为 Instant
,因此解析结果总是强制转换为 Instant
.
如果 Z
偏移量是可选的,您可以使用带有可选部分的 java.time.format.DateTimeFormatterBuilder
:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// date/time
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// optional offset
.optionalStart().appendOffsetId()
// create formatter
.toFormatter();
然后您可以使用 parseBest
方法,使用 TemporalQuery
的列表尝试创建相应的对象。然后你检查 return 类型并采取相应的行动:
Instant instant = null;
// tries to create Instant, and if it fails, try a LocalDateTime
TemporalAccessor parsed = fmt.parseBest("2017-09-11T12:44:07.793Z", Instant::from, LocalDateTime::from);
if (parsed instanceof Instant) {
instant = (Instant) parsed;
} else if (parsed instanceof LocalDateTime) {
// convert LocalDateTime to UTC instant
instant = ((LocalDateTime) parsed).atOffset(ZoneOffset.UTC).toInstant();
}
System.out.println(instant); // 2017-09-11T12:44:07.793Z
运行 第二个输入 (0001-01-01T00:00:00
) 产生等同于 0001-01-01T00:00:00Z
.
的 Instant
在上面的示例中,我只使用了 Instant::from
和 LocalDateTime::from
,因此格式化程序会尝试先创建一个 Instant
。如果不可能,则它会尝试创建 LocalDateTime
。您可以向该列表添加任意数量的类型(例如,我可以添加 ZonedDateTime::from
,如果创建了 ZonedDateTime
,我可以使用 [=28= 转换为 Instant
] 方法)。
你肯定知道输入始终是 UTC,你也可以直接在格式化程序中设置它:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// date/time
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// optional offset
.optionalStart().appendOffsetId()
// create formatter with UTC
.toFormatter().withZone(ZoneOffset.UTC);
所以可以直接解析为Instant
:
System.out.println(Instant.from(fmt.parse("2017-09-11T12:44:07.793Z"))); // 2017-09-11T12:44:07.793Z
System.out.println(Instant.from(fmt.parse("0001-01-01T00:00:00"))); // 0001-01-01T00:00:00Z
我的应用程序应该能够解析忽略时区的日期(我总是确定它是 UTC)。问题是日期可能有以下两种形式 -
2017-09-11T12:44:07.793Z
0001-01-01T00:00:00
我可以使用 LocalDateTime
解析第一个,使用 Instant
class 解析第二个。有没有办法使用单一机制来做到这一点?
P.S。我试图避免在输入字符串
的末尾硬编码Z
你可以"parseBest",像这样:
DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[Z]");
Temporal parsed = parser.parseBest(inputString, Instant::from, LocalDateTime::from);
然后你应该检查解析了什么,并采取相应的行动。
parseBest
方法适用于任何类型的 TemporalQuery
,包括 java.time
类 上可用的大多数 from
方法。例如,您可以使用 LocalDate.from
来延长该列表。
您还可以使用该方法和 lambda 将解析结果强制转换为您想要的类型,而无需 instanceof
外部结果解析检查(尽管并非没有一次转换):
Instant parsed = (Instant) parser.parseBest(inputString,
Instant::from,
interResult -> LocalDateTime.from(interResult).atZone(ZoneOffset.UTC).toInstant())
请注意,第二个选项使用 lambda 将 LocalDateTime
转换为 ZonedDateTime
,然后再转换为 Instant
,因此解析结果总是强制转换为 Instant
.
如果 Z
偏移量是可选的,您可以使用带有可选部分的 java.time.format.DateTimeFormatterBuilder
:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// date/time
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// optional offset
.optionalStart().appendOffsetId()
// create formatter
.toFormatter();
然后您可以使用 parseBest
方法,使用 TemporalQuery
的列表尝试创建相应的对象。然后你检查 return 类型并采取相应的行动:
Instant instant = null;
// tries to create Instant, and if it fails, try a LocalDateTime
TemporalAccessor parsed = fmt.parseBest("2017-09-11T12:44:07.793Z", Instant::from, LocalDateTime::from);
if (parsed instanceof Instant) {
instant = (Instant) parsed;
} else if (parsed instanceof LocalDateTime) {
// convert LocalDateTime to UTC instant
instant = ((LocalDateTime) parsed).atOffset(ZoneOffset.UTC).toInstant();
}
System.out.println(instant); // 2017-09-11T12:44:07.793Z
运行 第二个输入 (0001-01-01T00:00:00
) 产生等同于 0001-01-01T00:00:00Z
.
Instant
在上面的示例中,我只使用了 Instant::from
和 LocalDateTime::from
,因此格式化程序会尝试先创建一个 Instant
。如果不可能,则它会尝试创建 LocalDateTime
。您可以向该列表添加任意数量的类型(例如,我可以添加 ZonedDateTime::from
,如果创建了 ZonedDateTime
,我可以使用 [=28= 转换为 Instant
] 方法)。
你肯定知道输入始终是 UTC,你也可以直接在格式化程序中设置它:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// date/time
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// optional offset
.optionalStart().appendOffsetId()
// create formatter with UTC
.toFormatter().withZone(ZoneOffset.UTC);
所以可以直接解析为Instant
:
System.out.println(Instant.from(fmt.parse("2017-09-11T12:44:07.793Z"))); // 2017-09-11T12:44:07.793Z
System.out.println(Instant.from(fmt.parse("0001-01-01T00:00:00"))); // 0001-01-01T00:00:00Z