timezone和offset类

timezone是地球上使用相同标准时间的区域。每个timezone都由一个标识符描述,通常采用格式* region/city *(Asia/Tokyo)和 Greenwich/ UTC 时间的offset量。例如,东京的offset量是+09:00

ZoneId 和 ZoneOffset

Date-Time API 提供了两个用于指定timezone或offset量的类:

  • ZoneId指定timezone标识符,并提供用于在InstantLocalDateTime之间进行转换的规则。

  • ZoneOffset指定与 Greenwich/ UTC 时间的timezoneoffset量。

Greenwich 时间/ UTC 时间的offset量通常以小时为单位进行定义,但也有 exception。以下来自TimeZoneId示例的代码将打印所有使用格林尼治/ UTC offset量的timezone的列表,这些offset量未在整个小时内定义。

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示例还将所有timezone ID 的列表打印到名为timeZones的文件中。

日期时间类

Date-Time API 提供了三个与timezone一起使用的基于时间的类:

  • ZonedDateTime处理具有相应timezone的日期和时间,其timezone偏离 Greenwich/ UTC。

  • OffsetDateTime处理的日期和时间具有与 Greenwich/ UTC 相对应的timezoneoffset,没有timezone ID。

  • OffsetTime处理时间与 Greenwich/ UTC 有相应timezoneoffset,但没有timezone ID。

什么时候使用OffsetDateTime而不是ZonedDateTime?如果您要编写复杂的软件,该软件根据地理位置为日期和时间计算建模自己的规则,或者要将时间戳存储在仅跟踪 Greenwich/ UTC 时间的绝对offset量的数据库中,则可能需要使用OffsetDateTime 。同样,XML 和其他网络格式将日期时间传输定义为OffsetDateTimeOffsetTime

尽管所有这三个类都与 Greenwich/ UTC 时间保持offset,但是只有ZonedDateTime使用java\.time\.zone包的一部分ZoneRules来确定特定timezone的offset量如何变化。例如,大多数timezone在将时钟向前移动到夏时制时会出现间隔(通常为 1 小时),而在将时钟向后移动到标准时间以及重复转换的最后一个小时时,会出现时间重叠。 ZonedDateTime类可以适应这种情况,而无法访问ZoneRulesOffsetDateTimeOffsetTime类则不能。

ZonedDateTime

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

以下来自Flight示例的代码将在美国/洛杉矶timezone中从旧金山到东京的航班的起飞时间定义为ZonedDateTimewithZoneSameInstantplusMinutes方法用于创建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类组合在一起。它用于表示完整的日期(年,月,日)和时间(小时,分钟,秒,纳秒),与 Greenwich/ UTC 时间(/ -hours:minutes,例如+06:00\-08:00)之间存在偏差。

以下示例使用OffsetDateTimeTemporalAdjuster\.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类将LocalTime类与ZoneOffset类组合在一起。它用于表示时间(小时,分钟,秒,纳秒),相对于 Greenwich 时间/ UTC 时间(/ -hours:minutes,例如+06:00\-08:00)有所offset。

在与OffsetDateTime类相同的情况下使用OffsetTime类,但是在跟踪日期时不需要。