Java:解析 ISO_DATE / ISO_OFFSET_DATE

Pet*_*ser 6 java iso date timezone-offset java-time

对于 REST Web 服务,我需要返回带有时区的日期(无时间)

ZonedDate显然, Java 中没有 a 这样的东西(只有LocalDateZonedDateTime),所以我使用它ZonedDateTime作为后备。

将这些日期转换为 JSON 时,我使用DateTimeFormatter.ISO_OFFSET_DATE格式化日期,效果非常好:

DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE;
ZonedDateTime dateTime = ZonedDateTime.now();
String formatted = dateTime.format(formatter);
Run Code Online (Sandbox Code Playgroud)

2018-04-19+02:00

然而,尝试解析这样的日期......

ZonedDateTime parsed = ZonedDateTime.parse(formatted, formatter);
Run Code Online (Sandbox Code Playgroud)

...导致异常:

java.time.format.DateTimeParseException:无法解析文本“2018-04-19+02:00”:无法从 TemporalAccessor 获取 ZonedDateTime:{OffsetSeconds=7200},ISO 解析为 java 类型的 2018-04-19。时间.格式.解析

我也尝试过ISO_DATE并遇到了同样的问题。

我如何解析这样的分区日期?
或者是否有任何其他类型(在 Java Time API 内)我应该用于分区日期?

小智 6

问题是ZonedDateTime需要构建所有日期和时间字段(年、月、日、小时、分钟、秒、纳秒),但格式化程序ISO_OFFSET_DATE生成一个没有时间部分的字符串。

当解析它时,没有与时间相关的字段(小时、分钟、秒),并且您得到一个DateTimeParseException.

解析它的一种替代方法是使用 aDateTimeFormatterBuilder并为时间字段定义默认值。正如您atStartOfDay在答案中所使用的那样,我假设您想要午夜,因此您可以执行以下操作:

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // date and offset
    .append(DateTimeFormatter.ISO_OFFSET_DATE)
    // default values for hour and minute
    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
    .toFormatter();
ZonedDateTime parsed = ZonedDateTime.parse("2018-04-19+02:00", fmt); // 2018-04-19T00:00+02:00
Run Code Online (Sandbox Code Playgroud)

您的解决方案也可以正常工作,但唯一的问题是您解析输入两次(每次调用formatter.parse都会再次解析输入)。更好的替代方法是使用parse没有临时查询的方法(仅解析一次),然后使用解析的对象来获取所需的信息。

DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE;
// parse input
TemporalAccessor parsed = formatter.parse("2018-04-19+02:00");

// get data from the parsed object
LocalDate date = LocalDate.from(parsed);
ZoneId zone = ZoneId.from(parsed);
ZonedDateTime restored = date.atStartOfDay(zone); // 2018-04-19T00:00+02:00
Run Code Online (Sandbox Code Playgroud)

使用此解决方案,输入仅被解析一次。


Pet*_*ser 2

我找到了解决方案(使用TemporalQueries):
分别解析日期和区域,并使用该信息恢复分区日期:

LocalDate date = formatter.parse(formatted, TemporalQueries.localDate());
ZoneId zone = formatter.parse(formatted, TemporalQueries.zone());
ZonedDateTime restored = date.atStartOfDay(zone);
Run Code Online (Sandbox Code Playgroud)