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. 让nginx支持HLS

    准备工作: 1.安装nginx和rtmp模块 2.安装ffmepg(用来推流) 以上准备工作参见这篇博客:http://www.cnblogs.com/damiao/p/5231221.html 1. ...

  2. Oracle初始化参数之memory_target

    一.引言: Oracle 9i引入pga_aggregate_target,可以自动对PGA进行调整: Oracle 10g引入sga_target,可以自动对SGA进行调整: Oracle 11g则 ...

  3. A brief CRC tutorial

    https://www.kernel.org/doc/Documentation/crc32.txt A brief CRC tutorial. A CRC is a long-division re ...

  4. STM32的CRC32 测试代码

    // STM32 CRC32 Test App - sourcer32@gmail.com #include <windows.h> #include <stdio.h> DW ...

  5. 「GIT SourceTree冲突」解决方案

    现在程序猿标配GIT作为代码管理,但是从SVN到GIT学习中,其中GIT的冲突是一个难点,常常会导致Push不上去,Pull不下来,很尴尬的地步,还不知道自己写的代码被覆盖没,废话不多说,直接上干货! ...

  6. Asp.net core使用IIS在windows上进行托管

    摘要 最近项目中,尝试使用asp.net core开发,在部署的时候,考虑现有硬件,只能部署在windows上,linux服务器暂时没有. 部署注意事项 代码中启用iis和Kestrel public ...

  7. Idea2018激活破解

    原文:https://www.imsxm.com/2018/07/idea-2018-1-5-crack-patcher.html 最近更新了Intellij IDEA到2018.1.5之后,使用之前 ...

  8. FAQ:如何提高编译速度?

    问: 如何提高编译速度? 答: 减少一次需要编译的代码量,目前想到的有两种思路: 1:修改解决方案的属性配置,取消勾选不常修改的项目的“生成”复选框. 2:采用插件化的架构,每一个插件弄一个解决方案, ...

  9. mysql中文显示问号,不能识别中文的解决方案

    到后台mysql中查看是问号. 并不能显示出中文,初步判定是编码的问题. 我们利用 show variables like'character_set_%'; 与 show variables lik ...

  10. 详细解读Volley(三)—— ImageLoader & NetworkImageView

    ImageLoader是一个加载网络图片的封装类,其内部还是由ImageRequest来实现的.但因为源码中没有提供磁盘缓存的设置,所以咱们还需要去源码中进行修改,让我们可以更加自如的设定是否进行磁盘 ...