解析 java.time 尝试多种模式

Parse java.time trying multiple patterns

我们有一个库,用户可以在其中传递多种格式的日期。它们遵循 ISO,但有时会缩写。

所以我们得到诸如“19-3-12”和“2019-03-12T13:12:45.1234”之类的东西,其中小数秒的长度可以是 1 - 7 位。这是非常多的组合。

DateTimeFormatter.parseBest 不起作用,因为它不接受 "yy-m-d" 作为本地日期。 不会工作,因为它假设我们知道模式 - 我们不知道。

并且告诉人们获取他们的字符串格式 "correct" 是行不通的,因为存在大量现有数据(这些数据主要在 XML 和 JSON 文件中)。

我的问题是,如何在不必尝试 15 种不同的显式模式的情况下解析以这些不同模式出现的字符串?

或者更好的是,是否有某种方法可以解析字符串,它会尝试所有可能的方法,如果字符串对任何日期[时间]都有意义,return 一个 Temporal 对象?

尝试所有可能的格式比只尝试 15 种格式效果更差。

您可以尝试 "normalize" 到单一格式,但那样您将完成这 15 种格式应该完成的工作。

我认为最好的方法是@JB Nizet 描述的方法,即只尝试匹配字符串长度的模式。

public Date parse(String openFormat) { 
    String[] formats = {"YYY-MM-DD"};
    switch(openFormat.length()) {
       case 24: // 2019-03-12T13:12:45.1234
             formats = new String[]{"YYY-MM-DDThh:mm:ssetcetc", }; // all the formats for length 24
             break;
       ...
       case 6: //YYY-MM-DD, DD-MM-YYYY
             formats = new String[]{YYY-MM-DD", "DD-MM-YYYY", }; // all the formats for length 6
             break;
      }
      Date myDate
      // now try the reduced number of formats, possibly only 1 or 2
      for( String format : formats) try {
          myDate = date parse ( format ) etcetc
      } catch (DateFormatException d) {
          continue;
      } 
      if (myDate == null){
         throw InvalidDate
      } else {
      return myDate
      }
 }

没有完整的规格,很难给出准确的建议。通常用于可变格式的技术包括:

  1. 依次尝试多种已知格式。
  2. 格式模式中的可选部分。
  3. DateTimeFormatterBuilder.parseDefaulting() 对于解析后的字符串中可能不存在的部分。
  4. 如您所知,parseBest

我假设 y-M-d 始终按此顺序出现(例如,永远不会是 M-d-y 或 d-M-y)。 19-3-12 与 ISO 8601 冲突,因为该标准要求(至少)4 位数字年份和 2 位数字月份。 2 位数年份的挑战是猜测世纪:这是 1919 年还是 2019 年,或者可能是 2119 年?

好消息:秒的存在与否以及小数位数的不同数量都是内置的,不会造成任何问题。

根据您告诉我们的内容,在我看来,以下是一个不错的选择。

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendPattern("[uuuu][uu]-M-d")
            .optionalStart()
            .appendLiteral('T')
            .append(DateTimeFormatter.ISO_LOCAL_TIME)
            .optionalEnd()
            .toFormatter();

    TemporalAccessor dt = formatter.parseBest("19-3-12", LocalDateTime::from, LocalDate::from);
    System.out.println(dt.getClass());
    System.out.println(dt);

输出:

class java.time.LocalDate
2019-03-12

我认为它应该适用于您描述的各种格式。让我们试试你的另一个例子:

    dt = formatter.parseBest( "2019-03-12T13:12:45.1234", LocalDateTime::from, LocalDate::from);
    System.out.println(dt.getClass());
    System.out.println(dt);
class java.time.LocalDateTime
2019-03-12T13:12:45.123400

要控制 2 位数年份的解释,您可以使用 DateTimeFormatterBuilder.appendValueReduced() 的重载变体之一。我建议您考虑在其之上进行范围检查。