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中夏令时带来的Date不一致问题 (转)的更多相关文章

  1. java 中的SimpleDateFormat、Date函数以及字符串和Date类型互转

    SimpleDateFormat是一个以与语言环境有关的方式来格式化和解析日期的具体类.它允许进行格式化(日期 -> 文本).解析(文本 -> 日期)和规范化. SimpleDateFor ...

  2. java中XMLGregorianCalendar类型和Date类型之间的相互转换

    import java.text.SimpleDateFormat;import java.util.Date;import java.util.GregorianCalendar;import ja ...

  3. java中String类型与Date日期类型的互相转换

    //String格式的数据转化成Date格式,Date格式转化成String格式 SimpleDateFormat formatter= new SimpleDateFormat("yyyy ...

  4. Java中String型与Date型数据的互相转换

    /** * Date类型转为指定格式的String类型 * * @param source * @param pattern * @return */ public static String Dat ...

  5. Java中的时间日期Date和Calendar

    日期时间类 Date: Date类的构造方法: 可以发现Date类的toString方法被重写了. Date类的方法: SimpleDateFormat 它提供了解决Date输出问题的解决方案--格式 ...

  6. Java中Date类型与String 类型之间的互相转换

    Java中String类型和Date类型之间的转换 我们在注册网站的时候,往往需要填写个人信息,如姓名,年龄,出生日期等,在页面上的出生日期的值传递到后台的时候是一个字符串,而我们存入数据库的时候确需 ...

  7. java中hashCode()与equals()详解

    首先之所以会将hashCode()与equals()放到一起是因为它们具备一个相同的作用:用来比较某个东西.其中hashCode()主要是用在hash表中提高 查找效率,而equals()则相对而言使 ...

  8. Java中关于时间日期格式保存到mysql的问题

    首先在设置数据库的时间日期字段的时候要先确定好采用何种类型,DATETIME. TIMESTAMP.DATE.TIME.YEAR. 其中datetime.time用的比较多,对应java中生成的poj ...

  9. 第七节:详细讲解Java中的日期,java.util.date

    前言 大家好,给大家带来详细讲解Java中的日期,java.util.date的概述,希望你们喜欢 类Date Java.lang.Object->java.util.Date public c ...

随机推荐

  1. Linux/CentOS实现交换机-简单的交换机实现

    个人理解: 1.交换机的核心在于Mac地址学习,使其能在全双工/半双工下进行转发工作. 2.对于专业的交换机来说,使用的是专业的网络芯片并自己实现里面的协议,比如说华为的,为什么能卖那么贵,贵在于网络 ...

  2. CentOS 7使用yum安装PHP5.6

    删除旧php包 yum remove php.x86_64 php-cli.x86_64 php-common.x86_64 php-gd.x86_64 php-ldap.x86_64 php-mbs ...

  3. CentOS启动OpenVPN报错:Failed to start OpenVPN Robust And Highly Flexible Tunneling Application On server.

    tailf /var/log/openvpn.log 查看日志,里面有最详细的错误解说. 参考: https://forums.openvpn.net/viewtopic.php?t=21561

  4. [原创]SpotLight性能监控工具使用介绍

    [原创]SpotLight性能监控工具使用介绍 1  Spotlight工具是什么? SpotLight 是由Quest公司出品的一款第三方性能监控的图形化工具.SpotLight有一些的产品诸如可以 ...

  5. [原创]Jmeter工具学习思维导图

    [原创]Jmeter工具学习思维导图

  6. ISO 7816-4: GET RESPONSE and ENVELOPE command

    http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_7_transmission_interindustry_command ...

  7. Android WebView加载Html右边空白问题的解决方案

    用WebView显示Html时,右边会出现一条空白区,如下图所示: 最开始的时候,认为是网页本身的空白. 后来发现网页本身无问题,且这个空白区是跟Scroll Bar 的位置和粗细比较相符,于是去控制 ...

  8. SpringMVC介绍之视图解析器ViewResolver

    在前一篇博客中讲了SpringMVC的Controller控制器,在这篇博客中将接着介绍一下SpringMVC视图解析器.当我们对SpringMVC控制的资源发起请求时,这些请求都会被SpringMV ...

  9. 国内打不开onedrive,怎么办?

    Onedrive不能正常连接使用是由于DNS遭到污染闹的,其上传和下载文件慢也是DNS遭到污染闹的. 方法/步骤   在C盘windows/system32/drivers/etc/hosts下,用记 ...

  10. CRC8算法DELPHI源码

    unit Crc8; interface Uses Classes, Windows; Function Crc_8n(p : array of BYTE; len : BYTE) : Byte; i ...