【问题标题】:Parsing a String to LocalDateTime with optional trailing truncated zeros使用可选的尾随截断零将字符串解析为 LocalDateTime
【发布时间】:2020-07-27 08:56:16
【问题描述】:

从 API 传递的字符串通常遵循 yyyy-MM-dd HH:mm:ss.SSS 格式,但当时间戳中有尾随 0 时,它们会被截断,例如 2019-07-16 13:29:15.100 转换为 2019-07-16 13:29:15.12019-07-16 13:29:15.110 转换到2019-07-16 13:29:15.11。我有一个可行的解决方案,它只是用零填充结尾,但这感觉就像可以通过 DateTimeFormatter 字符串中的可选部分解决的问题。我最接近的工作解决方案如下:

String toParse = "2019-07-16 13:29:15.111";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.[S[S[S]]]]");
LocalDateTime timestamp = LocalDateTime.parse(toParse, formatter);

这适用于所有尾随零被截断的情况,但不适用于毫秒中所有数字均非零的情况。错误信息是

java.time.format.DateTimeParseException: Text '2019-07-16 13:29:15.111' could not be parsed, unparsed text found at index 21

这只是支架放置的问题吗?我使用的是 Java 8,我们无法更改 API 传递的内容。

【问题讨论】:

  • 我无法复制。我可以成功运行你的代码...
  • @Sweeper 代码没有给出完全正确的结果。预期:2019-07-16T13:29:15.111。在我的 Java 11 上观察到:2019-07-16T13:29:15.100。有关解释,请参阅我的答案。
  • @OleV.V.啊...我实际上并没有检查解析结果。我只是指出没有得到提到的例外。哇,我不知道方括号会破坏重复模式!学到了一些新东西。

标签: java string parsing datetime-format


【解决方案1】:

使用内置部件

编写自己的格式模式字符串不仅有时很棘手,而且总是容易出错。我建议您改为使用内置部件组装格式化程序:

    String toParse = "2019-07-16 13:29:15.111";
    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE)
            .appendLiteral(' ')
            .append(DateTimeFormatter.ISO_LOCAL_TIME)
            .toFormatter();
    LocalDateTime timestamp = LocalDateTime.parse(toParse, formatter);

    System.out.println(timestamp);

输出:

2019-07-16T13:29:15.111

这适用于完全没有小数 (2019-07-16 13:29:15) 以及从一位小数 (2019-07-16 13:29:15.1) 到九位小数 (2019-07-16 13:29:15.123456789) 的所有内容。它甚至可以在没有秒数的情况下工作 (2019-07-16 13:29)。

如果您想要抛出 4 位或更多小数的异常,您确实需要使用 appendFraction(),如 Deadpool 的回答。

DateTimeFormatter.ISO_LOCAL_TIME的文档中:

格式包括:

  • 两位数表示一天中的小时。这预先用零填充以确保两位数。
  • 冒号
  • 两位数表示小时。这预先用零填充以确保两位数。
  • 如果秒数不可用,则格式完整。
  • 冒号
  • 两位数表示秒。这预先用零填充以确保两位数。
  • 如果纳秒为零或不可用,则格式完整。
  • 小数点
  • 1 到 9 位表示纳秒。将根据需要输出尽可能多的数字。

为什么方括号在这里不起作用

您不能将方括号放在相同格式模式字母的重复字符串中间。 [.[S[S[S]]]] 被理解为可选地一个点,可选地后跟一位秒的小数,可选地后跟一位秒的小数,可选地后跟一位秒的小数。正如 Sweeper 在评论中指出的那样,当分数为 .111 时,它可能会毫无例外地运行,但它可能会被理解为 .1,而 1 被指定了 3 次。如果三个数字不相等,则会中断。相反,您可能已经摆脱了[.[SSS][SS][S]] 的可选小数点,后跟 3、2 或 1 个小数。您需要先输入 3 位小数。

链接

【讨论】:

    【解决方案2】:

    我对毫秒的这些可选括号并不是很完美,但您始终可以使用DateTimeFormatterBuilder 创建带有可选毫秒的DateTimeFormatter

    DateTimeFormatter formatter1 = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss")
                                        .optionalStart()
                                        .appendFraction(ChronoField.MILLI_OF_SECOND, 0, 3, true)
                                        .optionalEnd()
                                        .toFormatter();
    

    【讨论】:

    • 这成功了!我试图对括号感到满意,但这似乎是一个更强大的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多