(感谢评论中的更正,现在答案进行了广泛修订)
在Java 7中,您可以使用X
模式来匹配ISO8601时区,其中包括特殊的Z
(UTC)值。
X
模式还支持显式时区,例如+01:00
这种方法正确地处理时区指示器,并避免了将其仅视为字符串并因此在本地时区而不是UTC或其他时区错误解析时间戳的问题。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
Date date = sdf.parse("2007-09-25T15:40:51Z");
Date date2 = sdf.parse("2007-09-25T15:40:51+01:00");
这也可以与毫秒一起使用:
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
Date date3 = sdf2.parse("2007-09-25T15:40:51.500Z");
然而,正如其他人指出的那样,您的格式有7位小数秒,这些小数秒可能是十分之一微秒。如果是这样,SimpleDateFormat无法处理它,您将获得不正确的结果,因为每个0.1微秒将被解释为一毫秒,从而可能导致总体误差高达10,000秒(几个小时)。在极端情况下,如果小数秒值为0.9999999秒,则会错误地解释为9999999毫秒,约为167分钟或2.8小时。
// Right answer, error masked for zero fractional seconds
Date date6 = sdf2.parse("2007-09-25T15:40:51.0000000Z")
// Tue Sep 25 15:40:51 GMT 2007
// Error - wrong hour
// Should just half a second different to the previous example
Date date5 = sdf2.parse("2007-09-25T15:40:51.5000000Z")
// Tue Sep 25 17:04:11 GMT 2007
这个错误在小数秒为零时会被隐藏,就像你的例子一样,但是当它们不为零时,它就会表现出来。
通过关闭“宽松”解析(默认情况下接受超过一秒的小数部分并将其传递到秒/分/小时部分),可以在许多情况下检测到此错误并减少其影响:
sdf2.setLenient(false)
sdf2.parse("2007-09-25T15:40:51.5000000Z")
// java.text.ParseException: Unparseable date: "2007-09-25T15:40:51.5000000Z"
这将捕获毫秒值大于999的情况,但不检查数字的数量,因此仅是对毫秒/微秒不匹配的部分和间接保护措施。然而,在许多实际数据集中,这将捕获大量错误,并因此指示根本问题,即使一些值滑过去了。
我建议除非您有特定的需求,否则始终禁用宽松解析,因为它会捕获许多错误,否则这些错误将被悄悄隐藏并传播到下游数据中。
如果您的小数秒始终为零,则可以在此处使用其中一种解决方案,但有风险,即如果稍后在非零小数秒上使用该代码,则它们将无法工作。您可能希望记录此事并/或断言该值为零,以避免以后出现的错误。
否则,您可能需要将小数秒转换为毫秒,以便
SimpleDateFormat
可以正确解释它们。或使用较新的日期时间API之一。
.SSSZ
?如果你只需要日期或时间,那么去掉.SSSZ
即可。 - IgorGanapolskySimpleDateFormat
和Date
。这些类设计得很差,而且已经过时很久了,前者尤其令人头疼。相反,只需使用来自java.time,现代Java日期和时间API的Instant
即可。 - Ole V.V.