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 くらいなのかな、と思います。
関連記事
- 日付・時刻の基本
- Date, CalendarではなくLocalDateTime, ZonedDateTimeを使おう
- 文字列と日付・時刻の変換
- 日付と時刻、曜日の計算
- 現在時刻(日時)の取得いろいろ
- 現在の曜日(DayOfWeek)を取得
- API 使用例
- Calendar (カレンダー)
- ChronoLocalDate
- ChronoLocalDateTime
- ChronoZonedDateTime
- Clock (時計)
- Date (日付・時刻)
- DateTimeException (日付・時刻の例外)
- DateTimeParseException (日付・時刻の解析例外)
- DayOfWeek (曜日)
- Duration (時間の量)
- Era (紀元)
- Instant (時点)
- InstantSource
- JapaneseDate (和暦を使った日付)
- LocalDate (日付・タイムゾーンなし)
- LocalDateTime (日時・タイムゾーンなし)
- LocalTime (時刻・タイムゾーンなし)
- Month (月)
- MonthDay (月・日)
- OffsetDateTime (日時・オフセットあり)
- OffsetTime (時刻・オフセットあり)
- Period (日付の量)
- Temporal
- TemporalAccessor
- TemporalAdjuster (日付・時刻の調整)
- TemporalAdjusters (日付・時刻の調整ユーティリティ)
- TimeZone (タイムゾーン)
- Year (年)
- YearMonth (年・月)
- ZonedDateTime (日時・タイムゾーンあり)
- ZoneId (タイムゾーンID)
- ZoneOffset (タイムゾーン・オフセット)