# 时区和偏移类

> 原文： [https://docs.oracle.com/javase/tutorial/datetime/iso/timezones.html](https://docs.oracle.com/javase/tutorial/datetime/iso/timezones.html)

_ 时区 _ 是使用相同标准时间的地球区域。每个时区由标识符描述，并且通常具有格式 _ 区域/城市 _（`亚洲/东京`）和格林威治/ UTC 时间的偏移。例如，东京的偏移是 `+09：00` 。

## ZoneId 和 ZoneOffset

Date-Time API 提供了两个用于指定时区或偏移的类：

*   `ZoneId` 指定时区标识符，并提供在 `Instant` 和 `LocalDateTime` 之间进行转换的规则。
*   `ZoneOffset` 指定从格林威治/ UTC 时间偏移的时区。

格林威治/ UTC 时间的偏差通常以整个小时定义，但也有例外。以下代码来自 [``TimeZoneId` `](examples/TimeZoneId.java)示例，打印使用格林威治/ UTC 偏移的所有时区列表，这些时区未在整个小时内定义。

```
Set<String> allZones = ZoneId.getAvailableZoneIds();
LocalDateTime dt = LocalDateTime.now();

// Create a List using the set of zones and sort it.
List<String> zoneList = new ArrayList<String>(allZones);
Collections.sort(zoneList);

...

for (String s : zoneList) {
    ZoneId zone = ZoneId.of(s);
    ZonedDateTime zdt = dt.atZone(zone);
    ZoneOffset offset = zdt.getOffset();
    int secondsOfHour = offset.getTotalSeconds() % (60 * 60);
    String out = String.format("%35s %10s%n", zone, offset);

    // Write only time zones that do not have a whole hour offset
    // to standard out.
    if (secondsOfHour != 0) {
        System.out.printf(out);
    }
    ...
}

```

此示例将以下列表打印到标准输出：

```
      America/Caracas     -04:30
     America/St_Johns     -02:30
        Asia/Calcutta     +05:30
         Asia/Colombo     +05:30
           Asia/Kabul     +04:30
       Asia/Kathmandu     +05:45
        Asia/Katmandu     +05:45
         Asia/Kolkata     +05:30
         Asia/Rangoon     +06:30
          Asia/Tehran     +04:30
   Australia/Adelaide     +09:30
Australia/Broken_Hill     +09:30
     Australia/Darwin     +09:30
      Australia/Eucla     +08:45
        Australia/LHI     +10:30
  Australia/Lord_Howe     +10:30
      Australia/North     +09:30
      Australia/South     +09:30
 Australia/Yancowinna     +09:30
  Canada/Newfoundland     -02:30
         Indian/Cocos     +06:30
                 Iran     +04:30
              NZ-CHAT     +12:45
      Pacific/Chatham     +12:45
    Pacific/Marquesas     -09:30
      Pacific/Norfolk     +11:30

```

`TimeZoneId` 示例还会将所有时区 ID 的列表打印到名为 [``timeZones` `](examples/timeZones)的文件中。

## 日期时间类

Date-Time API 提供了三个适用于时区的基于时间的类：

*   `ZonedDateTime` 使用相应时区处理日期和时间，时区偏离格林威治/ UTC。
*   `OffsetDateTime` 使用与格林威治/ UTC 相对应的时区偏移处理日期和时间，没有时区 ID。
*   `OffsetTime` 处理与格林威治/ UTC 相对应的时区偏移的时间，没有时区 ID。

你何时会使用 `OffsetDateTime` 而不是 `ZonedDateTime` ？如果您正在编写复杂的软件，根据地理位置为自己的日期和时间计算规则建模，或者如果您将时间戳存储在仅跟踪格林威治/ UTC 时间的绝对偏移的数据库中，那么您可能希望使用 `OffsetDateTime` 。此外，XML 和其他网络格式将日期时间传输定义为 `OffsetDateTime` 或 `OffsetTime` 。

虽然所有三个类都保持与格林威治/ UTC 时间的偏移，但只有 `ZonedDateTime` 使用 [`ZoneRules`](https://docs.oracle.com/javase/8/docs/api/java/time/zone/ZoneRules.html)， `java.time.zone` 的一部分包，用于确定特定时区的偏移量如何变化。例如，当将时钟向前移动到夏令时时，大多数时区经历间隙（通常为 1 小时），并且当将时钟移回标准时间和重复转换之前的最后一小时时，时间重叠。 `ZonedDateTime` 类适应这种情况，而无法访问 `ZoneRules` 的 `OffsetDateTime` 和 `OffsetTime` 类则不适用。

### ZonedDateTime

[ZonedDateTime](https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html) 类实际上将 [`LocalDateTime`](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html)类与 [`ZoneId`](https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html)类组合在一起。它用于表示时区（区域/城市，例如 `Europe / Paris` ）的完整日期（年，月，日）和时间（小时，分钟，秒，纳秒）。

来自 [``Flight` `](examples/Flight.java)示例的以下代码定义了从旧金山飞往东京的航班起飞时间 `ZonedDateTime` 美国/洛杉矶时区。带有 ZoneSameInstant 和 `plusMinutes` 方法的`用于创建 `ZonedDateTime` 的实例，该实例表示 650 分钟飞行后在东京的预计到达时间。 `ZoneRules.isDaylightSavings` 方法确定航班抵达东京时是否为夏令时。`

`DateTimeFormatter` 对象用于格式化 `ZonedDateTime` 实例以进行打印：

```
DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM d yyyy  hh:mm a");

// Leaving from San Francisco on July 20, 2013, at 7:30 p.m.
LocalDateTime leaving = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
ZoneId leavingZone = ZoneId.of("America/Los_Angeles"); 
ZonedDateTime departure = ZonedDateTime.of(leaving, leavingZone);

try {
    String out1 = departure.format(format);
    System.out.printf("LEAVING:  %s (%s)%n", out1, leavingZone);
} catch (DateTimeException exc) {
    System.out.printf("%s can't be formatted!%n", departure);
    throw exc;
}

// Flight is 10 hours and 50 minutes, or 650 minutes
ZoneId arrivingZone = ZoneId.of("Asia/Tokyo"); 
ZonedDateTime arrival = departure.withZoneSameInstant(arrivingZone)
                                 .plusMinutes(650);

try {
    String out2 = arrival.format(format);
    System.out.printf("ARRIVING: %s (%s)%n", out2, arrivingZone);
} catch (DateTimeException exc) {
    System.out.printf("%s can't be formatted!%n", arrival);
    throw exc;
}

if (arrivingZone.getRules().isDaylightSavings(arrival.toInstant())) 
    System.out.printf("  (%s daylight saving time will be in effect.)%n",
                      arrivingZone);
else
    System.out.printf("  (%s standard time will be in effect.)%n",
                      arrivingZone);

```

这会产生以下输出：

```
LEAVING:  Jul 20 2013  07:30 PM (America/Los_Angeles)
ARRIVING: Jul 21 2013  10:20 PM (Asia/Tokyo)
  (Asia/Tokyo standard time will be in effect.)

```

### OffsetDateTime

[OffsetDateTime](https://docs.oracle.com/javase/8/docs/api/java/time/OffsetDateTime.html) 类实际上将 [`LocalDateTime`](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html)类与 [`ZoneOffset`](https://docs.oracle.com/javase/8/docs/api/java/time/ZoneOffset.html)类组合在一起。它用于表示完整日期（年，月，日）和时间（小时，分钟，秒，纳秒），偏离格林威治/ UTC 时间（+/-小时：分钟，例如 `+06： 00` 或 `-08：00` ）。

以下示例使用 `OffsetDateTime` 和 `TemporalAdjuster.lastDay` 方法查找 2013 年 7 月的最后一个星期四。

```
// Find the last Thursday in July 2013.
LocalDateTime localDate = LocalDateTime.of(2013, Month.JULY, 20, 19, 30);
ZoneOffset offset = ZoneOffset.of("-08:00");

OffsetDateTime offsetDate = OffsetDateTime.of(localDate, offset);
OffsetDateTime lastThursday =
        offsetDate.with(TemporalAdjusters.lastInMonth(DayOfWeek.THURSDAY));
System.out.printf("The last Thursday in July 2013 is the %sth.%n",
                   lastThursday.getDayOfMonth());

```

运行此代码的输出是：

```
The last Thursday in July 2013 is the 25th.

```

### OffsetTime

[OffsetTime](https://docs.oracle.com/javase/8/docs/api/java/time/OffsetTime.html) 类实际上将 [`LocalTime`](https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html)类与 [`ZoneOffset`](https://docs.oracle.com/javase/8/docs/api/java/time/ZoneOffset.html)类组合在一起。它用于表示时间（小时，分钟，秒，纳秒），偏离格林威治/ UTC 时间（+/-小时：分钟，例如 `+06：00` 或 `-08： 00` ）。

`OffsetTime` 类用于与 `OffsetDateTime` 类相同的情况，但不需要跟踪日期。