1

I'm using javac 15.0.1, on a Java SE8 project.
I have irregular input for LocalDate:
String fateDate = "1970-02-29";
With parse() of LocalDate I get this Exception (rightly so):
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

But using a DateTimeFormatter does some sort of correction:

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

yields to:

{},ISO resolved to 1970-02-28

This behavior is the same with all months and all values for day<32. So, for April you do not get an exception for 31, but a LocalDate with 30 as value for day. This doesn't look like a perfect incarnation of "fail early"...
Why is there different behavior

  • in LocalDate.parse() vs. DateTimeFormatter.parse()?
  • in DateTimeFormatter for invalid days_of_month >31 vs. !>31?
neverShown
  • 13
  • 3

1 Answers1

2

ResolverStyle

Your own DateTimeFormatter is trying to be smart. It is using a resolver style that is indeed called SMART. It’s the default for formatters from DateTImeFormatter.ofPattern(), but you can of course set it otherwise. Resolver style SMART makes small adjustments like the ones you have observed. On the other hand it does know that a month in the ISO calendar can never have more than 31 days, so still rejects day of month 32 or greater. It’s a design decision, and I don’t know why they designed it exactly this way.

On the other hand, the one-arg LocalDate.parse() uses the built-in DateTimeFormatter.ISO_LOCAL_DATE which has resolver style STRICT. It rejects all dates that are invalid in the proleptic Gregorian calendar.

As I said, you may set the resolver style on your formatter:

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

Edit: I have needed to make one other change. Because with strict resolver style the formatter refuses to resolve year of era, month and day of month into a date at all. The format pattern letter y that you used means year of era, and strictly speaking Java doesn’t know if 1970 is 1970 BCE (BC) or 1970 CE (AD), so cannot resolve. With smart resolver style it would have defaulted to the common era (our era); not with strict. Instead I am using u for a signed year, which makes 1970 unambiguous. With these two changes you now get the exception that you asked for:

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

For the sake of completeness there is also a resolver style LENIENT, but you certainly don’t what that for your situation.

Links

Ole V.V.
  • 65,573
  • 11
  • 96
  • 117