时区转换
主要介绍一下 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个国家每年要实行夏令时。