Java Tips | 日付・時間:ZonedDateTime生成

Java Java
スポンサーリンク

ZonedDateTime は「タイムゾーン付きの日時」

ZonedDateTime は、
「日付(年月日)+時刻(時分秒)+タイムゾーン」 をまとめて扱うクラスです。

LocalDateTime は「どこの国の 10:00 か」が分かりませんが、
ZonedDateTime は「日本時間の 10:00」「UTC の 10:00」のように、
“世界のどの瞬間か”まで表現できます。

業務では、
ログの時刻を UTC で保存したいとき、
海外システムと日時をやり取りするとき、
サマータイムを正しく扱いたいとき、
などで本気で頼りになるクラスです。


現在の ZonedDateTime を生成する:ZonedDateTime.now()

「今この瞬間を、タイムゾーン付きで表現する」

一番基本は ZonedDateTime.now() です。

import java.time.ZonedDateTime;

public class NowExample {

    public static void main(String[] args) {
        ZonedDateTime now = ZonedDateTime.now();
        System.out.println(now); // 例: 2025-03-26T22:15:30.123+09:00[Asia/Tokyo]
    }
}
Java

ここで出てくる情報は三つです。

年月日+時刻:2025-03-26T22:15:30.123
UTC からのオフセット:+09:00
タイムゾーンID:[Asia/Tokyo]

重要なのは、
「この1つの値だけで、“世界のどの瞬間か”が一意に決まる」 ということです。

LocalDateTime だと「2025-03-26T22:15:30」が
日本なのかロンドンなのか分かりませんが、
ZonedDateTime なら「Asia/Tokyo の 22:15:30」とはっきり分かります。


特定のタイムゾーンの「今」を生成する:ZonedDateTime.now(ZoneId)

「日本時間の今」「UTC の今」を明示的に作る

デフォルトタイムゾーンに頼らず、
「どのタイムゾーン基準か」を明示したいことが多いです。

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class NowWithZoneExample {

    public static void main(String[] args) {
        ZoneId tokyo = ZoneId.of("Asia/Tokyo");
        ZoneId utc   = ZoneId.of("UTC");

        ZonedDateTime nowTokyo = ZonedDateTime.now(tokyo);
        ZonedDateTime nowUtc   = ZonedDateTime.now(utc);

        System.out.println("Tokyo: " + nowTokyo);
        System.out.println("UTC  : " + nowUtc);
    }
}
Java

ここでの重要ポイントは二つです。

一つ目は、「ZoneId.of("Asia/Tokyo") のように、タイムゾーンを“名前”で指定する」ことです。
"Asia/Tokyo", "Europe/London", "America/New_York" など、IANA タイムゾーンIDを使います。

二つ目は、「同じ“瞬間”でも、タイムゾーンによって表示される日時が変わる」ことです。
UTC で 13:00 なら、日本時間では 22:00(+09:00)になります。

「ログは UTC、画面表示は日本時間」のような要件では、
ZonedDateTimeZoneId の組み合わせが必須になります。


年月日・時刻・タイムゾーンを指定して生成する:of(…, ZoneId)

「2025-04-01 09:00(日本時間)」のような意味のある日時を作る

ビジネス上意味のある日時を、
「どのタイムゾーンか」まで含めてコードに埋め込みたいときは of を使います。

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class OfExample {

    public static void main(String[] args) {
        ZoneId tokyo = ZoneId.of("Asia/Tokyo");

        ZonedDateTime opening = ZonedDateTime.of(
                2025, 4, 1,
                9, 0, 0, 0,
                tokyo
        );

        System.out.println(opening); // 2025-04-01T09:00+09:00[Asia/Tokyo]
    }
}
Java

ここで深掘りしたいポイントは三つです。

一つ目は、「年月日・時分秒・ナノ秒・ZoneId を一気に指定できる」ことです。
LocalDateTime.of(...)ZoneId を足したイメージです。

二つ目は、「“日本時間の 9:00”という意味がはっきりする」ことです。
海外システムと連携するときに、「どのタイムゾーンの 9:00 か」が曖昧だと事故の元になります。

三つ目は、「存在しない日時を指定すると例外になる」ことです。
サマータイムの切り替えなどで「そのタイムゾーンには存在しない時刻」があり得ます。
ZonedDateTime はそのあたりもきちんとチェックしてくれます。


LocalDateTime + ZoneId から ZonedDateTime を作る

