精彩文章免费看

浅解 JAVA与时区

时区转换

主要介绍一下 Java 时区转换相关的一些概念,和转换示例。

由于夏令时的存在,应该通过Java 或者 DB 提供的方法来转换。

JAVA 时间的时区转换

Java Date 支持 UTC 时间

世界标准时间:2018-01-31T14:32:19Z
T 表示后面跟着时间,Z 表示时区为 0 时区

本地时间,也叫不含时区信息的时间,末尾没有Z
2018-01-31T14:32:19

Java 时区 java.util.TimeZone 类

TimeZone 表示时区偏移量,也可以计算夏令时。
可以通过 getDefault 获取当前系统时区例如,对于在中国运行的程序,getDefault 基于中国标准时间创建 TimeZone 对象。

也可使用getTimeZone来获取某个时区 ID 的 TimeZone。例如美国太平洋时区的时区 ID 是 "America/Los_Angeles"。因此,可以使用下面语句获得美国太平洋时间 TimeZone 对象:
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");通过 getAvailableIDs 方法获取所有受支持的时区 ID (时区 map 保存在 ZoneInfoFile 类中) ,通过这些 ID 可以正确转换时区(包括夏令时的计算)。

如果想要的时区无法用受支持的 ID 之一表示,那么可以指定自定义时区 ID 来生成 TimeZone。自定义时区 ID 的语法是:
CustomID:
GMT Sign Hours : Minutes
GMT Sign Hours Minutes
GMT Sign Hours
Sign: 下面之一
Hours:
Digit
Digit Digit
Minutes:
Digit Digit
Digit: 下面之一
0 1 2 3 4 5 6 7 8 9
Hours 必须在 0 至 23 之间,Minutes 必须在 00 至 59 之间。例如,"GMT+10" 和 "GMT+0010" 分别意味着比 GMT 提前 10 小时和 10 分钟。

格式是与区域无关的,并且数字必须取自 Unicode 标准的 Basic Latin 块。没有夏令时转换安排可以用自定义时区 ID 指定。如果指定的字符串与语法不匹配,就使用 "GMT"。

Java 时区转换

SimpleDateFormat

    String timeSummer = "2019-03-10 09:00:00"; //字面时间
    String timeSummer1 = "2019-03-10 10:00:00"; //字面时间
    // 设置为 GMT 时间
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
    Date date9 =  dateFormat.parse(timeSummer);
    Date date10 = dateFormat.parse(timeSummer1);
    // timeSummer,timeSummer1的当前时区时间
    System.out.println(TimeZone.getDefault().getDisplayName() + "," + date9);
    System.out.println(TimeZone.getDefault().getDisplayName() + "," + date10);
    // timeSummer,timeSummer1的太平洋时区时间,转换夏令时
    dateFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
    System.out.println(dateFormat.getTimeZone().getDisplayName() + "," + dateFormat.format(date9));
    System.out.println(dateFormat.getTimeZone().getDisplayName()+ "," + dateFormat.format(date10));
    // timeSummer,timeSummer1的自定义时区时间,没有转换夏令时
    dateFormat.setTimeZone(TimeZone.getTimeZone("GMT-08:00"));
    System.out.println(dateFormat.getTimeZone().getDisplayName() + "," + dateFormat.format(date9));
    System.out.println(dateFormat.getTimeZone().getDisplayName() + "," + dateFormat.format(date10));

运行结果:

China Standard Time,Sun Mar 10 17:00:00 CST 2019
China Standard Time,Sun Mar 10 18:00:00 CST 2019
Pacific Standard Time,2019-03-10 01:00:00
Pacific Standard Time,2019-03-10 03:00:00
GMT-08:00,2019-03-10 01:00:00
GMT-08:00,2019-03-10 02:00:00

ZonedDateTime

    LocalDateTime localDateTime =    LocalDateTime.now();
    // 无时区的本地时间
    System.out.println(localDateTime);
    System.out.println(LocalDateTime.now(ZoneId.of("America/New_York")));
    // 向本地时间添加时区
    System.out.println(localDateTime.atZone(ZoneId.of("America/New_York")));
    System.out.println(ZonedDateTime.of(localDateTime, ZoneId.of("America/New_York")));
    // 从 ZoneDateTime 获取不同时区的时间
    System.out.println(new Date(0));
    System.out.println(ZonedDateTime.ofInstant(new Date(0).toInstant(), ZoneId.of("GMT")));
    System.out.println(ZonedDateTime.ofInstant(new Date(0).toInstant(), ZoneId.of("America/New_York")));
    ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(new Date(0).toInstant(), ZoneId.of("GMT"));
    System.out.println(zonedDateTime.withZoneSameInstant(ZoneId.of("America/New_York")));
    System.out.println(zonedDateTime.withZoneSameInstant(ZoneId.of("GMT+08:00")));

