【问题标题】:Java: DateTimeFormatter fail to parse time string when seconds and milliseconds are all 0s?Java:当秒和毫秒都为 0 时,DateTimeFormatter 无法解析时间字符串?
【发布时间】:2018-10-02 16:13:37
【问题描述】:

基本上,我使用以下代码将字符串解析为 LocalDateTime,这在大多数情况下都可以正常工作。

DateTimeFormatter dtformatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");

但是,我遇到秒和毫秒为00000 的情况,这是解析器失败并打印LocalDateTime 2018-03-01T09:16 而不是2018-03-01T09:16:00.000

System.out.println(LocalDateTime.parse("20180301091600000",dtformatter));

(请注意,在我的代码中,我必须将字符串解析为LocalDateTime,进行一些比较,然后最后将LocalDateTime 打印到csv)

如何修复它以使其打印2018-03-01T09:16:00.000 而不是2018-03-01T09:16

仅供参考,我正在使用 jdk10。

【问题讨论】:

  • 这是 Java 8 中的一个错误:bugs.openjdk.java.net/browse/JDK-8031085。但它应该在 Java 9 中得到修复,所以在 Java 10 中看到它是令人惊讶的。
  • 解析器似乎很好,否则它根本不会打印任何东西,除了解析错误。您的问题似乎是打印LocalDateTime 的默认格式。你也应该格式化你的打印。
  • 您的代码在我的 Java 9 上执行得很好。
  • @OleV.V.我正在使用 Java 10 它返回 2018-03-01T09:16
  • @YCF_L 为简洁起见,我想(我没有设计它)。在2018-03-01T00:00 中,可以理解秒和秒的小数部分为 0,因此不需要打印它们。我认为Basil Bourque’s answer 解释了。顺便说一句,为了比较,如果你想打印LocalDateTime 对象的完整精度,你需要2018-03-01T00:00:00.000000000

标签: java date format java-time java-10


【解决方案1】:

tl;博士

秒和毫秒是 00000,这是解析器失败的时候

不,解析器成功。你的问题是生成一个字符串,而不是解析。

默认的DateTimeFormatter 禁止以秒和小数秒为单位的零值as documented

功能,而不是错误

您的问题不在于解析,而在于解析后生成字符串。请记住,日期时间对象的文本表示是不同的并且与对象分开。换句话说,日期时间对象没有“格式”。

[String] --> parse --> [LocalDateTime] --> toString --> [String]

LocalDateTime::toString 的文档清楚地表明,当在最不重要的部分遇到零值时,将使用最短的格式变化。引用:

输出将是以下 ISO-8601 格式之一:

uuuu-MM-dd'T'HH:mm

uuuu-MM-dd'T'HH:mm:ss

uuuu-MM-dd'T'HH:mm:ss.SSS

uuuu-MM-dd'T'HH:mm:ss.SSSSSS

uuuu-MM-dd'T'HH:mm:ss.SSSSSSSSS

所使用的格式将是最短的输出时间的完整值,其中省略的部分被暗示为零。

示例

关于the accepted Answer by YCF_L中显示的两个示例...

20180301091600001 结果是 2018-03-01T09:16:00.001

在该示例中,最低有效部分(毫秒)具有非零值,因此它在结果中表示。

2018030100000000 结果是 2018-03-01T00:00

在该示例中,小时、分钟、秒、毫秒、微秒和纳秒的最低有效部分均为零。因此它们的显示被抑制,除了小时和分钟,因为文档承诺总是显示年分钟。

因此,您的两个示例都按文档说明工作;功能,而不是错误。

解决方案

解决方案是不使用toString 方法中提供的默认格式化程序。相反,请使用其他格式化程序。例如,使用您为解析定义的相同自定义格式化程序。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS" );
LocalDateTime ldt = LocalDateTime.parse( "20180301091600000" , f );

String outputDefault = ldt.toString();
String outputCustom = ldt.format( f );

转储到控制台。

System.out.println( "outputDefault: " + outputDefault );
System.out.println( "outputCustom: " + outputCustom );

输出默认值:2018-03-01T09:16

输出自定义:20180301091600000

问题问:

如何修复它以使其打印 2018-03-01T09:16:00.000 而不是 2018-03-01T09:16 ?

指定自定义格式化程序而不是默认格式化程序。

DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss:SSS") ; 
String output = ldt.format( f ) ;

但请记住,您生成的字符串将禁止在 LocalDateTime 对象中显示任何微秒或纳秒。

【讨论】:

  • 嗨@Basil Bourque,但OP想要产生2018-03-01T09:16:00:000所以我认为你的解决方案不是OP想要的吗?
  • @YCF_L 所需的输出无关紧要 - 问题的重点(见标题)是认为解析器无法解析并省略数据。但是不,问题中的代码隐式调用toString,它使用了一个格式化程序,以秒和小数秒为单位抑制零值。所以问题的前提是错误的。至于所需的格式,这是一个附带问题,并且已经处理了数百个(如果不是数千个)现有的问题和答案。我想我可以展示这样的格式化代码,但这不是重点,你已经做得很好了。
  • 你是英雄@Basil 阅读我刚刚收到的报告bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8202144 这与你在回答中已经提到的一样:)
【解决方案2】:

我不知道为什么它不起作用,这似乎是一个错误,因为当我使用时:

20180301091600001        result is      2018-03-01T09:16:00.001
----------------^                       -----------------^^^^^^

还有另一个测试:

2018030100000000         result is      2018-03-01T00:00
--------^^^-----                        -----------^^^^^^^^^^^

解析器好像忽略了毫秒为零的时候,为什么?

完整的解释为什么?,在Basil Bourque的回答中。


解决方案

这是一个快速修复,您可以使用其他格式化程序,如下所示:

var result = LocalDateTime.parse("20180301091600000", dtformatter)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss:SSS"));

输出

2018-03-01T09:16:00:000

【讨论】:

  • @mynameisJEFF 没问题,我报告它打开 jdk 这样我们就可以等到他们确定问题所在
  • @YCF_L 你能链接到你的错误报告吗?
  • @YCF_L 不,我不认为这是一个错误。在此页面上查看我的答案。
  • 换句话说,@YCF_L 您的“临时解决方案”是正确的,而且不仅仅是临时解决方案,它是 解决方案。
  • 谢谢@OleV.V。然后我将删除那个说它是错误的部分,或者是临时词
猜你喜欢
  • 2012-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-27
  • 2013-02-17
  • 2016-06-05
  • 2017-02-10
相关资源
最近更新 更多