javatimejava-timedatetime-parsinglocaldatetime

Cannot parse '12/16/2022, 1:33 pm' using 'M/d/yyyy, h:mm a' format pattern into a LocalDateTime object


The value 12/16/2022, 1:33 pm looks to match to expected format pattern M/d/yyyy, h:mm a, what is incorrect at index 17?

Java unit test:

@Test
public void testLocalDateTime_shortDateTimePattern() {
    String pattern = "M/d/yyyy, h:mm a";
    System.out.println(pattern);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);

    String dateString = "12/16/2022, 1:33 pm";
    System.out.println(dateString);

    LocalDateTime result = LocalDateTime.parse(dateString, formatter);
    System.out.println(result);
}

Console output:

M/d/yyyy, h:mm a
12/16/2022, 1:33 pm

Text '12/16/2022, 1:33 pm' could not be parsed at index 17
java.time.format.DateTimeParseException: Text '12/16/2022, 1:33 pm' could not be parsed at index 17
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:492)

Solution

  • tl;dr
    Index 17 is AM/PM of day
    Problems:

    If you can change the input String, you should make the pm (or am) upper case, otherwise use a DateTimeFormatter that parses case insensitive(ly). You can use a DateTimeFormatterBuilder for that.

    In any case add a Locale since AM/PM is not everywhere exactly one of those two abbreviations (on my German JVM it would be nachm., abbreviating nachmittags, which means something like at noon).

    Here's an example:

    public static void main(String[] args) {
        // define the pattern (your original one should do)
        String pattern = "M/d/yyyy, h:mm a";
        // build a formatter that handles the lower-case AM/PM of day
        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                                                .parseCaseInsensitive()
                                                .appendPattern(pattern)
                                                .toFormatter(Locale.ENGLISH);
        // example input / datetime
        String dateString = "12/16/2022, 1:33 pm";
        // parse with the formatter
        LocalDateTime resultB = LocalDateTime.parse(dateString, formatter);
        // print the result
        System.out.println(resultB);
    }
    

    Output:

    2022-12-16T13:33
    

    Small hint (because I just made that mistake myself):
    the call to .parseCaseInsensitive() must come before appending the (relevant part of) the pattern. Otherwise the formatter would only know to parse case insensitively afterwards, which then fails with the same DateTimeParseException.


    Update:
    Obviously, UK's standard is lower case, so you can use DateTimeFormatter.ofPattern(pattern, Locale.UK), too. There may be more such Locales, but a fix one is less flexible than a formatter that parses case insensitively (IMHO).