最近在使用SimpleDateFormat的时候,对一些日期进行转换遇到了奇怪的现象
先看一段代码
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setLenient(false);
Date dt = sdf.parse("1991-04-14 00:00:00");
System.out.println(sdf.format(dt));
乍一看 这段代码没有任何问题,理论上应该会输出1991-04-14 00:00:00
但是如果你真正去运行一下,就会发现,这是实际上会抛出异常
Exception in thread "main" java.text.ParseException: Unparseable date: "1991-04-14 00:00:00"
但是如果把日期加上一个小时,换成1991-04-14 01:00:00 就不会抛异常,并且结果也会正确显示
1986-05-04 00:00:00
1987-04-12 00:00:00
1988-04-10 00:00:00
1989-04-16 00:00:00
1990-04-15 00:00:00
这些日期也会有这种情况,以上现象在jdk1.5以上会出现,在1.4以下则没有这个问题,实际上这并不是jdk升级坏了,而恰恰是jdk修复了这个漏洞——其实应该说是更加符合实际情况。
先来看一个截图
看到这个你应该明白了什么,再甩一个链接,有兴趣的可以深入了解下
https://www.zhihu.com/question/20309772
所以,究其原因,为什么这些时刻在做转换的时候会抛出异常,就是因为这些时刻在历史上是不存在的,但是,仅限于本地历史,比如,1986-05-04 00:00:00,这个时刻只在中国历史上不存在,在美国,这个时间点是有的。在看一段代码
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setLenient(false);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
Date dt = sdf.parse("1991-04-14 00:00:00");
System.out.println(sdf.format(dt));
SimpleDateFormat有个方法可以设置时区,你会发现当你设置成纽约时的时候,这段代码就不会再抛出异常,原因很简单,美国的夏令时和中国的夏令时是不一样的,这个时间点在美国存在,因此这就不会解析出异常了。
但其实因为美国也有夏令时,你如果把日期字符串换成2017-03-12 02:00:00,也是会抛出一样的异常,因为今年美国的夏令时时间是3月12日,凌晨2点钟,所有时间会直接从2点变成3点。
所以,在做类似的日期转换的时候,我们可以挑一个在图中为红色区域的地方,那些地方从未实行夏令时,因此在日期格式转换上也不会出现这些问题,比如说你可以找一个中非时区:Africa/Brazzaville
sdf.setTimeZone(TimeZone.getTimeZone("Africa/Brazzaville"));
这样就能比较安全的对这些特殊地区的特殊时刻进行转换,这也是最近遇到感觉比较有意思的事情。
其实在时间转换上不只有夏令时这种少了一个小时的情况。
你可以试试对1582年10月5日到10月14日的任何一个时间进行转换,都是会报错的,因为在历史上这10天是不存在的。