无法将单个数字小时和小写的am-pm解析为Java 8 LocalTime

ben*_*rre 5 datetime localtime datetime-parsing java-8 java-time

当我尝试运行以下代码时:

LocalTime test = LocalTime.parse("8:00am", DateTimeFormatter.ofPattern("hh:mma"));
Run Code Online (Sandbox Code Playgroud)

我明白了:

Exception in thread "main" java.time.format.DateTimeParseException: Text '8:00am' could not be parsed at index 0
Run Code Online (Sandbox Code Playgroud)

知道为什么会这样吗?

小智 6

AM/PM模式是区域敏感的。如果您创建格式化程序但未设置java.util.Locale,它将使用 JVM 的默认值。无论如何,我已经检查了 JDK 1.8.0_144 并且没有使用小写am作为AM/PM字段文本的语言环境(我发现使用a.m.and 的语言环境AM,但没有使用am)。

因此,一种替代方法是设置使用AM(example: Locale.ENGLISH)的语言环境并使用 ajava.time.format.DateTimeFormatterBuilder构建不区分大小写的格式化程序。另一个细节是输入中的小时只有 1 位数字,因此您必须将模式更改为h(接受 1 位或 2 位数字,而hh仅接受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);
Run Code Online (Sandbox Code Playgroud)

问题是语言环境也会影响其他字段(例如,如果您使用月份或星期几名称,或基于周的字段)。

另一个细节是格式化程序仅在解析时不区分大小写。格式化时,它将使用特定于语言环境的符号,在本例中为大写。所以这:

System.out.println(fmt.format(test)); // 8:00AM
Run Code Online (Sandbox Code Playgroud)

印刷:

8:00 AM


为了不依赖于语言环境,您可以使用此字段的自定义文本映射,使用java.time.temporal.ChronoField

// map of custom text for AM/PM field
Map<Long, String> 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);
Run Code Online (Sandbox Code Playgroud)

与之前的格式化程序的不同之处在于它使用自定义文本(小写ampm)进行解析和格式化。所以这段代码:

System.out.println(fmt.format(test)); // 8:00am
Run Code Online (Sandbox Code Playgroud)

将打印:

8:00 AM


eve*_*ton 5

AM/PM令牌*为大写:

LocalTime test = LocalTime.parse("8:00AM", DateTimeFormatter.ofPattern("hh:mma"));
Run Code Online (Sandbox Code Playgroud)

小时的模式定义:

Symbol  Meaning                     Presentation      Examples
------  ------------                ------------      --------
a       am-pm-of-day                text              PM
h       clock-hour-of-am-pm (1-12)  number            12
K       hour-of-am-pm (0-11)        number            0
k       clock-hour-of-am-pm (1-24)  number            0
H       hour-of-day (0-23)          number            0
Run Code Online (Sandbox Code Playgroud)

DateTimeFormatter有关所有可用模式,请参阅javadoc:https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

- 编辑

*根据@ Hugo的评论,这与您设置格式化程序的语言环境有关.如果未指定任何内容,则使用JVM默认值.只是发生了绝大多数Locales强制AM/PM令牌为大写:

碰巧的是,对于大多数语言环境,使用大写的AM,因此它适用于大多数人.但它也可以是ga_IE和es_US语言环境,日语中的午前(ja_JP),瑞典语中的fm(sv_SE)以及许多其他语言.我已经在JDK 1.8.0_144中进行了测试,它有160个语言环境 - 其中108个使用AM - 无论如何,如果您的JVM使用其中一个108个语言环境(并且没有人更改此配置),这不是问题

  • 实际上,它不需要*必须*是大写的(javadoc 仅显示了一个示例),这完全取决于语言环境(当您创建格式化程序而不指定语言环境时,它使用 JVM 的默认值)。碰巧的是,对于大多数语言环境,使用大写的“AM”,因此它适用于大多数人。但它也可以是 ga_IE 和 es_US 语言环境中的“am”、日语 (ja_JP) 中的“午前”、瑞典语 (sv_SE) 中的“fm”等等。我已经在 J​​DK 1.8.0_144 中进行了测试,它有 160 个语言环境 - 其中 108 个使用`AM` - 无论如何,如果您的 JVM 使用这 108 个语言环境之一(并且没有人更改此配置),这不是问题 (2认同)

Ell*_*sch 2

AM/PM 为大写,需要用h一位数来表示小时(需要hh两位数)。喜欢,

LocalTime test = LocalTime.parse("8:00am".toUpperCase(), 
        DateTimeFormatter.ofPattern("h:mma"));
Run Code Online (Sandbox Code Playgroud)