Failed to parse single digit hour and lowercase am-pm of day into Java 8 LocalTime

前端 未结 6 2007
借酒劲吻你
借酒劲吻你 2021-01-05 10:50

When I try to run the following code:

LocalTime test = LocalTime.parse(\"8:00am\", DateTimeFormatter.ofPattern(\"hh:mma\"));

I get this:

6条回答
  •  夕颜
    夕颜 (楼主)
    2021-01-05 11:14

    AM/PM pattern is locale sensitive. If you create a formatter and don't set a java.util.Locale, it'll use the JVM's default. Anyway, I've checked in JDK 1.8.0_144 and there's no locale that uses lowercase am as the text for AM/PM field (I've found locales that use a.m. and AM, but no am).

    So, one alternative is to set a locale that uses AM (example: Locale.ENGLISH) and use a java.time.format.DateTimeFormatterBuilder to build a case insensitive formatter. Another detail is that the hour in the input has only 1 digit, so you must change the pattern to h (which accepts 1 or 2 digits, while hh accepts only 2):

    DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        // case insensitive
        .parseCaseInsensitive()
        // pattern
        .appendPattern("h:mma")
        // set Locale that uses "AM" and "PM"
        .toFormatter(Locale.ENGLISH);
    // now it works
    LocalTime test = LocalTime.parse("8:00am", fmt);
    

    The problem is that the locale can also affect other fields (if you use month or day of week names, or week based fields, for example).

    Another detail is that the formatter is case insensitive only for parsing. When formatting, it'll use the locale specific symbols, which in this case is uppercase. So this:

    System.out.println(fmt.format(test)); // 8:00AM
    

    Prints:

    8:00AM


    To not depend on the locale, you can use a map of custom texts for this field, using a java.time.temporal.ChronoField:

    // map of custom text for AM/PM field
    Map map = new HashMap<>();
    // AM's value is 0
    map.put(0L, "am");
    // PM's value is 1
    map.put(1L, "pm");
    DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        // pattern (hour:minute)
        .appendPattern("h:mm")
        // use custom text for AM/PM
        .appendText(ChronoField.AMPM_OF_DAY, map)
        // create formatter, no need to set locale
        .toFormatter();
    // it also works
    LocalTime test = LocalTime.parse("8:00am", fmt);
    

    The difference from the previous formatter is that it uses the custom text (lowercase am and pm) for both parsing and formatting. So this code:

    System.out.println(fmt.format(test)); // 8:00am
    

    Will print:

    8:00am

提交回复
热议问题