1
String s = "2020 Jun 31";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MMM dd");
LocalDate date = LocalDate.parse(s, formatter);
System.out.println(date);

Output:

2020-06-30

Why does 31 turn into 30 without any warnings or exceptions?

Arvind Kumar Avinash
  • 50,121
  • 5
  • 26
  • 72
Daniel
  • 219
  • 1
  • 7

2 Answers2

3

DateTimeFormatter has a ResolverStyle that affects how strict or lenient the parser should be with invalid date and time values. To get an exception in this case you need to set the resolver style to STRICT.

You also need to use u (year) instead of y (year-of-era) in the format string.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu MMM dd")
                                  .withResolverStyle(ResolverStyle.STRICT);

The default resolver type is SMART:

Using smart resolution will perform the sensible default for each field, which may be the same as strict, the same as lenient, or a third behavior. Individual fields will interpret this differently.

For example, resolving year-month and day-of-month in the ISO calendar system using smart mode will ensure that the day-of-month is from 1 to 31, converting any value beyond the last valid day-of-month to be the last valid day-of-month.

Joni
  • 101,441
  • 12
  • 123
  • 178
  • 1
    Now it refuses to parse the string "2020 Jun 30": Text '2020 Jun 30' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {MonthOfYear=6, YearOfEra=2020, DayOfMonth=30},ISO of type java.time.format.Parsed at java.time.format.DateTimeFormatter.createError(Unknown Source). What causes this? – Daniel Aug 08 '20 at 14:31
  • 1
    @Daniel apparently you have to change the pattern to use `u` ("year") instead of `y` ("year of era") - I've updated the answer to reflect that. It's not clear to me what the difference is but I see there are more questions about this https://stackoverflow.com/questions/29014225/what-is-the-difference-between-year-and-year-of-era and https://stackoverflow.com/questions/41177442/uuuu-versus-yyyy-in-datetimeformatter-formatting-pattern-codes-in-java?rq=1 – Joni Aug 08 '20 at 14:53
  • @Joni That’s only logical when you think about it. Strictly speaking with `yyyy MMM dd` the date 2020 Jun 31 could have been in year 2020 before Christ/before the common era or in 2020 anno Domini/current era (this year). So you can’t parse into an unambiguous date, so the strict resolver style causes the formatter to refuse to parse at all. See for example [this answer by Meno Hochschild](https://stackoverflow.com/a/41182911/5772882). – Ole V.V. Aug 08 '20 at 18:39
1

The ResolverStyle is an enum that offers three different approaches: strict, smart and lenient. The smart option is the default. It can be set using withResolverStyle(ResolverStyle). The following code will throw an exception:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String s = "2020 Jun 31";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("u MMM d")
                                        .withResolverStyle(ResolverStyle.STRICT)
                                        .localizedBy(Locale.ENGLISH);
        LocalDate date = LocalDate.parse(s, formatter);
        System.out.println(date);
    }
}

However, it will work without any exception for the day as 30 i.e. the output will be 2020-06-30 for String s = "2020 Jun 30".

Check Resolving section at https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html for more details.

Arvind Kumar Avinash
  • 50,121
  • 5
  • 26
  • 72