Java : ZoneIdとZoneOffsetの違い

タイムゾーンを扱うクラスにはZoneIdとZoneOffsetがあります。
なぜ2つもあるのだろう…どちらかひとつだけでよいのでは? と疑問に思ったかたもいるかもしれません。

本記事では、そんなZoneIdとZoneOffsetの違いをざっくりと解説していきます。


クラス関係

クラス構成

クラス 概要
ZoneId 地理的な地域ごとのタイムゾーンを定義します。
例えば日本標準時は"Asia/Tokyo"として定義されています。時差(オフセット)は+9時間です。
ZoneOffset UTCからの単純な時差(オフセット)を定義します。

UTC(協定世界時)とは

  • 世界で基準となる時刻です。
    世界各地の標準時はUTCを基準としています。例えば日本標準時(JST)はUTCより9時間進めた時間です。

ZoneId

ZoneIdは地域と時差のルールを定義します。

日本標準時では、UTCとの時差は(季節によらずいつでも)+9時間です。これはご存じのかたも多いかもしれません。

final var zoneId = ZoneId.of("Asia/Tokyo");
System.out.println(zoneId); // Asia/Tokyo

final var zonedDateTime = ZonedDateTime.of(2020, 1, 1, 0, 0, 0, 0, zoneId);
System.out.println(zonedDateTime); // 2020-01-01T00:00+09:00[Asia/Tokyo]

日本ではあまりなじみがないかもしれませんが、夏時間・冬時間がある地域もあります。(サマータイムとも呼ばれています)
例えばヨーロッパのパリでは、

  • 夏時間は時差が+2時間
  • 冬時間は時差が+1時間

と、季節によって時差が変わります。

final var zoneId = ZoneId.of("Europe/Paris");
System.out.println(zoneId);

final var zonedDateTime = ZonedDateTime.of(2020, 10, 25, 3, 30, 0, 0, zoneId);
System.out.println(zonedDateTime); // 2020-10-25T03:30+01:00[Europe/Paris]

// 時間によって時差(オフセット)が変わります。
System.out.println(zonedDateTime.minusHours(1)); // 2020-10-25T02:30+01:00[Europe/Paris]
System.out.println(zonedDateTime.minusHours(2)); // 2020-10-25T02:30+02:00[Europe/Paris]
System.out.println(zonedDateTime.minusHours(3)); // 2020-10-25T01:30+02:00[Europe/Paris]
  • 03:30から-1時間すると、02:30(時差+01:00)
  • 03:30から-2時間すると、02:30(時差 +02:00 )
  • 03:30から-3時間すると、01:30(時差+02:00)

となります。
夏時間に慣れていないと、ちょっと奇妙に感じるかもしれません。

このように、ZoneIdでは各地域がどの季節にどれだけ時差が出るかというルールが定義されています。

ZoneOffset

ZoneOffsetは、単純なUTCからの時差を定義します。
夏時間や冬時間といった概念はありません。

日本標準時のZoneOffsetは+09:00です。
以下は、あえてZoneIdではなくZoneOffsetを使った例です。

final var zoneOffset = ZoneOffset.ofHours(9);
System.out.println(zoneOffset); // +09:00

final var offsetDateTime = OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 0, zoneOffset);
System.out.println(offsetDateTime); // 2020-01-01T00:00+09:00

UTC

よく使うZoneOffsetとして ZoneOffset.UTC があります。

System.out.println(ZoneOffset.UTC); // Z

final var offsetDateTime = OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC);
System.out.println(offsetDateTime); // 2020-01-01T00:00Z

UTC(協定世界時)はZoneOffsetに定数として定義されています。
UTCは夏時間・冬時間といったルールはなく、固定で0時間の時差です。

よって、ZoneIdではなくZoneOffsetに定義されているわけですね。

TIPS

ZoneId.systemDefault()

システムのデフォルトのタイムゾーンを取得します。

final var zoneId = ZoneId.systemDefault();
System.out.println(zoneId); // Asia/Tokyo

一般的な日本語環境であれば、デフォルトとして"Asia/Tokyo"が取得されます。

実はZoneOffsetはZoneIdのサブクラスなので、ZoneOffset.systemDefault()とも書けます。
しかし、戻り値はあくまでZoneIdなので、ZoneId.systemDefault()と書く方が可読性も高くおすすめです。

final ZoneId zoneId1 = ZoneId.systemDefault(); // おすすめ
final ZoneId zoneId2 = ZoneOffset.systemDefault(); // 間違いではないけどあまりおすすめはしません

まとめ

クラス 時差の定義 夏時間・冬時間といった季節ごとの時差のルール
ZoneId
ZoneOffset ×

大きな違いは上の表のようになります。

基本的にはZoneIdを使うことをおすすめします。
ややこしい夏時間・冬時間をちゃんと考慮してくれるためです。

ZoneOffsetを使うのはUTCくらいなのかな、と思います。


関連記事

ページの先頭へ