一,先看看中国的夏令时
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:001988-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.通过代码判断的夏令时时间段比 实际宣传的少一个小时
如果你想解决第一个问题,即在夏令时结束的时候,也让其时间突变,