1. 引言
在我们生活的世界中,每个国家都会遵循特定的时区(time-zone)。这些时区对于准确、方便地表达时间至关重要。然而,由于夏令时等因素的影响,时区有时会变得不那么明确。
在代码中表示这些时区时也容易产生混淆。Java 在过去提供了诸如 Date
、Time
和 DateTime
等类来处理时区问题。
随着 Java 版本的演进,引入了更加实用且表达力更强的新类,如 ZoneId
和 ZoneOffset
,用于更精细地管理时区信息。
本文将重点讨论 ZoneId
与 ZoneOffset
以及相关的日期时间类。
如果你对 Java 8 新引入的日期时间 API 还不太熟悉,可以先阅读我们之前的介绍文章:Java 8 日期时间 API 入门。
2. ZoneId
与 ZoneOffset
随着 JSR-310 的引入,Java 提供了一系列强大的 API 来处理日期、时间和时区。其中就包括 ZoneId
和 ZoneOffset
类。
2.1. ZoneId
正如前面提到的,**ZoneId
表示一个时区标识符**,例如 "Europe/Paris"
。
ZoneId
有两种实现方式:
- 一种是相对于 GMT/UTC 的固定偏移量;
- 另一种是地理区域,它有一套规则用于计算相对于 GMT/UTC 的偏移量。
举个例子,我们可以创建一个代表德国柏林的 ZoneId
:
ZoneId zone = ZoneId.of("Europe/Berlin");
2.2. ZoneOffset
ZoneOffset
继承自 ZoneId
,表示当前时区相对于 GMT/UTC 的固定偏移量,例如 +02:00
。
这意味着该数值表示固定的小时和分钟差值,用于描述当前时区与 GMT/UTC 时间之间的差异:
LocalDateTime now = LocalDateTime.now();
ZoneId zone = ZoneId.of("Europe/Berlin");
ZoneOffset zoneOffSet = zone.getRules().getOffset(now);
⚠️ 如果一个国家在夏季和冬季使用不同的偏移量,那么同一地区就会存在两个不同的 ZoneOffset
实现。因此需要传入一个 LocalDateTime
来确定具体使用的偏移量。
3. 使用 ZoneId
和 ZoneOffset
的日期时间类
下面我们将介绍一些实际利用了 ZoneId
和 ZoneOffset
的日期时间类。
3.1. ZonedDateTime
ZonedDateTime
是一个不可变的类,表示 ISO-8601 日历系统中带有时区信息的日期时间,例如:
2007-12-03T10:15:30+01:00 Europe/Paris
✅ ZonedDateTime
内部包含了三个核心组件:
- 一个
LocalDateTime
- 一个
ZoneId
- 以及解析后的
ZoneOffset
这个类能够存储纳秒级精度的所有日期和时间字段,并通过 ZoneOffset
处理本地时间的歧义问题。例如它可以存储如下值:
“2007年10月2日 13:45:30.123456789 +02:00 欧洲巴黎时区”
获取当前时区下的 ZonedDateTime
示例:
ZoneId zone = ZoneId.of("Europe/Berlin");
ZonedDateTime date = ZonedDateTime.now(zone);
此外,ZonedDateTime
还提供了便捷方法,用于将某个时间从一个时区转换到另一个时区:
ZonedDateTime destDate = sourceDate.withZoneSameInstant(destZoneId);
3.2. OffsetDateTime
OffsetDateTime
是一个不可变类,表示 ISO-8601 日历系统中带有偏移量的日期时间,例如:
2007-12-03T10:15:30+01:00
✅ 此类存储所有日期和时间字段(精度到纳秒),并包含相对于 GMT/UTC 的偏移量。
例如它可以存储如下值:
“2007年10月2日 13:45:30.123456789 +02:00”
创建一个比 GMT/UTC 快两个小时的 OffsetDateTime
:
ZoneOffset zoneOffSet = ZoneOffset.of("+02:00");
OffsetDateTime date = OffsetDateTime.now(zoneOffSet);
3.3. OffsetTime
OffsetTime
是一个不可变类,表示 ISO-8601 日历系统中带有时区偏移的时间,例如:
10:15:30+01:00
✅ 此类存储所有时间字段(精度到纳秒)以及对应的时区偏移量。
例如它可以存储如下值:
“13:45:30.123456789 +02:00”
创建一个比 GMT/UTC 快两个小时的 OffsetTime
:
ZoneOffset zoneOffSet = ZoneOffset.of("+02:00");
OffsetTime time = OffsetTime.now(zoneOffSet);
4. 总结
回到重点:**ZoneOffset
是一种基于 GMT/UTC 偏移量来表示时区的方式**。虽然还有其他方式表示时区,但这种方式简单直接,在很多场景下非常实用。
同时,ZoneId
和 ZoneOffset
不仅可以单独使用,也被广泛应用于 Java 的一些日期时间类中,如:
ZonedDateTime
OffsetDateTime
OffsetTime
一如既往,本文所涉及的代码示例均可在我们的 GitHub 仓库中找到: 👉 GitHub 项目地址