「ローカルな日時」に「どのタイムゾーンか」を後から付ける

すでに LocalDateTime があって、
「これは日本時間として扱いたい」というケースもよくあります。

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class FromLocalDateTimeExample {

    public static void main(String[] args) {
        LocalDateTime local = LocalDateTime.of(2025, 4, 1, 9, 0);
        ZoneId tokyo = ZoneId.of("Asia/Tokyo");

        ZonedDateTime zoned = local.atZone(tokyo);
        System.out.println(zoned); // 2025-04-01T09:00+09:00[Asia/Tokyo]
    }
}
Java

ここでの重要ポイントは、
「LocalDateTime は“どこの 9:00 か分からない”、ZonedDateTime は“どこの 9:00 かまで分かる”」
という役割の違いです。

業務ロジックの中では LocalDateTime で十分なことも多いですが、
外部システムとのやり取りや保存形式としては、
ZonedDateTime(あるいは Instant+ZoneId)まで落とし込んでおくと安全です。


文字列から ZonedDateTime を生成する:parse(…)

ISO 形式の文字列をそのまま parse する

"2025-03-26T22:15:30+09:00[Asia/Tokyo]" のような文字列を
そのまま ZonedDateTime に変換できます。

import java.time.ZonedDateTime;

public class ParseExample {

    public static void main(String[] args) {
        ZonedDateTime zdt = ZonedDateTime.parse("2025-03-26T22:15:30+09:00[Asia/Tokyo]");
        System.out.println(zdt);
    }
}
Java

この形式は少し長いですが、
「日時+オフセット+タイムゾーンID」が全部入っているので、
“世界のどの瞬間か”を完全に再現できます。

独自フォーマットを parse する(オフセットまで、など)

例えば、
"2025-03-26T22:15:30+09:00" のように
「オフセットまでだがゾーンIDはない」形式を扱いたい場合は、
OffsetDateTimeDateTimeFormatter と組み合わせて扱うことが多いです。

ZonedDateTime にこだわるより、
「どこまで情報が必要か」でクラスを選ぶ、という感覚も大事です。


タイムゾーン変換:withZoneSameInstant(…)

「日本時間の 9:00 を、UTC では何時か」に変換する

ZonedDateTime の真骨頂は、「タイムゾーンを変えても“同じ瞬間”を保てる」ことです。

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class ConvertZoneExample {

    public static void main(String[] args) {
        ZoneId tokyo = ZoneId.of("Asia/Tokyo");
        ZoneId utc   = ZoneId.of("UTC");

        ZonedDateTime tokyoTime = ZonedDateTime.of(
                2025, 3, 26,
                9, 0, 0, 0,
                tokyo
        );

        ZonedDateTime utcTime = tokyoTime.withZoneSameInstant(utc);

        System.out.println("Tokyo: " + tokyoTime); // 2025-03-26T09:00+09:00[Asia/Tokyo]
        System.out.println("UTC  : " + utcTime);   // 2025-03-26T00:00Z[UTC]
    }
}
Java

ここでの重要ポイントは二つです。

一つ目は、「withZoneSameInstant は“同じ瞬間”を別のタイムゾーンで表現し直す」ことです。
日本時間の 9:00 は、UTC では 0:00 になります。

二つ目は、「“同じローカル時刻”にしたいときは別メソッドが必要」ということです。
withZoneSameLocal というメソッドもありますが、
業務ではたいてい「同じ瞬間」を保ちたいので、
まずは withZoneSameInstant を覚えておくとよいです。


まとめ:ZonedDateTime生成で押さえておきたい感覚

ZonedDateTime は、
「世界のどの瞬間か」を正確に扱うためのクラス です。

now() で「今この瞬間」をタイムゾーン付きで取れる。
of(..., ZoneId)LocalDateTime.atZone(...) で、「どのタイムゾーンの日時か」を明示できる。
parse で文字列から復元できる。
withZoneSameInstant で、同じ瞬間を別のタイムゾーンに変換できる。

あなたのコードのどこかに、
「Date をそのまま使っていて、UTC なのか日本時間なのかよく分からない」
「文字列で日時を持っていて、タイムゾーンの話になると毎回混乱する」
そんな箇所があれば、一度 ZonedDateTimeZoneId を軸に設計し直せないか眺めてみてください。

それが、「時間とタイムゾーンを味方につけて設計できるエンジニア」への、
かなり大きな一歩になります。

タイトルとURLをコピーしました