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、画面表示は日本時間」のような要件では、ZonedDateTime と ZoneId の組み合わせが必須になります。
年月日・時刻・タイムゾーンを指定して生成する: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はない」形式を扱いたい場合は、OffsetDateTime や DateTimeFormatter と組み合わせて扱うことが多いです。
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 なのか日本時間なのかよく分からない」
「文字列で日時を持っていて、タイムゾーンの話になると毎回混乱する」
そんな箇所があれば、一度 ZonedDateTime と ZoneId を軸に設計し直せないか眺めてみてください。
それが、「時間とタイムゾーンを味方につけて設計できるエンジニア」への、
かなり大きな一歩になります。
