概要
タイムゾーン処理の基本は timezone-conversion で整理しましたが、実務では更に踏み込んだ判断が求められます。サマータイム(DST)のある地域と連携するとき、冬と夏でオフセットが変わる影響をどう吸収するか。DB に保存する時刻を UTC に統一する具体的なパターン。ZonedDateTime と OffsetDateTime をどう使い分けるか。この記事では、これらの実務的なテーマを掘り下げ、DB保存→表示のラウンドトリップや、DST 切り替え時のエッジケースを含めたコード例を紹介します。
使いどころ
海外拠点のシステムとデータ連携する際に、DST の影響でログの時刻がずれる問題を解消する
DB にはすべて UTC で保存し、画面表示時にユーザーのタイムゾーンで変換して表示する
外部 API のレスポンスに含まれる ISO 8601 タイムスタンプを JST に変換して帳票に出力する
コード例
import java.time.LocalDateTime;
public class TimezoneAdvanced {
private static final ZoneId JST = ZoneId.of("Asia/Tokyo");
private static final ZoneId UTC = ZoneId.of("UTC");
public static String toDbValue(ZonedDateTime jstTime) {
return jstTime.withZoneSameInstant(UTC)
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
public static String toDisplay(String dbValue) {
return ZonedDateTime.parse(dbValue)
.withZoneSameInstant(JST)
.format(DateTimeFormatter.ofPattern(
"yyyy/MM/dd HH:mm"));
}
public static void main(String[] args) {
var now = ZonedDateTime.now(JST);
var saved = toDbValue(now);
var displayed = toDisplay(saved);
System.out.println("保存値(UTC): " + saved);
System.out.println("表示(JST): " + displayed);
var nyWinter = ZonedDateTime.of(
LocalDateTime.of(2025, 1, 15, 12, 0),
ZoneId.of("America/New_York"));
var nySummer = ZonedDateTime.of(
LocalDateTime.of(2025, 7, 15, 12, 0),
ZoneId.of("America/New_York"));
System.out.println("NY冬: " + nyWinter.getOffset());
System.out.println("NY夏: " + nySummer.getOffset());
var offset = OffsetDateTime.now(ZoneOffset.of("+09:00"));
System.out.println("OffsetDateTime: " + offset);
}
}Version Coverage
API に大きな変化はない。var 宣言とテキストブロックで記述が若干簡潔になる程度。
// Java 17: var で簡潔に記述
var jst = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
var utc = jst.withZoneSameInstant(ZoneId.of("UTC"));
var dbValue = utc.format(
DateTimeFormatter.ISO_OFFSET_DATE_TIME);Library Comparison
注意点
ZonedDateTime は DST ルールを持つため、将来の予定を扱うのに適している。OffsetDateTime は固定オフセットなので DB 保存や API 連携向き
サマータイム切り替え時に「存在しない時刻」(2:00〜3:00 がスキップ)や「重複する時刻」が生じる。ZonedDateTime はこれを自動調整するが、意図通りか確認が必要
DB には UTC で保存するのが鉄則。ローカルタイムで保存すると、DST やタイムゾーン変更時にデータが破綻する
ZoneId.of() に不正な文字列を渡すと DateTimeException が発生する。外部入力の場合はバリデーションが必要
FAQ
DB には OffsetDateTime(UTC 固定)で保存するのが安全です。ZonedDateTime のタイムゾーンルールは DB に保存しにくいためです。
ZonedDateTime.of() は自動的に「先に来る方」のオフセットを選びます。明示的に制御するには withEarlierOffsetAtOverlap / withLaterOffsetAtOverlap を使います。
JDK のマイナーアップデートに含まれます。TZUpdater ツールで個別に更新することも可能です。