java 夏令时jvm设置问题

关于上一篇https://blog.csdn.net/u011165335/article/details/78924498
介绍了中国以及美国等的夏令时的特点:
*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:001986-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文件;

猜你喜欢

转载自blog.csdn.net/u011165335/article/details/81916265