我最近一直在和一个讨厌的海森伯打交道。它使我想起了famous 500-mile email story。我解析了几乎是ISO 8601
中收到的日期,但是解析后的Date
对象有时是将来的几分钟。但是,当我尝试在单元测试中解析相同的数据时,结果日期总是正确的。
这是我收到的格式:
2018-06-25T13:50:53.984771
我使用JSON
从Jackson
解析它:
new StdDateFormat().parse(timestamp);
以下是一些解析日期的示例:
2018-06-25T13:50:59.337828
Mon Jun 25 15:56:36 CEST 2018
2018-06-25T13:50:53.984771
Mon Jun 25 16:07:17 CEST 2018
两个小时的区别很好,那只是时区,但是分钟呢?它们似乎是随机偏移的!
答案 0 :(得分:3)
我在回答自己的问题,希望它能一天挽救别人的皮肤。
该问题未在单元测试中显示,因此我认为服务器可能存在问题。我尝试了所有我认为可能不对的地方,包括研究可能被15分钟抵消的怪异时区,调查Java实际花费时间的地方等等。最后,我别无选择,只能对代码进行一些修改,以将问题二等分。原来是解析器。
以下是导致问题的错误: https://github.com/FasterXML/jackson-databind/issues/1668
我只有在一个朋友注意到时间戳记末尾的毫秒数对应于以分钟为单位的偏移量时才找到它:
Original: 2018-06-25T13:50:53.984771
Milliseconds: 984771 ms = 16 minutes 24.77 seconds
Result: Mon Jun 25 16:07:17 CEST 2018
问题是解析器将String的最后一部分计为毫秒,然后将其静默添加到内部Calendar
对象中,该对象很高兴地接受任意数量的毫秒并将其添加到本身就是设计的。
该问题未在单元测试中显示的原因是我创建的库使用了较新版本的Jackson,其中不再包含该错误。但是,使用该库的应用程序已经包含Jackson,并且由于Maven在解析依赖项时选择了正在构建的库which is closest to the project的定义,因此它用我自己的{ {1}}。因此,最终,这是一个自残的依赖地狱。
答案 1 :(得分:2)
您正在使用Java最早版本中非常麻烦的旧日期时间类(Date
,Calendar
)。这些类现在已成为旧类,几年前被 java.time 类取代。
我不是Jackson用户,所以我不知道Jackson是否添加了对 java.time 的内置支持。但我想这很有可能。
如果没有,您似乎可以为此目的向Jackson添加支持模块。请参见this article,this Question和此GitHub site。
java.time 类使用纳秒分辨率。这意味着最多九(9)个小数秒的十进制数字。对于您输入的6位数字来说绰绰有余。
您还有另一个性质不同的问题。您正在输入缺少任何时区或UTC偏移量概念的日期时间。然后,将其保存为不同类型,该类型具有时区。不好。您在没有应得的地方注入了价值。就像拿一个假定为价格的通用数字,然后任意应用输入中未指定的特定货币一样。
您应该将诸如2018-06-25T13:50:53.984771
之类的输入解析为LocalDateTime
。此类型有目的地缺少任何时区或从UTC偏移,例如您的输入。这种类型的 not 并不代表特定时刻,不是时间线上的一点,因为在应用区域/偏移量的上下文之前,它没有真正的意义。
有关更多讨论,请在堆栈溢出中搜索LocalDateTime
,ZonedDateTime
,OffsetDateTime
和Instant
。尤其是对于我的一些答案,请搜索“圣诞老人”。
java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
目前位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
在哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如Interval
,YearWeek
,YearQuarter
和more。
答案 2 :(得分:1)
使用jackson-modules-java8和Basil Bourque在his answer中推荐的java.time
类型。我还没有使用jackson-modules-java8的经验,但是它绝对应该正确地解析秒的小数部分,最多可以正确地包含至少9个小数,并且它确实支持现代的日期时间类型。