系列中的上一篇
当前教程
时区和偏移量
系列中的下一篇

系列中的上一篇: 日期和时间

系列中的下一篇: 瞬时

时区和偏移量

时区是地球上使用相同标准时间的区域。每个时区都由一个标识符描述,通常采用区域/城市Asia/Tokyo)的格式,并相对于格林威治/UTC 时间有一个偏移量。例如,东京的偏移量为+09:00

 

ZoneId 和 ZoneOffset 类

日期时间 API 提供了两个类来指定时区或偏移量

  • ZoneId 指定一个时区标识符,并提供在瞬时和 LocalDateTime 之间进行转换的规则。
  • ZoneOffset 指定相对于格林威治/UTC 时间的时区偏移量。

相对于格林威治/UTC 时间的偏移量通常以整小时定义,但也有例外。以下代码打印所有使用相对于格林威治/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<>(allZones).sort();

for (String zone : zoneList) {
    ZoneId zone = ZoneId.of(zone);
    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

 

日期时间类

日期时间 API 提供了三个基于时间的类,这些类与时区一起使用

  • ZonedDateTime 处理具有对应时区(相对于格林威治/UTC 时间的时区偏移量)的日期和时间。
  • OffsetDateTime 处理具有对应时区偏移量(相对于格林威治/UTC 时间)的日期和时间,但没有时区 ID。
  • OffsetTime 处理具有对应时区偏移量(相对于格林威治/UTC 时间)的时间,但没有时区 ID。

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

虽然所有三个类都维护相对于格林威治/UTC 时间的偏移量,但只有 ZonedDateTime 使用 ZoneRulesjava.time.zone 包的一部分)来确定特定时区的偏移量如何变化。例如,大多数时区在将时钟向前拨至夏令时时会经历一个间隙(通常为 1 小时),而在将时钟向后拨至标准时间时会经历一个时间重叠,并且转换前的最后一个小时会重复。该 ZonedDateTime 类适应这种情况,而 OffsetDateTimeOffsetTime 类无法访问 ZoneRules,因此无法做到这一点。

 

ZonedDateTime 类

ZonedDateTime 类实际上将 LocalDateTime 类与 ZoneId 类结合在一起。它用于表示完整的日期(年、月、日)和时间(时、分、秒、纳秒),以及时区(区域/城市,例如 Europe/Paris)。

以下代码将从旧金山飞往东京的航班的起飞时间定义为 ZonedDateTime,位于 America/Los Angeles 时区。该 withZoneSameInstant()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 类实际上将 LocalDateTime 类与 ZoneOffset 类结合在一起。它用于表示完整的日期(年、月、日)和时间(时、分、秒、纳秒),以及相对于格林威治/UTC 时间的偏移量(+/- 小时:分钟,例如 +06:00 或 -08:00)。

以下示例使用 OffsetDateTimeTemporalAdjusters.lastInMonth() 方法来查找 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 类实际上将 LocalTime 类与 ZoneOffset 类结合在一起。它用于表示时间(时、分、秒、纳秒),以及相对于格林威治/UTC 时间的偏移量(+/- 小时:分钟,例如 +06:00 或 -08:00)。

OffsetTime 类在与 OffsetDateTime 类相同的场景中使用,但不需要跟踪日期。


最后更新: 2022年1月27日


系列中的上一篇
当前教程
时区和偏移量
系列中的下一篇

系列中的上一篇: 日期和时间

系列中的下一篇: 瞬时