2

我正在尝试构建一个接受 AM 和 PM 标记的日期时间格式。例如,每当使用默认格式调用 java 中的 LocalTime.parse("12:00 am") 时,就会引发异常。所以我像这样添加自己的格式

String time = "2:00 am"

DateTimeFormatter format = new DateTimeFormatterBuilder()
                        .appendValue(HOUR_OF_DAY, 2)
                        .appendLiteral(':')
                        .appendValue(MINUTE_OF_HOUR, 2)
                        .optionalStart()
                        .appendLiteral(':')
                        .appendValue(SECOND_OF_MINUTE, 2)
                        .optionalStart()
                        .appendValue(AMPM_OF_DAY)
                        .toFormatter();

LocalTime.parse(time, format);


它似乎不起作用。我得到:

线程“主”java.time.format.DateTimeParseException 中的异常:无法在索引 0 处解析文本“凌晨 2:00”

如何构建一个正确的 DateTimeFormatterBuilder 来解析以下字符串:“2:00 am”、“12:15 pm”?是的,我知道您可以附加格式,但在这种情况下,需要使用附加值。

4

2 回答 2

1
  1. 由于 AM 或 PM 中的小时可以是 1 位数字,因此请指定。appendValue使用接受最小和最大字段宽度的重载方法。
  2. 正如 Edwin Dalezo 已经说过的,你可能想要ChronoField.CLOCK_HOUR_OF_AMPM.
  3. 您无条件地需要 am 或 pm,所以它应该在……之外optionalStart()……optionalEnd()正如 Turing85 在评论中所说。
  4. 考虑您是否需要接受amAM两者兼而有之。在最后一种情况下,您需要指定不区分大小写的解析
  5. 始终为格式化程序指定区域设置(一种语言)。虽然 AM 和 PM 在英语以外的其他语言中几乎不使用,但它们在其他语言中确实具有不同的值(文本)。

所以我的做法是:

    String time = "2:00 am";

    DateTimeFormatter format = new DateTimeFormatterBuilder()
            .appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 1, 2, SignStyle.NEVER)
            .appendLiteral(':')
            .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
            .optionalStart()
            .appendLiteral(':')
            .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
            .optionalEnd()
            .appendLiteral(' ')
            .appendText(ChronoField.AMPM_OF_DAY)
            .toFormatter(Locale.forLanguageTag("en-AU"));

    System.out.println(LocalTime.parse(time, format));

输出是:

02:00

在澳大利亚英语中,am 和 pm 是小写的(根据我的 Java 11),所以我指定了这个语言环境。只有当您的输入来自澳大利亚时,您才应该这样做,否则只会混淆(还有一些地方 am 和 pm 是小写的)。要接受来自另一个英语区域设置的小写 am 和 pm,请使用parseCaseInsensitive(). 例如:

    DateTimeFormatter format = new DateTimeFormatterBuilder()
            .appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 1, 2, SignStyle.NEVER)
            .appendLiteral(':')
            .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
            .optionalStart()
            .appendLiteral(':')
            .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
            .optionalEnd()
            .appendLiteral(' ')
            .parseCaseInsensitive() // Accept AM or am
            .appendText(ChronoField.AMPM_OF_DAY)
            .toFormatter(Locale.ENGLISH);

如果你想要一个格式模式字符串

这本身不是建议。如果您不需要不区分大小写的解析,则可以从格式模式字符串而不是构建器构建格式化程序。一方面它可能更容易出错,另一方面它更短。

    DateTimeFormatter format = DateTimeFormatter
            .ofPattern("h:mm[.ss] a", Locale.forLanguageTag("en-AU"));

输出和以前一样。格式模式字符串中的方括号指定秒是可选的。

如果您确实需要不区分大小写的解析,则需要构建器来指定它。

您也可以混合使用这些方法,因为构建器也接受格式模式字符串:

    DateTimeFormatter format = new DateTimeFormatterBuilder()
            .parseCaseInsensitive() // Accept AM or am
            .appendPattern("h:mm[.ss] a")
            .toFormatter(Locale.ENGLISH);
于 2021-08-17T03:24:59.930 回答
0

我相信您需要将HOUR_OF_DAY哪个从(0-23)更改为CLOCK_HOUR_OF_AMPM从(1-12)。我还在 AM/PM 之前添加了一个空格文字,appendText而不是appendValue用于 AM/PM 字段。

以下工作正常。

var formatter = new DateTimeFormatterBuilder()
                .appendValue(CLOCK_HOUR_OF_AMPM, 2)
                .appendLiteral(':')
                .appendValue(MINUTE_OF_HOUR, 2)
                .optionalStart()
                .appendLiteral(":")
                .appendValue(SECOND_OF_MINUTE, 2)
                .optionalStart()
                .appendLiteral(" ")
                .appendText(AMPM_OF_DAY)
                .toFormatter();

        //formatter = new DateTimeFormatterBuilder().appendPattern("hh:mm:ss a").toFormatter();
var time = LocalTime.parse("07:20:54 AM", formatted);
System.out.println(time);  // 19:20:54
于 2021-08-17T00:38:01.350 回答