Java 8 DateTimeFormatter解析可选部分

时间:2018-07-04 15:24:57

标签: java java-8 java-time date-parsing

我需要将日期时间解析为两种不同格式的字符串:

  • 19861221235959Z
  • 1986-12-21T23:59:59Z

以下dateTimeFormatter模式可以正确解析第一种日期字符串

DateTimeFormatter.ofPattern ("uuuuMMddHHmmss[,S][.S]X")

但第二个失败,因为破折号,冒号和T不在预期范围内。

我试图使用如下可选部分:

DateTimeFormatter.ofPattern ("uuuu[-]MM[-]dd['T']HH[:]mm[:]ss[,S][.S]X")

出乎意料的是,这会解析第二种日期字符串(带破折号的日期字符串),而不是第一种日期字符串,并抛出一个

java.time.format.DateTimeParseException: Text '19861221235959Z' could not be parsed at index 0

好像可选部分没有被评估为可选...

4 个答案:

答案 0 :(得分:9)

正如Peter在评论中所说,问题在于您的模式正在将整个字符串视为年份。您可以使用.appendValue(ChronoField.YEAR, 4)将其限制为四个字符:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendValue(ChronoField.YEAR, 4)
    .appendPattern("[-]MM[-]dd['T']HH[:]mm[:]ss[,S][.S]X")
    .toFormatter();

这可以正确解析您的两个示例。

如果您希望更加冗长,可以这样做:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendValue(ChronoField.YEAR, 4)
    .optionalStart().appendLiteral('-').optionalEnd()
    .appendPattern("MM")
    .optionalStart().appendLiteral('-').optionalEnd()
    .appendPattern("dd")
    .optionalStart().appendLiteral('T').optionalEnd()
    .appendPattern("HH")
    .optionalStart().appendLiteral(':').optionalEnd()
    .appendPattern("mm")
    .optionalStart().appendLiteral(':').optionalEnd()
    .appendPattern("ss")
    .optionalStart().appendPattern("X").optionalEnd()
    .toFormatter();

答案 1 :(得分:2)

从文档中尚不清楚,但是我猜测是以下情况。

当您在格式模式字符串中使用uuuuMMddHHmmss时,格式化程序可以轻松地看到有几个相邻的数字字段,因此可以使用字段宽度来分隔字段。前4位数字表示年份,依此类推。

相反,当您使用uuuu[-]MM[-]dd['T']HH[:]mm[:]ss时,格式化程序不会将其视为相邻的数字字段。我同意彼得·劳瑞(Peter Lawrey)的评论,因此,它需要较长的数字来表示年份,最终超过最大年份(999999999)并抛出异常。

解决方案?请参阅Michael’s answer

答案 2 :(得分:1)

基于模式的

DateTimeFormatter不够聪明,无法处理可选节和具有两个数字字段且不分隔的可能性。当确实需要数字字段不带分隔符时,不问任何问题,则模式会理解,模式字母从u更改为M意味着它需要对数字进行计数才能知道哪个数字是哪个字段的一部分。但是,如果不能确定,则该模式不会尝试这种方法。它看到一个完整描述的数字字段,而不是紧随其后的是另一个数字字段。因此,没有理由计算数字。所有数字都是应该在此处表示的字段的一部分。

为此,您不应该尝试使用模式来构建DateTimeFormatter,而应该使用Builder。从DateTimeFormatter.BASIC_ISO_DATE和附近的其他人那里汲取灵感。

答案 3 :(得分:0)

乍一看,您的第二种格式应适用于两种情况。不知道为什么没有。顺便说一句,我很好奇为什么你用“ u”而不是“ y”一年了。因此,我也会尝试使用“ y”,只是看它是否有所不同。但总的来说,您会谈到一个有趣的问题-如何从未知格式解析日期(想象一下,您正在处理的是未知数量的格式,而不是2种可能的格式)。我实际上曾经写过一个这样的解析器。我的文章Java 8 java.time package: parsing any string to date中描述了我用来解决此问题的想法。您可能会觉得这个想法有用。简而言之,我们的想法是拥有一个包含所有受支持格式的外部文件,并尝试一种一种地应用每种格式,直到一种可行为止。