以下是抛出异常的代码段:
SimpleDateformat dateFormatter = new SimpleDateFormat("yyyyMMddHHmm");
Date date = dateFormatter.parse("201710010200");
上面的代码在凌晨2点之后的所有日期都抛出了异常。它运行良好,直到凌晨01:30。
配置了DayLight节省时间(我正在使用Australia/Sydney
时区)。
之后,我可以看到凌晨3点的日志。 凌晨2点之间的时间和凌晨3点也没有记录。
日志:
01 Oct 03:02:01错误:无法解析的日期:“201710010200”
引起:java.text.ParseException:无法解析的日期:“201710010200” 在java.text.DateFormat.parse(DateFormat.java:357)
如果指定了正确的日期格式,日期字符串"201710010200"
无法解析的问题是什么?
答案 0 :(得分:6)
您正在尝试解析未发生的日期/时间。
我们现在知道这是在悉尼时区。 2017年10月1日凌晨2点在悉尼,时钟一直持续到凌晨3点。如果你每分钟都在看钟表,你会看到:
所以在凌晨2点(含)和凌晨3点(独家)之间的任何日期/时间都不会在该时区内发生。我们不知道是什么产生了您要解析的值,但是:
java.time
包并将其解析为LocalDateTime
值答案 1 :(得分:1)
LocalDateTime.parse( // Parse a string lacking any indicator of time zone or offset-from-UTC into a `LocalDateTime` object.
"201710010200" , // Parse string in “basic” ISO 8601 format.
DateTimeFormatter.ofPattern( "uuuuMMddHHmm" ) // Define a formatting pattern to match the input. If you used expanded ISO 8601 format instead of “basic” you would not need to bother with this step.
).atZone( // Place the inexact unzoned value (`LocalDateTime` object) into the context of a time zone. Produces a `ZonedDateTime` object.
ZoneId.of( "Australia/Sydney" )
).toString() // Generate a string in standard expanded ISO 8601 format. This class extends the standard to wisely append the name of the zone in square brackets.
2017-10-01T03:00 + 11:00 [澳大利亚/悉尼]
如果该区域中该日期的某个时间段无效,则ZonedDateTime
课程会进行调整。
如果您担心错误的输入,请陷阱DateTimeParseException
。
Answer by Jon Skeet是正确的。根据他的评论: 2017年10月1日当地时间凌晨2点在悉尼没有及时。
现代解决方案是 java.time 类,它取代了麻烦的旧遗留日期时间类。
定义格式模式以匹配您的输入。
String input = "201710010200" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuuMMddHHmm" ) ;
LocalDateTime
您的输入缺少与UTC或时区偏移的指示。因此解析为LocalDateTime
。
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
ldt.toString():2017-10-01T02:00
如果没有时区的上下文或从UTC偏移,则此值没有实际意义。它不代表时刻,时间轴上的一个点。关于在大约26-27小时范围内可能发生的瞬间,这只是一个模糊的想法。
要确定时刻,我们需要将此值放在时区或偏移的上下文中。
您声称知道该值旨在表示Australia/Sydney
时区中的时刻。
ZoneId z = ZoneId.of( "Australia/Sydney" ) ;
2017-10-01T03:00 + 11:00 [澳大利亚/悉尼]
结果是凌晨3点。那个地区的人们使用的offset-from-UTC在那个时候改变了,Daylight Saving Time (DST)愚蠢的切断。当澳大利亚那个地区的时钟即将凌晨2点开始时,时钟跳到凌晨3点。凌晨2点从未在那片土地上存在过。 ZonedDateTime
类具有自动调整此类无效时刻的策略。请务必阅读文档,了解您是否理解enter link description here并同意其算法。
因为这个挂钟时间从未存在过,所以我怀疑你对那些代表Australia/Sydney
区域中的时刻的日志数据不正确。
这里更大的解决方案是学习以UTC格式思考,工作,记录和交换数据。将UTC视为 One True Time ,所有其他区域仅仅是该主题的变体。在工作编程/管理时忘掉你自己的狭隘时区。
在 java.time 中,UTC值的基本类为Instant
。 Instant
类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位)。
此类可以捕获UTC中的当前时刻。
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Instant
类也可以解析/生成标准ISO 8601格式的字符串。
String output = Instant.now().toString() ;
“2018-01-23T12:34:56.734591Z”
最后Z
是Zulu
的缩写,表示UTC。这是您原始问题的症结所在:您存储的日期时间值没有区域/偏移的指示。
解析:
Instant instant = Instant.parse( "2018-01-23T12:34:56.734591Z" )
我强烈建议将ISO 8601格式用于文本日期时间值的所有此类存储/交换。一定要:
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
使用符合JDBC driver或更高版本的JDBC 4.2,您可以直接与数据库交换 java.time 对象。不需要字符串也不需要java.sql。* classes。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。