关于上一篇
介绍了中国以及美国等的夏令时的特点:
*1.夏令时在结束的时间点是不会突变的
*2.通过代码可以知道实际的夏令时时间段比 宣传的少一个小时
系统时区在:注册表
计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\China Standard Time
先回顾一下之前的6个突变点:
环境jdk6
package com.ysy;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import java.util.TimeZone;
import org.junit.Test;
public class Demo {
// 比如1986年的夏令时时间,从代码来看,是从1986-05-04 00:00:00到1986-09-13 22:59:59
@Test
public void test4() throws Exception {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
TimeZone zone = TimeZone.getDefault();
System.out.println(zone);
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");//
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("===1989-04-16 00:00:00实际时间=====" + sf.parse("1989-04-16 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个时间点发生加1个小时的变化:
1986-05-04 00:00:00实际时间=====1986-5-4 1:00:00
1987-04-12 00:00:00实际时间=====1987-4-12 1:00:00
1988-04-10 00:00:00实际时间=====1988-4-10 1:00:00
1989-04-16 00:00:00实际时间=====1989-4-16 1:00:00
1990-04-15 00:00:00实际时间=====1990-4-15 1:00:00
1991-04-14 00:00:00实际时间=====1991-4-14 1:00:00
上面出现,这种情况,说明系统的时区是Asia/Shanghai
那么如果不让时间变化?
java.util.TimeZone类中getDefault方法的源代码显示,它最终是会调用sun.util.calendar.ZoneInfo类的getTimeZone 方法。这个方法为需要的时间区域返回一个作为ID的String参数。这个默认的时间区域ID是从 user.timezone (system)属性那里得到。如果user.timezone没有定义,它就会尝试从user.country和java.home (System)属性来得到ID。 如果它没有成功找到一个时间区域ID,它就会使用一个”fallback” 的GMT值。换句话说, 如果它没有计算出你的时间区域ID,它将使用GMT作为你默认的时间区域。
win对应jre\lib\zi
所以最关键的是设置好user.timezone即可;
如何设置,可以通过jvm参数设置或者System.setProperty
查看user.timezone
System.out.println(System.getProperty(“user.timezone”));
在jdk6,7输出的时区是空的;jdk8的user.timezone可以直接获取到系统时区;
所以只要在jvm设置 -Duser.timezone=GMT+8就可以了;
但是如果有多台服务器,一个设置了GMT+8,一个没设置,还是原来的Asia/Shanghai;那么2台机子的时间传输会发生时间的转换;
下面说的是模拟这种转换:
情况1:win系统的默认时区为Asia/Shanghai;
然后你设置了TimeZone.setDefault(TimeZone.getTimeZone(“GMT+8”));类似你在jvm设置了GMT+8
那么现在一个时间 比如1990-04-17 00:00:00 ,这个时间正处在夏令时时间;
当你设置了GMT+8时区后,它认为正确的显示时间应该是减一个小时,即1990-04-16 23:00:00;
怎么证明?
你可以在截图的里面,把VM参数该为: -Duser.timezone=Asia/Shanghai
然后设置TimeZone.setDefault(TimeZone.getTimeZone(“GMT+8”)),
即可以印证;
===在夏令时1986-05-18 00:00:00实际时间=====1986-5-17 23:00:00
===在夏令时1987-06-17 00:00:00实际时间=====1987-6-16 23:00:00
===在夏令时1988-07-14 00:00:00实际时间=====1988-7-13 23:00:00
===不在夏令时1988-09-11 00:00:00实际时间=====1988-9-11 0:00:00
同理情况2:如win系统的默认时区为GMT+8;
然后你设置了TimeZone.setDefault(TimeZone.getTimeZone(“Asia/Shanghai”));
那么现在一个时间,比如1990-04-17 00:00:00,这个时间对于GMT+8而言,没有夏令时概念;
当你设置了Asia/Shanghai这个时区后,(相当于带有夏令时的东八区),此时会加一个小时,因为对于Asia/Shanghai而言,刚好在夏令时时间内! 于是实际显示为:1990-04-17 01:00:00;
这里代码演示如下:
这里假设win系统的默认时区为GMT+8;
private void test() throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
TimeZone zone = TimeZone.getDefault();
System.out.println(zone);
// 夏令时开始时间
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("===1989-04-16 00:00:00实际时间=====" + sf.parse("1989-04-16 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());
// 夏令时结束时间
System.out.println("===1986-09-13 23:00:00实际时间=====" + sf.parse("1986-09-13 23:00:00").toLocaleString());
System.out.println("===1987-09-12 23:00:00实际时间=====" + sf.parse("1987-09-12 23:00:00").toLocaleString());
System.out.println("===1988-09-10 23:00:00实际时间=====" + sf.parse("1988-09-10 23:00:00").toLocaleString());
System.out.println("===1989-09-16 23:00:00实际时间=====" + sf.parse("1989-09-16 23:00:00").toLocaleString());
System.out.println("===1990-09-15 23:00:00实际时间=====" + sf.parse("1990-09-15 23:00:00").toLocaleString());
System.out.println("===1991-09-14 23:00:00实际时间=====" + sf.parse("1991-09-14 23:00:00").toLocaleString());
上面是已知的夏令时开始和结束时间
打印下面几个时间:
System.out.println("===在夏令时1986-05-18 00:00:00实际时间=====" + sf.parse("1986-05-18 00:00:00").toLocaleString());
System.out.println("===在夏令时1987-06-17 00:00:00实际时间=====" + sf.parse("1987-06-17 00:00:00").toLocaleString());
System.out.println("===在夏令时1988-07-14 00:00:00实际时间=====" + sf.parse("1988-07-14 00:00:00").toLocaleString());
System.out.println("===不在夏令时1988-09-11 00:00:00实际时间=====" + sf.parse("1988-09-11 00:00:00").toLocaleString());
}
可以发现:跟预测的一样,在夏令时的加1,不在的不变
===在夏令时1986-05-18 00:00:00实际时间=====1986-5-18 1:00:00
===在夏令时1987-06-17 00:00:00实际时间=====1987-6-17 1:00:00
===在夏令时1988-07-14 00:00:00实际时间=====1988-7-14 1:00:00
===不在夏令时1988-09-11 00:00:00实际时间=====1988-9-11 0:00:00
情况3:如win系统的默认时区为GMT+8;
然后你设置了TimeZone.setDefault(TimeZone.getTimeZone(“GMT+8”));那么时区一致,时间不会变化;
上面演示的就是类似2台机子时区不一致的情况;
jvm设置对机子的设置是可以直接修改user.timezone,没问题的;
让多台机子保持统一就可以了;
如果是linux,也可以修改clock文件;