java 中的自动日期格式识别

Automatic date format recognition in java

我想解析 Java 中的一些日期,但格式未定义并且可能有很多(任何已经很多的 ISO-8601 格式,任何单位的 Unix 时间戳,以及更多的) 以下是一些示例:

完美的解析可能是不可能的,因为模棱两可的情况,但是,用一些逻辑解析大多数常见日期的解决方案可能是可以实现的(例如,时间戳以秒/毫/微/纳米为单位考虑,以便给出接近 2000 年的日期,像“08/07/2021”这样的日期可能有默认的月份和日期区分)。 我没有在 Java 中找到任何简单的方法来做到这一点,而在 python 中使用 infer_datetime_format熊猫函数 to_datetime (https://pandas.pydata.org/docs/reference/api/pandas.to_datetime.html).

在Java中有一些简单的方法吗?

我不知道任何具有此功能的标准库,但您始终可以使用 DateTimeFormatter class 并猜测格式循环遍历预定义格式列表,或使用由这个class.

这是您要归档的典型近似值。

在这里你可以看到旧的实现https://balusc.omnifaces.org/2007/09/dateutil.html

The perfect parsing might be impossible because of ambiguous cases but, a solution to parse most of the common dates with some logical might be achievable

当然,如此广泛的猜测绝对不应该成为标准 java.* API 的一部分。我认为您也大大低估了歧义。 1234567890?说这可以合理地解析是完全错误的。

您 运行 遇到很多很多问题:

  • Java 通常更喜欢抛出错误而不是猜测。这是语言固有的(java 有几个可选的语法结构;分号不是可选的,() 方法调用不是可选的,java 有意没有 'truthy/false' ,即 if (foo) 仅在 foo 是布尔类型的表达式时才有效,不像 python 你可以在其中粘贴任何东西并且有一个很大的列表,其中包含其余的考虑真实性。在罗马时,像罗马人一样:如果这个原则让你烦恼,那么,要么学会爱它,不情愿地接受它,要么用另一种语言编程。这个想法在整个生态系统中很普遍。为了什么值得一提的是,鉴于调试往往比输入可选结构花费的时间要长得多,java 客观上是正确的,或者至少做出这样的理性决定。

  • 要么你不能引入'hey, this number is larger than 12, therefore it cannot possibly be the month'、的概念,你必须接受某个日期格式是否正确解析取决于日期值是高于还是低于 12。我会 强烈 提倡您避免使用像瘟疫一样不符合此规则的库。到底有什么可能的点? “我的应用程序将正确解析您的日期,但仅适用于所有日期的大约 3/5?”因此,鉴于您不能't/should 不考虑这一点,1234567890 是自 1970 年以来的秒数吗?自 1970 年以来的毫秒数?是5678年34月12日,第90个小时,分秒毫秒都为零吗?如果图书馆猜测,那个图书馆是错误的,因为除非你有 95%+ 的把握,否则你不应该猜测。

  • 明显的常年“不要猜”的例子当然是101112,那是2012年11月10日(欧式)吗?是 2012 年 10 月 11 日(美式)还是 2010 年 11 月 12 日(ISO 式)?这些都是合理的猜测,因此这里的猜测是错误的。做。不是。猜测。除非你真的确定。鉴于这是输入日期的一种比较常见的方式,因此:不惜一切代价进行猜测在客观上是愚蠢的(见上文)。仅在非常清楚的情况下进行猜测并且在其他情况下出错大多是无用的,因为歧义很容易引入。

  • 猜测的概念可能是站得住脚的,但前提是要有更多的信息。例如,如果你给我输入'101112100000',这里就不可能猜对了。但是如果你还告诉我这个输入是一个人输入的,并且那个人清楚地知道,比如说,德语语言环境,那么我可以看到需要能够把它变成“2012 年 11 月 10 日,10 点钟” the morning': 解释为秒或毫秒,因为某些时期被人为因素排除,日-月-年顺序由语言环境决定。

您问的是:

Are there some easy approach in Java?

整个问题都不正确。 in Java 部分需要从这个问题中剥离,然后答案很简单:不。没有比输入字符串更多的信息将字符串解析为 date/times 的简单方法。如果另一个图书馆说他们可以做到这一点,那他们就是在撒谎,或者至少是在我的腿上的文化和来源假设列表下运作,你不应该使用那个图书馆。

嗯,首先,我同意 rzwitserloot 的观点,即自由格式的日期解析极其困难且充满歧义。所以你是如履薄冰,如果你只是假设用户输入将按照你认为的方式正确解析,最终会运行陷入麻烦。

尽管如此,如果我假设以下任一情况,我们可以让它工作:

  • 你根本不关心它是否会被错误解析;或者

  • 您这样做是出于娱乐或学习目的;或者

  • 你有一个横幅,写着:

    If the parsing goes wrong, it's your fault. Don't blame us.

无论如何,DateTimeFormatterBuilder 能够构建一个 DateTimeFormatter,它可以解析许多不同的模式。由于格式化程序支持可选解析,可以指示它尝试解析某个值,或者如果找不到有效值则跳过该部分。

例如,此构建器能够解析相当广泛的类 ISO 日期,其中包含许多可选部分:

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder()
    .appendPattern("uuuu-M-d")
    .optionalStart()
        .optionalStart().appendLiteral(' ').optionalEnd()
        .optionalStart().appendLiteral('T').optionalEnd()
        .appendValue(ChronoField.HOUR_OF_DAY)
        .optionalStart()
            .appendLiteral(':')
            .appendValue(ChronoField.MINUTE_OF_HOUR)
            .optionalStart()
                .appendLiteral(':')
                .appendValue(ChronoField.SECOND_OF_MINUTE)
                .optionalStart()
                    .appendFraction(ChronoField.NANO_OF_SECOND, 1, 9, true)
                .optionalEnd()
            .optionalEnd()
        .optionalEnd()
        .appendPattern("[XXXXX][XXXX][XXX][XX][X]")
    .optionalEnd();
DateTimeFormatter formatter = builder.toFormatter(Locale.ROOT);

此格式化程序可以成功解析以下所有字符串。

Stream.of(
    "2021-09-28",
    "2021-07-04T14",
    "2021-07-04T14:06",
    "2001-09-11 00:00:15",
    "1970-01-01T00:00:15.446-08:00",
    "2021-07-04T14:06:15.2017323Z",
    "2021-09-20T17:27:00.000+02:00"
).forEach(testcase -> System.out.println(formatter.parse(testcase)));

您还可以看到,使用 optionalStart()optionalEnd(),您可以定义格式的可选部分。

您可能还想解析更多模式。您可以将这些模式添加到上述构建器中。或者,可以使用 appendOptional​(DateTimeFormatter) 方法来包含多个构建器。