java中System.currentTimeMillis与夏令时

来源:互联网 发布:天津软件学院一卡通 编辑:程序博客网 时间:2024/05/16 05:39

这段正好有个故障,涉及到JAVA程序在向过去调整时间之后,系统出现的一些异常现象。手工通过系统程序时间日期管理器调整系统的时间、日期,发现Java用Timer、TimerTask框架实现的定时器机制,发现在时间被调整,特别往后面调整不断时间(几分钟粒度),就会发现定时器任务被延迟了。

当时,对这种大规模向过去调整时间的行为,本来不想修改,因为大规模向后调整时间,毕竟是一个非常非常异常的行为。但是,在讨论故障时有同事提到如果遇到夏令时的时间调整怎么办?看来他还是很有大局观的,我们系统确实需要应对夏令时。时间在夏令时进入的时候,时间会向将来调整,特别地在夏令时退出的时间,时间会向后面调整,照样会存在一个小时的大规模时间调整!!

而我们程序原来是被设计支持夏令时的,至少是程序业务设定某些数据记录时间字段的时间,是支持夏令时。但是,我确实很怀疑定时器,在遇到夏令时机制的时间,是否会出现延迟呢?
对于这个问题,同一个团队的同事,以前开发夏令时功能的,坚持说夏令时机制不会影响定时器。因为夏令时只是一个显示而已,而内部时间不会变,但,我一直很怀疑,一直在做研究。

今天,通过通读JDK1.6.17的源码和JavaDoc发现,我们同事这个说法在一定程度上是对的,也在阅读过程中给自己以理论指导的方向,但还有不足的地方。虽然,这刻,我没还没有验证,但是,直觉准确地告诉我,觉得,自己找到了答案。所以,自己很兴奋地写了这篇博客,也是为了答疑自己不断地在网上搜索,确实没有太大的真实性获得,几乎没有人回答System.currentTimeMillis的意义,也怪自己没有详细看JavaDoc中的说明。不过,就以当时的理解水平,看到也未必能够深入地理解,只有这一刻我觉得理解了。。。。

关于时间,在JavaDoc中谈论比较多文字的是UTC、UT、GMT等

UTC:科学纪年,时间采自原子时钟。在每过一两年会有一个跳秒,在某个跳点,一分钟有61秒
UT: GMT格林威治时间的科学学名,取自天文学观测

从这两种时间的定义,我们发现,从PC和一些机器系统获得所谓的UTC时间实际上是虚假的,因为其根本上就没有那样的设备或这样的tcp访问机制,让其知晓,其依靠的不过是主板上的晶振而已,跟上两种时间的定义有很大的区别。

在UT时间中,有一个特殊的时间,即起点 epoch:, namely January 1,  1970, 00:00:00 GMT. 如果在电脑上手工调整了系统时间,则距离此起点的时间值也会被调整。


夏令时是一种时间调整机制,与时区,特别某些国家所在的时区有很大关系。我们所用Java的Date类的接口中的计算是跟默认的时区有关系,计算的根本算法为现在距离起点时间的milliseconds,然后在根据特殊的时区的设定,包括偏移值,夏令时等,进行相应的计算。所以,说夏令时的进入和退出,仅于跟时区相绑定的计算有关系,例如Date、Calendar等,但是,跟系统距离起点时间的距离没有太大关系。这个时间距离在夏令时进入和退出时确实不会变化,一直是线性地增长的。不过,手工调整未必!特别是针对PC来说,呵呵,还是要强调这一点。

所以,我的结论是,夏令时不会影响定时器的执行,即使时间向后大范围调整。Java中Timer、TimerTask框架是依据 System.currentTimeMillis()进行计算下一次运行时间,这个时间在夏令时生效失效前后,一直是线性增长的,所以不会受到影响。但是,手工调整系统时间,则会遭遇到定时器的缓期执行。

摘一段Java JavaDoc中的文字感谢一些SUN。
Java中currentTimeMillis()方法返还的

     * @return  the difference, measured in milliseconds, between
     *          the current time and midnight, January 1, 1970 UTC.
     * @see     java.util.Date
     */
    public static native long currentTimeMillis();


记录一下前一段自己的辛勤劳动,验证结果:

    对于时间向过去调整后,由于迷惑,也研究了其他系统一些与时间有关的行为,发现一些有些很有意思的东西,行为不尽相同,特别是不同的系统:

    JavaScript:setInerval貌似不受时间调整影响,很准确地以固定间隔运行
    VC:  WM_Timer事件,不受时间调整影响,但是准确性降低
    Java: Timer、TimerTask定时器任务会受到影响,
          但是Thread.sleep不会受到影响,很怪异,可能使用的另外一套时间计算方法,例如操作系统的ticks。