一,先看看中国的夏令时
1986年至1991年,中华人民共和国在全国范围实行了六年夏令时,每年从4月中旬的第一个星期日2时整(北京时间)到9月中旬第一个星期日的凌晨2时整;
十日为旬。 上旬 每月第一日至第十日的十天,为上旬。 中旬 每月十一日到二十日的十天,为中旬,下旬同理
可以通过如下代码找出这6的异常点
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); }
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] ----------------------------------------------------------------然后我们再准确的找下是那几个时间点有问题
// // 比如1986年的夏令时时间,从代码来看,是从1986-05-04 00:00:00到1986-09-13 22:59:59 // 但是java也不一定对,需要看老人或者看当年的报纸才能知道准确的夏令时时间 @Test public void test4() throws Exception { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); TimeZone zone = TimeZone.getDefault(); Date d12=sf.parse("1986-05-03 23:59:00");// Date d13=sf.parse("1986-05-04 00:00:00");// Date d14=sf.parse("1986-06-04 00:00:00");// Date d15=sf.parse("1986-09-13 22:59:59");// Date d16=sf.parse("1986-09-13 23:00:00");// //突变点 System.out.println("===1986-05-04 00:00:00实际时间====="+sf.parse("1986-05-04 00:00:00").toLocaleString()); System.out.println("===1986-09-13 22:59:59实际时间====="+sf.parse("1986-09-13 22:59:59").toLocaleString()); System.out.println("===1986-09-13 23:00:00实际时间====="+sf.parse("1986-09-13 23:00:00").toLocaleString()); System.out.println("===1986-09-13 23:59:59实际时间====="+sf.parse("1986-09-13 23:59:59").toLocaleString()); Date d21=sf.parse("1987-04-11 23:59:00");// Date d22=sf.parse("1987-04-12 00:00:00");// Date d23=sf.parse("1987-09-12 22:59:59");// Date d24=sf.parse("1987-09-12 23:00:00");// Date d31=sf.parse("1988-04-09 23:59:59");// Date d32=sf.parse("1988-04-10 00:00:00");// Date d33=sf.parse("1988-09-10 22:59:59");// Date d34=sf.parse("1988-09-10 23:00:00");// System.out.println("==============="); Date d41=sf.parse("1989-04-15 23:59:59");// Date d42=sf.parse("1989-04-16 00:00:00");// Date d43=sf.parse("1989-09-16 22:59:59");// Date d44=sf.parse("1989-09-16 23:00:00");// System.out.println("==============="); Date d51=sf.parse("1990-04-14 23:59:59");// Date d52=sf.parse("1990-04-15 00:00:00");// Date d53=sf.parse("1990-09-15 22:59:59");// Date d54=sf.parse("1990-09-15 23:00:00");// System.out.println("==============="); Date d61=sf.parse("1991-04-13 23:59:59");// Date d62=sf.parse("1991-04-14 00:00:00");// Date d63=sf.parse("1991-09-14 22:59:59");// Date d64=sf.parse("1991-09-14 23:00:00");// System.out.println("=========1986======="); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d12)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d13)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d14)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d15)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d16)); System.out.println("=========1987======="); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d21)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d22)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d23)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d24)); System.out.println("=========1988======="); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d31)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d32)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d33)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d34)); System.out.println("=========1989======="); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d41)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d42)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d43)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d44)); System.out.println("=========1990======="); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d51)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d52)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d53)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d54)); System.out.println("======1991=========="); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d61)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d62)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d63)); System.out.println("目标时区是否使用了夏令时:"+isDaylight(zone, d64)); //6个突变点如下,只在开始实行的时候变化 System.out.println("===1986-05-04 00:00:00实际时间====="+sf.parse("1986-05-04 00:00:00").toLocaleString()); System.out.println("===1987-04-12 00:00:00实际时间====="+sf.parse("1987-04-12 00:00:00").toLocaleString()); System.out.println("===1988-04-10 00:00:00实际时间====="+sf.parse("1988-04-10 00:00:00").toLocaleString()); System.out.println("===1990-04-15 00:00:00实际时间====="+sf.parse("1990-04-15 00:00:00").toLocaleString()); System.out.println("===1991-04-14 00:00:00实际时间====="+sf.parse("1991-04-14 00:00:00").toLocaleString()); } //判断是否在夏令时 private boolean isDaylight(TimeZone zone,Date date) { //正常逻辑是:时区使用了夏令时再判断时间,这里因为中国取消了 if (zone.getID().equals("Asia/Shanghai")) { return zone.inDaylightTime(date); } return zone.useDaylightTime()&&zone.inDaylightTime(date); }
通过上面,找到了6个突变点:
1986-05-04 00:00:00
1987-04-12 00:00:00
1988-04-10 00:00:00
1989-04-16 00:00:00
1990-04-15 00:00:00
1991-04-14 00:00:00/**
* 说明:这6个夏令时日期会导致实际存入数据库的date日期发生变化,比如写的是1986-05-04,实际存入的是1986-05-04 01:00:00
* 如果之后在数据库里面进行日期匹配的时候会出现问题,你需要trunc(date)来比较
*
*
* 这里有2个问题,
* 1.为什么夏令时结束的时候不会突变呢,比如:1986-09-13 23:00:00这个时候结束了夏令时
* 按理实际的时间应该是变为:1986-09-13 22:00:00, 结果没有变,那么减少的那一个小时去哪里了呢?怎么体现?(待琢磨。。。)
* 2.上面得到的夏令时时间范围跟实际规定的真的一样吗?
*
* 下面我们看看美国的夏令时是不是也是这种情况
*/
//美国的夏令时从三月的第二个周日开始到十一月的第一个周日结束 //已知官方2016年:America/New_York的夏令时时间是: 2016-3-13 02:00:00 到 2016-11-06 01:59:59 @Test public void test5() throws Exception { // 转换为0时区时间作为参照点 SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // sf.setTimeZone(TimeZone.getTimeZone("GMT+0")); TimeZone york = TimeZone.getTimeZone("America/New_York"); // GMT-5 sf.setTimeZone(york); Date d1 = sf.parse("2016-03-13 01:59:59");// false Date d2 = sf.parse("2016-03-13 02:00:00");// true Date d3 = sf.parse("2016-11-06 00:59:59");// true Date d3_1 = sf.parse("2016-11-06 01:00:00");// false Date d3_2 = sf.parse("2016-11-06 01:59:59");// false预计是夏令时时间,实际不是 Date d4 = sf.parse("2016-11-06 02:00:00");// false //可以发现,对于夏令时开始的时间判断确实没问题,但是对于夏令时的结束时间判断错误,准确说,提前了一个小时判断了 System.out.println("目标时区是否使用了夏令时:" + isDaylight(york, d1)); System.out.println("目标时区是否使用了夏令时:" + isDaylight(york, d2)); System.out.println("目标时区是否使用了夏令时:" + isDaylight(york, d3)); System.out.println("目标时区是否使用了夏令时:" + isDaylight(york, d3_1)); System.out.println("目标时区是否使用了夏令时:" + isDaylight(york, d3_2)); System.out.println("目标时区是否使用了夏令时:" + isDaylight(york, d4)); }
//再来看下美国夏令时的突变时间 @Test public void test6() throws Exception { //中间相隔13个小时 中国+8 纽约-5 ChangeZone("2016-3-13 14:59:59", "PRC","America/New_York", "yyyy-MM-dd HH:mm:ss");//2016-03-13 01:59:59 ChangeZone("2016-3-13 15:00:00", "PRC","America/New_York", "yyyy-MM-dd HH:mm:ss");//2016-03-13 03:00:00 ChangeZone("2016-11-6 13:59:59", "PRC","America/New_York", "yyyy-MM-dd HH:mm:ss");//2016-11-06 01:59:59 //这个结果是不对的,应该02:00:00,结果还是01:00:00 ChangeZone("2016-11-6 14:00:00", "PRC","America/New_York", "yyyy-MM-dd HH:mm:ss");//2016-11-06 01:00:00 } /** *通过以上测试可以发现:无论是中国还是美国的夏令时 *依然存在上面的2个问题: *1.夏令时在结束的时间点是不会突变的,具体原因待查 *2.通过代码判断的夏令时时间段比 实际宣传的少一个小时 */ public static void ChangeZone(String time, String srcID, String destID, String pattern) throws ParseException { // 设置默认时区 TimeZone zone = TimeZone.getTimeZone(srcID); TimeZone.setDefault(zone); Date date = new SimpleDateFormat(pattern).parse(time); // 设置目标时区 TimeZone destzone = TimeZone.getTimeZone(destID); SimpleDateFormat sdf = new SimpleDateFormat(pattern); // 设置要格式化的时区 sdf.setTimeZone(destzone); String changTime = sdf.format(date); // 获取目标时区 System.out.println("修改时区后" + destzone.getID() + "的时间:" + changTime); }
小结:
虽然存在这2个问题,但是时间并不影响我们代码逻辑,
唯一要注意的是夏令时开始时那6个突变点,是真实的会影响日期的准确性,在数据库进行date比较时,必须要截取日期来比较
*1.夏令时在结束的时间点是不会突变的,具体原因待查 *2.通过代码判断的夏令时时间段比 实际宣传的少一个小时
如果你想解决第一个问题,即在夏令时结束的时候,也让其时间突变,
参考我的另外一篇文章:
http://blog.csdn.net/u011165335/article/details/76636296