Java 8之新日期时间API笔记(四)ZoneId
前言
在现实生活中我们互相交流,默认使用的都是本地日期时间(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。他的子类我们一般不直接使用。
如下图:
ZoneOffset
ZoneOffset表示从UTC/Greenwich标准时间的偏移量,eg:+02:00。另外不要使用==比较ZoneOffset,因为内部实现是有缓存的,所以可能是对同一个对象进行比较,比较可以使用equal方法。
ZoneRegion
ZoneRegion是地理时区的概念。ZoneRegion的规则由内部的ZoneRule制定。ZoneRule来源于ZoneRulesProvider。三者的关系如下图:
地理时区不是一成不变的。权威的信息来源是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加载方式。写完这个系列后我们来讲讲他们。