【问题标题】:Java 8 optional time part not workingJava 8 可选时间部分不起作用
【发布时间】:2017-09-20 12:01:31
【问题描述】:

我正在尝试为当前我实现的可选时间部分创建日期时间格式

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.text.ParseException;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        System.out.println(Ideone.getDate("2017-07-01T00:00:00.0Z"));
        System.out.println(Ideone.getDate("2017-07-01T00:00:00.00Z"));
        System.out.println(Ideone.getDate("2017-07-01T00:00:00.000Z"));
    }
    
    public static LocalDateTime getDate(String date) {
        try {
           
            DateTimeFormatter formatter2 =
                DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'");
            LocalDateTime ldt = LocalDateTime.parse(date, formatter2);
            
            
            return ldt;
           
        } catch (DateTimeParseException ex) {
            return null;
        }
    }
}

还有输出

空

2017-07-01T00:00

2017-07-01T00:00

现在我的问题是,为什么带有 1 个时间分数的日期不起作用,而它正在使用 2 个和 3 个分数?它应该接受 1,2 和 3 分数吗?还是只有 3 个分数?

提前致谢

【问题讨论】:

  • 我认为它在documentation 中指定得不好。这就是为什么我评论了一些可能对你有用的东西,但我无法真正回答你的问题。
  • 好的。不直接相关,但最好使用 yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S]X - X 将解析偏移量(Z 是 UTC designator 并且虽然 LocalDateTime 丢弃它,但最好不要将其视为文字)
  • 我担心你发现了一个微妙的错误。为了比较:使用我的 lib Time4J 按您的模式工作:LocalDateTime ldt = ChronoFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'", PatternType.CLDR, Locale.ROOT, PlainTimestamp.axis(TemporalType.LOCAL_DATE_TIME)).parse(input);
  • 根据DateTimeFormatterBuilder javadoc,SSS 等价于appendFraction(ChronoField.NANO_OF_SECOND, 3, 3, false),所以它应该正好接受 3 位数字。这看起来真的像一个错误。
  • @Hugo 我也尝试过调试。 3 的最小宽度仅在严格解析期间很重要。但是解析器并不严格(经过测试)。无论如何,即使在严格模式下,解析器也会在可选部分内失败,然后按照规范不会中止而是保持旧位置(在点处)并尝试跳转到下一个模式指令(这里的“Z”为文字,然后会失败,因为输入有点而不是“Z在那个位置=>不同的原因)。没有那样的东西,所以它显然是一个错误。

标签: java java-8 datetime-format java-time


【解决方案1】:

这似乎是一个错误。在 Java 8 中,日期时间解析的毫秒部分似乎通常有问题,请参阅 this issue 及其重复项。

相关引述:

然而,更糟糕的是,SSS 模式因此最终使用 适合宽松模式时的严格模式。正如目前 代表,DateTimeFormatter.ofPattern("hhmmss.SSS") 需要三个 毫秒的数字,当它最初打算要求 0 时 到 9(宽松的行为)。

鉴于当前的实现需要三位数的 SSS,因此不适用相邻值解析是非常令人惊讶的。

但是您似乎发现了上述要求也不适用的情况。此外,即使上述问题处于“已修复”状态,您的示例似乎在 java 8 和 java 9 中都有问题:

>java -version
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)

>javac Ideone.java

>java Ideone
null
2017-07-01T00:00
2017-07-01T00:00

>"c:\Program Files\Java\jdk1.8.0\bin"\javac Ideone.java

>"c:\Program Files\Java\jdk1.8.0\bin"\java Ideone
null
2017-07-01T00:00
2017-07-01T00:00

它应该接受 1,2 和 3 分数吗?还是只有 3 个分数?

根据引用,它应该只有 3,虽然最初打算是 0-9。

【讨论】:

  • 这确实是一个错误,但它与您链接的问题无关。这与字符串末尾的文字有关(请参阅我对问题的评论)。最后我检查了一下,这根本没有报告给 JDK 人员。
  • @M.Prokhorov 我没有声称这是我链接的问题,是吗?我指出了它的cmets关于文字应该如何工作。
【解决方案2】:

我想你想要的是DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS][.S]'Z'")

基本上它是说首先检查 2+ 小数位数字的长格式,否则如果该可选字段不存在,则检查由 @ 指定的单个小数位数字的可选字段987654322@

【讨论】:

  • “问题是,为什么日期为 1 时间分数不起作用,而它正在使用 2 和 3 分数?它应该接受 1,2 和 3 分数吗?还是只有 3 分数?” - 这是怎么回答的?
【解决方案3】:

当你通过 this 时得到 null 的原因是

“2017-07-01T00:00:00.0Z”

您已经告诉解析器需要 3 个字符格式(见下文 .SSS)

DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'");

如果您将其更改为 .SS,它应该可以工作,但是您的最后一个输入,即 2017-07-01T00:00:00.000Z 将会中断。您可能需要包含一些逻辑来处理不同的格式

【讨论】:

  • 但问题是,为什么它适用于.SS 而不适用于.S。我的模式是.SSS,所以它应该只适用于.SSS
猜你喜欢
  • 2018-12-06
  • 2018-12-13
  • 1970-01-01
  • 1970-01-01
  • 2016-09-24
  • 1970-01-01
  • 2015-03-11
  • 2022-01-21
  • 2015-04-25
相关资源
最近更新 更多