Java 8之新日期时间API笔记(四)ZoneId

java Jul 12, 2018

前言

在现实生活中我们互相交流,默认使用的都是本地日期时间(LocalDate,LocalTime)。但是如果和大洋彼岸的美国打电话,我们就需要加上北京时间,华盛顿时间这样的前缀。因为虽然我们都在地球上,但是因为处在位置不同,所以看见的太阳的角度是不同。正对太阳的地区是白天,背对太阳的地区处于黑夜。不同的两地存在时差。所以把地球划分为24个不同的时区。但是生活中我们使用的是地理时区,它是根据国家或者地区规定使用同一个时区的区域。

ZoneId

Java新版日期时间API中使用ZoneId代表时区。ZoneId通常作为Instant和LocalDateTime转换的桥梁。Instant表示时间线上的一点(时间戳),LocalDateTime也表示时间点。他们两者不同之处就是Instant的时间点是带上了时区的。我们可以理解为Instant = LocalDateTime + ZoneId。下面我们就来详细介绍ZoneId。

ZoneOffset和ZoneRegion

ZoneId支持两种类型格式初始化,一种是时区偏移的格式(基于UTC/Greenwich时),一种是地域时区的格式(eg:Europe/Paris)。 ZoneId是抽象类,具体的逻辑实现由来子类完成,ZoneOffset处理时区偏移类型的格式,ZoneRegion处理基于地域时区的格式。但是我们在实际使用中还是使用ZoneId。他的子类我们一般不直接使用。
如下图:
image_1ci6g66ar10j3f1hpnc635ddnp.png-12.5kB

ZoneOffset

ZoneOffset表示从UTC/Greenwich标准时间的偏移量,eg:+02:00。另外不要使用==比较ZoneOffset,因为内部实现是有缓存的,所以可能是对同一个对象进行比较,比较可以使用equal方法。

ZoneRegion

ZoneRegion是地理时区的概念。ZoneRegion的规则由内部的ZoneRule制定。ZoneRule来源于ZoneRulesProvider。三者的关系如下图:
image_1ci6l486b1dl2bd3o29ofc1df416.png-25kB
地理时区不是一成不变的。权威的信息来源是Time Zone Database,由IANA制定。Java内部默认实现就是TzdbZoneRulesProvider。当然Java也提供了替换ZoneRulesProvider的方式。
1.通过环境变量设置

  • 首先实现自己的ZoneRuleProvider类
  • 加入到Java ClassPath路径下(放到项目中就可以了)
  • 设置java.time.zone.DefaultZoneRulesProvider环境变量如下
java.time.zone.DefaultZoneRulesProvider="com.zzx.MyZoneRulesProvider"

这样Java在启动的时候就会使用我们自己实现的ZoneRulesProvider了。
2.通过SPI机制引入

ZoneId的使用

1.获取系统默认时区:

ZoneId zoneId = ZoneId.systemDefault();

2.获取可用的时区Id:

Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();

部分结果如下:

Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8, Africa/Nairobi, America/Marigot, Asia/Aqtau, Pacific/Kwajalein, America/El_Salvador, Asia/Pontianak, Africa/Cairo, Pacific/Pago_Pago...国内一般用Asia/Shanghai。(为什么没有Asia/Beijing

3.初始化方法:

  • 使用ZoneRegion格式,获取中国地区时区,打印当地日期时间
ZoneId zoneId = ZoneId.of("Asia/Shanghai");  
Clock clock = Clock.system(zoneId);            
Instant instant = clock.instant();             
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zoneId); 
System.out.println(localDateTime.toString());  
  • 使用ZoneOffset格式,获取中国地区时区,打印当地日期时间
ZoneId zoneId = ZoneId.of("+8");
Clock clock1 = Clock.system(zoneId);
Instant instant = clock1.instant();
LocalDateTime localDateTime1 = LocalDateTime.ofInstant(instant, zoneId);
System.out.println(localDateTime1.toString());

tips:ZoneId支持的格式

格式 描述 示例
Z, GMT, UTC, UT 格林尼治标准时间,和中国相差8个小时 ZoneId.of("Z");
+h +hh +hh:mm -hh:mm +hhmm -hhmm +hh:mm:ss -hh:mm:ss +hhmmss -hhmmss 表示从格林尼治标准时间偏移时间,中国用+8表示 ZoneId.of("+8");
前缀:UTC+, UTC-, GMT+, GMT-, UT+ UT-。后缀:+h +hh +hh:mm -hh:mm... 表示从格林尼治标准时间偏移时间 ZoneId.of("UTC+8");
Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8, Africa/Nairobi, America/Marigot... 地区表示法,这些ID必须包涵在getAvailableZoneIds集合中,否则会抛出异常 ZoneId.of("Asia/Shanghai");

总结

最难理解的时区终于写完了,在学习ZoneId的过程中,发现了一些很有意思的Java代码,比如ZoneRulesProvider的SPI加载方式。写完这个系列后我们来讲讲他们。

zzx

There is my place for writing,coding and reading