zoukankan      html  css  js  c++  java
  • Java中夏令时带来的Date不一致问题 (转)

    http://www.cnblogs.com/snake-hand/archive/2013/06/10/3131157.html

    最近同事W发现使用Java Date创建日期,在不同的机器上执行,得到的部分天小时数不一致。一开始怀疑机器的时间同步有问题,便拿到自己的机器上运行,异常复现,开始排查。

     
    一. 测试代码
    package com.bc.time;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.GregorianCalendar;
    
    public class TimeZoneTest {
    	public static void main(String[] args) {
    		TimeZoneTest test = new TimeZoneTest();
    		
    		final long ONE_DAY_MILLIS = 1000 * 3600 * 24L;
    		Date date0 = new Date(ONE_DAY_MILLIS * 0);
    		System.out.println("start date=" + test.format(date0) + "millis=" +  date0.getTime());
    		System.out.println("********************");
    		int count = 0;
    		
    		for (int i = 0; i < 300000; ++i) {
    			long millis = ONE_DAY_MILLIS * i;
    			Date date = new Date(millis);
    			
    			if (date.getHours() != 8 || date.getMinutes() != 0 || date.getSeconds() != 0) {
    				System.out.println("i = " + i + " | " + test.format(date) + "| " + date + ", millis=" + millis);
    			
    				GregorianCalendar newDate = new GregorianCalendar();
    				newDate.setTimeInMillis(millis);
    				System.out.println("i = " + i + " | newDate " + newDate.getTimeInMillis() + "| " + newDate);
    				count++;
    			}
    		}
    		System.out.println("count=" + count);
    	}
    	
    	public String format(Date date){
    		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		String dateStr = format.format(date);
    		return dateStr;
    	}
    }
    首先,Date和Calendar计算的都是local time,和本地的时区有关,任何联网计算机的系统时间都是一致的。
    按照每天都是24小时的假设,上述程序的if条件理应恒为false,有意思的是,从1986-05-04这天开始,打出的时间不再是8:00,而是9:00点,一共打出了903个日期,最后一个日期是1991-09-14 09:00:00。
     
    二. 恼人的夏令时
    既然"异常"是从1986-05-04这天开始的,先从这天排查,该天正好是个夏令时。以下摘抄来自百度百科:
    夏时制(Daylight Saving Time:DST),又称“日光节约时制”和“夏令时间”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间提前一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。目前全世界有近110个国家每年要实行夏令时。 自2011年3月27日开始俄罗斯永久使用夏令时,把时间拨快一小时,不再调回。
    夏令时会导致某一天多出一个小时,或者少出一个小时。
    那Java中那些天是夏令时呢,见代码:
    package com.bc.time;
    
    import java.util.Calendar;
    import java.util.Date;
    import java.util.TimeZone;
    import java.text.SimpleDateFormat;
    
    public class WhatTime {
        
         /**
          * Dates  those have not EXACTLY 24 hours ?
          **/
         public static void testDayTime(TimeZone timeZone){
             
              SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             
              System.out.println("Time Zone is " + timeZone.getDisplayName() + " " + timeZone.getID());
             
              Calendar start = Calendar.getInstance(timeZone);
              start.setTime(new Date(0));//UTC 1970-01-01
             
              System.out.println("start=" + fmt.format(start.getTime()));
             
              long end = Calendar.getInstance(timeZone).getTimeInMillis();//current time
             
              boolean find = false;
              for(long i = start.getTimeInMillis(); i < end; i= start.getTimeInMillis() ){
                   start.add(Calendar.DATE, 1); //add one day
                  
                   if((start.getTimeInMillis() - i)%(24*3600*1000L) != 0){
                       find = true;
                        System.out.println("from " + fmt.format(new Date(i)) +
                                  "to " + fmt.format(start.getTime()) +
                                  " has " + (start.getTimeInMillis() - i) + "ms" +
                                  "[" + (start.getTimeInMillis() - i)/(3600*1000L) + "hours]");
                   }
              }
              if(!find){
                  System.out.println("Every day is ok.");
              }
         }
        
         public static void main(String argv[] ) throws Exception{
             
              TimeZone timeZone = TimeZone.getDefault();
              WhatTime.testDayTime(timeZone);
             
              System.out.println("----------------------------------------------------------------");
             
              timeZone = TimeZone.getTimeZone("GMT");
              WhatTime.testDayTime(timeZone);
         }
    
    }
    Time Zone is 中国标准时间 Asia/Shanghai
    start=1970-01-01 08:00:00
    from 1986-05-03 08:00:00to 1986-05-04 08:00:00 has 82800000ms[23hours]
    from 1986-09-13 08:00:00to 1986-09-14 08:00:00 has 90000000ms[25hours]
    from 1987-04-11 08:00:00to 1987-04-12 08:00:00 has 82800000ms[23hours]
    from 1987-09-12 08:00:00to 1987-09-13 08:00:00 has 90000000ms[25hours]
    from 1988-04-09 08:00:00to 1988-04-10 08:00:00 has 82800000ms[23hours]
    from 1988-09-10 08:00:00to 1988-09-11 08:00:00 has 90000000ms[25hours]
    from 1989-04-15 08:00:00to 1989-04-16 08:00:00 has 82800000ms[23hours]
    from 1989-09-16 08:00:00to 1989-09-17 08:00:00 has 90000000ms[25hours]
    from 1990-04-14 08:00:00to 1990-04-15 08:00:00 has 82800000ms[23hours]
    from 1990-09-15 08:00:00to 1990-09-16 08:00:00 has 90000000ms[25hours]
    from 1991-04-13 08:00:00to 1991-04-14 08:00:00 has 82800000ms[23hours]
    from 1991-09-14 08:00:00to 1991-09-15 08:00:00 has 90000000ms[25hours]
    ----------------------------------------------------------------
    Time Zone is 格林威治时间 GMT
    start=1970-01-01 08:00:00
    Every day is ok.
     
     
    三. 结论
    1. 原来Java中不是每天都是标准的24个小时,可能是23,也可能是25
    2. 日期的计算,使用Calendar提供的API,是不会出差错的,简单的new Date(long milliseconds)可能并不靠谱
    3. 来自多方协作的项目,最好使用统一的时间标准,例如系统时间,或是统一时区
    4. 好消息是,从1992年开始,中国已经停止使用夏令时。
     
    四. 附录
    详细的夏令时时区数据页面:http://www.twinsun.com/tz/tz-link.htm
  • 相关阅读:
    Java基础50道经典练习题(33)——杨辉三角
    Java基础50道经典练习题(32)——左移右移
    团队第二阶段冲刺04
    团队第二阶段冲刺03
    团队第二阶段冲刺02
    团队第二阶段冲刺01
    团队意见汇总
    各组意见汇总
    团队第一阶段冲刺07
    绩效评估01
  • 原文地址:https://www.cnblogs.com/quietwalk/p/7573240.html
Copyright © 2011-2022 走看看