Java 8解析ISO-8601日期忽略时区的存在(或不存在)

时间:2017-09-25 17:10:02

标签: datetime java-8 java-time iso8601 datetime-parsing

我的应用程序应该能够解析忽略时区的日期(我总是知道它是UTC)。问题是日期可能会出现以下两种形式 -

2017-09-11T12:44:07.793Z

0001-01-01T00:00:00

我可以使用LocalDateTime解析第一个,使用Instant类解析第二个。有没有办法使用单一机制?

P.S。我试图避免在输入字符串

的末尾硬编码Z

2 个答案:

答案 0 :(得分:5)

如果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列表。然后检查返回类型并采取相应措施:

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)运行会产生相当于Instant的{​​{1}}。

在上面的示例中,我只使用了0001-01-01T00:00:00ZInstant::from,因此格式化程序会尝试首先创建LocalDateTime::from。如果不可能,则尝试创建Instant。您可以在列表中添加任意数量的类型(例如,我可以添加LocalDateTime,如果创建了ZonedDateTime::from,我只能使用{{1}转换为ZonedDateTime方法)。

如您所知,输入始终为UTC,您也可以直接在格式化程序中设置:

Instant

因此,您可以直接将其解析为toInstant()

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // date/time
    .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
    // optional offset
    .optionalStart().appendOffsetId()
    // create formatter with UTC
    .toFormatter().withZone(ZoneOffset.UTC);

答案 1 :(得分:4)

你可以“parseBest”,像这样:

DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[Z]");

Temporal parsed = parser.parseBest(inputString, Instant::from, LocalDateTime::from);

然后你应该检查解析的内容,并采取相应的行动。 parseBest方法适用于任何类型的TemporalQuery,包括from类上可用的大多数java.time方法。因此,您可以使用LocalDate.from来延长该列表。

你也可以使用该方法和lambdas来强制解析结果到你想要的类型,而不需要instanceof检查结果解决外部(虽然没有一个强制转换):

Instant parsed = (Instant) parser.parseBest(inputString,
                    Instant::from,
                    interResult -> LocalDateTime.from(interResult).atZone(ZoneOffset.UTC).toInstant())

请注意,第二个选项使用lambda将LocalDateTime转换为ZonedDateTime,然后转换为Instant,因此解析结果始终转换为Instant