am/pm 的 DateTimeFormatterBuilder

DateTimeFormatterBuilder with am/pm

我正在尝试构建一种接受 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);


怎么好像不行。我得到:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2:00 am' could not be parsed at index 0

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

我认为您需要将 HOUR_OF_DAY 从 (0-23) 更改为 CLOCK_HOUR_OF_AMPM 从 (1-12)。我还在 AM/PM 之前添加了一个 space 文字,并在 AM/PM 字段中使用 appendText 而不是 appendValue

以下工作正常。

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
  1. 由于AM或PM中的小时可以是1位,所以指定。使用接受最小和最大字段宽度的重载 appendValue 方法。
  2. 正如 Edwin Daloezo 所说,您可能需要 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);