MySQL convert_tz() 时区转换函数

CONVERT_TZ(dt,from_tz,to_tz) 转换datetime值dt,从 from_tz 由给定转到 to_tz 时区给出的时区,并返回的结果值。 如果参数无效该函数返回NULL。

mysql> SELECT CONVERT_TZ('2004-01-01 12:00:00','GMT','MET');  
+---------------------------------------------------------+  
| CONVERT_TZ('2004-01-01 12:00:00','GMT','MET')           |  
+---------------------------------------------------------+  
| 2004-01-01 13:00:00                                     |  
+---------------------------------------------------------+  
1 row in set (0.00 sec)  
mysql> SELECT CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00');  
+---------------------------------------------------------+  
| CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00')     |  
+---------------------------------------------------------+  
| 2004-01-01 22:00:00                                     |  
+---------------------------------------------------------+  
1 row in set (0.00 sec)

Unix时间戳

Unix的时间戳都是1970年1月1日0时0分0秒开始到现在的秒数。

起点1970-01-01T00:00:00 指的是 GMT 时间,Unix时间戳是 0,在中国北京时区时间为1970-01-01 T08:00:00, 在美国纽约时间为 1969-12-31T16:00:00。

TimeZone 时区

理论时区以被15整除的子午线为中心,向东西两侧延伸7.5度,即每15°划分一个时区,划分24个时区。
为了避开国界线,划分了法定时区--时区列表

TAI UT UTC GMT 时间

UT Universal Time

世界时,一种以格林尼治子夜起算的平太阳时。世界时从 UT0 开始,修正到 UT1、UT2。UT 的1s为全年内每日平均长度的1/8.64×104,由于天体运动不规律,比如地球本身自转速度不均匀,导致时间长度不均匀。

GMT Greenwich Mean Time

格林威治标准时间,平均太阳时。
GMT 时间在1972年之前与世界时( UT )相同,1972年后 GMT 不在作为时间标准。现在 GMT 是一个时区(GMT 0:00 格林尼治标准时间)。

UTC Coordinated Universal Time

世界协调时,是当今最主要的世界时间标准,以原子时秒长为基础。为确保 UTC 与世界时相差不会超过 0.9 秒,在有需要的情况下会在协调世界时内加上正或负闰秒。因此协调世界时与国际原子时之间会出现若干整数秒的差别。位于巴黎的国际地球自转事务中央局负责决定何时加入闰秒,一般会在每年的 6 月 30 日、12 月 31 日的最后一秒进行调整。

Leap Second 闰秒

闰秒,是指为保持协调世界时接近于世界时时刻,由国际计量局统一规定在年底或年中(也可能在季末)对协调世界时增加或减少1秒的调整。由于地球自转的不均匀性和长期变慢性(主要由潮汐摩擦引起的),会使世界时(民用时)和原子时之间相差超过到±0.9秒时,就把协调世界时向前拨1秒(负闰秒,最后一分钟为59秒)或向后拨1秒(正闰秒,最后一分钟为61秒); 闰秒一般加在公历年末或公历六月末。

Unix time (Unix 时间戳)

从协调世界时1970年1月1日0时0分0秒起至现在的总秒数,不考虑闰秒。即Unix不存在闰秒。

NTP Network Time Protocal

计算机时间同步化的一种协议,它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒),且可介由加密确认的方式来防止恶毒的协议攻击。时间按NTP服务器的等级传播。按照离外部UTC源的远近把所有服务器归入不同的Stratum(层)中。

谷歌将修改其NTP服务器,使之在闰秒前、后各10小时的期限内时钟频率降低0.0014%。自2008年的闰秒以来,谷歌一直使用这一技术。

Daylight Saving Time:DST

夏令时,又称“日光节约时制”或“夏时制”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间提前一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏令时的国家具体规定不同。目前全世界有近110个国家每年要实行夏令时。