搞过Java的都知道1970/1/1问题,没错!我们java时间戳就是这天开始,这个我相信大家都知道历史原因:
——
最初计算机操作系统是32位,而时间也是用32位表示。32位能表示的最大值是2147483647。另外1年365天的总秒数是31536000,2147483647/31536000 = 68.1,也就是说32位能表示的最长时间是68年,而实际上到2038年01月19日03时14分07秒,便会到达最大时间,过了这个时间点,所有32位操作系统时间便会变为10000000 00000000 00000000 00000000,也就是1901年12月13日20时45分52秒,这样便会出现时间回归的现象,很多软件便会运行异常了。
到这里,我想问题的答案已经出来了:因为用32位来表示时间的最大间隔是68年,而最早出现的UNIX操作系统考虑到计算机产生的年代和应用的时限综合取了1970年1月1日作为UNIX TIME的纪元时间(开始时间),至于时间回归的现象相信随着64为操作系统的产生逐渐得到解决,因为用64位操作系统可以表示到292,277,026,596年12月4日15时30分08秒,相信我们的N代子孙,哪怕地球毁灭那天都不用愁不够用了,因为这个时间已经是千亿年以后了。
Date date = new Date(0);
System.out.println(date);
出来的结果:Thu Jan 01 08:00:00 CST 1970
今天,又遇到一个因历史问题而产生的坑:
// .Net中Datetime的起始时间
String doNetStartTime = "0001-01-01T00:00:00.0000000+08:00";
// 利用joda(jdk8中已包含) datetime转换成一个GregorianCalendar对象
GregorianCalendar gregorianCalendar = DateTime.parse(doNetStartTime).toGregorianCalendar();
// 将这个calendar对象的date属性用simpledateformat格式化打印出来
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSXXX");
System.out.println(format.format(gregorianCalendar.getTime()));
结果却是:0001-01-03T00:00:00.000+08:00
为啥日期变成了3号?网上搜到一个中文靠谱解释:
——
在java.time包中,像所有主要的时间/日期类一样,LocalDate是固定于单个历法系统的:由ISO-8601标准定义。
ISO-8601历法系统是事实上的世界民用历法系统,也就是公历。平年有365天,闰年是366天。闰年的定义是:非世纪年,能被4整除;世纪年能被400整除。为了计算的一致性,公元1年的前一年被当做公元0年,以此类推。
采用这套历法,第一个影响就是,ISO-8601的日期不必跟GregorianCalendar一致。在GregorianCalendar中,凯撒历和格里高利历之间有一个转换日,一般默认在1582年10月15日。那天之前,用凯撒历:每4年一个闰年,没有例外。那天之后,用格里高利历,也就是公历,拥有稍微复杂点的闰年计算方式。
既然凯撒历和格里高利历之间的转换是个历史事实,那为什么新的java.time开发包不参照它呢?原因就是,现在使用历史日期的大部分Java应用程序,都是不正确的,继续下去,是个错误。这是为什么呢?当年,罗马的梵蒂冈,把历法从凯撒历改换成格里高利历的时候,世界上大部分其他地区并没有更换历法。比如大英帝国,包括早期的美国,直到大约200后的1752年9月14日才换历法,沙俄直到1918年2月14日,而瑞典的历法转换更是一团糟。因此,实际上,对1918之前的日期,解释是相当多的;仅相信拥有单一转换日的GregorianCalendar,是不靠谱的。所以LocalDate中没有这种转换,就是一个合理的选择了。应用程序需要额外的上下文信息,才能在凯撒历和格里高利历间,精确的解释特定的历史日期。
英文好的同学可以直接看JDK中java.util.GregorianCalendar的jdoc,太长,这里不贴了···
总结:
Joda-time是对ISO8601国际标准化组织的国际标准的一个实现,1582年10月15日之前是没有Gregorian历法的(又译作格林尼治日历时间),所以使用Joda-time把一个历法外的时间用该历法进行转换时,结果将是不正确的。