DateTimeFormatter.parse() 更正日期信息而不是抛出异常

DateTimeFormatter.parse() corrects date information instead of throwing Exception

我在 Java SE8 项目上使用 javac 15.0.1。
我有 LocalDate 的不规则输入:
String fateDate = "1970-02-29";
使用 LocalDate 的 parse() 我得到这个异常(正确地如此):
LocalDate.parse(fateDate);

Exception in thread "main" java.time.format.DateTimeParseException: Text '1970-02-29' could not be parsed: Invalid date 'February 29' as '1970' is not a leap year

但是使用 DateTimeFormatter 会进行某种更正:

final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("y-M-d");
TemporalAccessor ta = dtf.parse(fateDate);
System.out.println(ta);

产生:

{},ISO resolved to 1970-02-28

此行为对于所有月份和天<32 的所有值都是相同的。因此,对于 4 月,您不会得到 31 的异常,而是会得到一个 LocalDate,其中 30 作为一天的值。这看起来不像是“早期失败”的完美化身...
为什么会有不同的行为

解析器样式

你自己的 DateTimeFormatter 正在努力变得聪明。它使用的是确实称为 SMART 的解析器样式。这是来自 DateTImeFormatter.ofPattern() 的格式化程序的默认值,但您当然可以设置为其他方式。解析器样式 SMART 会像您观察到的那样进行小的调整。另一方面,它确实知道 ISO 日历中的一个月永远不会超过 31 天,因此仍然拒绝第 32 个月或更大的日期。这是一个设计决定,我不知道他们为什么要这样设计。

另一方面,单参数 LocalDate.parse() 使用具有解析器样式 STRICT 的内置 DateTimeFormatter.ISO_LOCAL_DATE。它拒绝所有在 proleptic 公历中无效的日期。

正如我所说,您可以在格式化程序上设置解析器样式:

    String fateDate = "1970-02-29";
    final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d")
            .withResolverStyle(ResolverStyle.STRICT);
    TemporalAccessor ta = dtf.parse(fateDate );
    System.out.println(ta);

编辑:我需要进行另一项更改。因为使用严格的解析器样式,格式化程序根本拒绝将年代、月份和日期解析为日期。您使用的格式模式字母y表示纪元,严格来说Java不知道1970是1970 BCE(BC)还是1970 CE(AD),所以无法解析。使用智能解析器样式,它会默认为共同时代(我们的时代);不严格。相反,我使用 u 作为签名年份,这使得 1970 变得明确。通过这两项更改,您现在可以获得您要求的例外情况:

Exception in thread "main" java.time.format.DateTimeParseException: Text '1970-02-29' could not be parsed: Invalid date 'February 29' as '1970' is not a leap year

为了完整起见,还有一种解析器样式 LENIENT,但您肯定不会根据自己的情况选择它。

链接

  • 问题
    • 解释(除其他外)为什么我们需要具有严格解析器样式的格式模式 u