概要
海外拠点との連携やクラウドサービスとの通信で、タイムゾーンの扱いは避けて通れません。Java 8 以降の ZonedDateTime と ZoneId を使えば、JST / UTC / EST などの時差変換を安全に行えます。ただし、サマータイムの切り替わりタイミングや、DB への保存形式の選び方を間違えると、1 時間のズレや日付の食い違いが本番で発生します。この記事では、タイムゾーン変換の基本操作から、サマータイムが絡むエッジケースの扱い、UTC で統一して保存する設計パターンまでを整理します。
使いどころ
海外拠点のシステムとデータ連携する際に UTC で統一して保存する
ユーザーのタイムゾーンに合わせて表示時刻を変換する
ログのタイムスタンプを複数のタイムゾーンで比較する
コード例
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class TimezoneCompare {
public static void main(String[] args) {
var now = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
var zones = java.util.List.of(
"Asia/Tokyo", "UTC", "America/New_York",
"Europe/London", "Asia/Shanghai"
);
var fmt = DateTimeFormatter.ofPattern(
"yyyy-MM-dd HH:mm:ss z");
for (var zone : zones) {
var converted = now.withZoneSameInstant(
ZoneId.of(zone));
System.out.printf("%-20s %s%n",
zone, converted.format(fmt));
}
}
}Version Coverage
基本的な使い方は Java 8 と同じ。switch 式で出力フォーマットの切り替えが簡潔に書ける。
// Java 17: switch 式で税率切り替えと同様の書き方
var now = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
// DB保存は UTC、表示は JST パターン
String dbValue = now.withZoneSameInstant(ZoneId.of("UTC"))
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
// 取得時に表示用へ変換
var fromDb = ZonedDateTime.parse(dbValue);
String display = fromDb
.withZoneSameInstant(ZoneId.of("Asia/Tokyo"))
.format(DateTimeFormatter
.ofPattern("yyyy/MM/dd HH:mm"));Library Comparison
注意点
DB には UTC で保存し、表示時にユーザーのタイムゾーンに変換するのが安全。
サマータイムの切り替え時に時刻が重複・欠落する場合がある。
ZoneId.of() に不正な文字列を渡すと DateTimeException が発生する。
FAQ
ZoneId.getAvailableZoneIds() で全タイムゾーンIDを取得できます。600以上のIDが返るため、実用上はリージョン形式(Asia/Tokyo等)に絞ると扱いやすくなります。
内部的には UTC で処理し、表示時のみローカルタイムに変換するのが安全です。UTC基準なら時刻の重複や欠落が発生しないためです。
ZonedDateTime はタイムゾーンルール(サマータイム等)を含み、OffsetDateTime は固定オフセットのみです。