Java 8日期和时间:在偏移

时间:2017-09-29 10:57:40

标签: java datetime iso8601 timezone-offset datetime-parsing

我们尝试使用时区偏移量解析以下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'

但根据Wikipedia,时区偏移有4种有效格式:

<time>Z 
<time>±hh:mm 
<time>±hhmm 
<time>±hh

其他框架/语言可以解析此字符串而不会出现任何问题,例如Javascript Date()或Jacksons ISO8601Utils(他们讨论此问题here

现在我们可以使用复杂的RegEx编写自己的DateTimeFormatter,但在我看来,java.time库应该能够默认解析这个有效的ISO 8601字符串,因为它是有效的。

目前我们使用Jacksons ISO8601DateFormat,但我们更愿意使用官方date.time库来使用。你解决这个问题的方法是什么?

4 个答案:

答案 0 :(得分:19)

如果您要解析所有有效的偏移格式(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以获取有关每种模式的更多详细信息。

备注:

  • 具有上述可选部分的格式化程序适用于解析,但不适用于格式化。格式化时,它会打印所有可选部分,这意味着它会多次打印偏移量。因此,要格式化日期,只需使用另一个格式化程序。
  • 第二个格式化程序在小数点后正好接受3位数(因为.SSS)。另一方面,ISO_LOCAL_DATE_TIME更灵活:秒和纳秒是可选的,它也接受小数点后的0到9位数。选择最适合您输入数据的那个。

答案 1 :(得分:2)

您不需要编写复杂的正则表达式 - 您可以轻松构建一个可以使用该格式的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偏移量,那么它应该没问题。

答案 2 :(得分:-1)

我不会称之为解决方案,而是一种解决方法。 SimpleDateFormat的map2_chr( as.numeric(substr(time_hour, 1, 2)), substr(time_hour, 3, 4), ~case_when( ((.x == 0) & (.y == "00")) ~ "2330-0000", (as.numeric(.y) > 30) ~ sprintf("%02d30-%02d00", .x, (.x+1)), TRUE ~ sprintf("%02d00-%02d30", .x, .x) ) ) 模板支持您展示的时区语法,因此您可以执行以下操作:

Z

您仍在使用JVM附带的官方库。一个不是date.time-library的一部分,但仍然是; - )

答案 3 :(得分:-1)

由于它没有冒号,你可以使用自己的格式字符串:

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);