古い Date と新しい java.time を「つなぐ」という発想
java.util.Date は、昔からある「日時クラス」です。
でも今の Java では、LocalDate / LocalTime / LocalDateTime / ZonedDateTime / Instant など、java.time パッケージのクラスを使うのが基本になっています。
とはいえ、業務ではまだまだ Date を要求するライブラリや、Date 型のカラムを持つ既存システムがたくさんあります。
だから大事なのは、
「Date を主役にする」のではなく、
「内部では java.time を使い、必要なところだけ Date と相互変換する」
という発想です。
ここでは、そのための「Date変換」のパターンを整理していきます。
Date と Instant の変換が“ハブ”になる
Date → Instant:toInstant()
Date は内部的に「epoch milli(1970-01-01T00:00:00Z からのミリ秒)」を持っています。Instant も同じ基準を使うので、ここはほぼ 1 対 1 で変換できます。
import java.time.Instant;
import java.util.Date;
public class DateToInstantExample {
public static void main(String[] args) {
Date date = new Date(); // 現在日時
Instant instant = date.toInstant();
System.out.println(date);
System.out.println(instant); // 例: 2025-03-26T13:25:30.123Z
}
}
Javaここでの重要ポイントは、
「Date → Instant にしてしまえば、あとは java.time の世界で自由に扱える」
ということです。
Instant から LocalDateTime や ZonedDateTime への変換は、すでに整った API が用意されています。
Instant → Date:Date.from(instant)
逆方向もシンプルです。
import java.time.Instant;
import java.util.Date;
public class InstantToDateExample {
public static void main(String[] args) {
Instant instant = Instant.now();
Date date = Date.from(instant);
System.out.println(instant);
System.out.println(date);
}
}
Java既存ライブラリのメソッドが Date を要求してくるとき、
内部では Instant や LocalDateTime で扱い、
最後に Date.from(...) で渡す、という形にするとコードがすっきりします。
Date ↔ LocalDateTime / ZonedDateTime 変換
Date → LocalDateTime(タイムゾーンが必須)
LocalDateTime はタイムゾーンを持たないので、
「Date をどのタイムゾーンとして解釈するか」を決める必要があります。
import java.time.*;
import java.util.Date;
public class DateToLocalDateTimeExample {
public static void main(String[] args) {
Date date = new Date(); // 例: DB から取ってきた Date
Instant instant = date.toInstant();
ZoneId zone = ZoneId.of("Asia/Tokyo");
LocalDateTime ldt = LocalDateTime.ofInstant(instant, zone);
System.out.println("Date : " + date);
System.out.println("LocalDateTime : " + ldt);
}
}
Javaここで深掘りしたいのは、
「Date 単体には“どのタイムゾーンか”という情報はない」
という点です。
「日本時間として扱うのか」「UTC として扱うのか」を、
変換する側が決めてあげる必要があります。
LocalDateTime → Date(やはりタイムゾーンが必要)
逆に、LocalDateTime から Date に変換するときも、
「どのタイムゾーンの LocalDateTime か」を決めないといけません。
import java.time.*;
import java.util.Date;
public class LocalDateTimeToDateExample {
public static void main(String[] args) {
LocalDateTime ldt = LocalDateTime.of(2025, 3, 26, 9, 0);
ZoneId zone = ZoneId.of("Asia/Tokyo");
Instant instant = ldt.atZone(zone).toInstant();
Date date = Date.from(instant);
System.out.println("LocalDateTime : " + ldt);
System.out.println("Date : " + date);
}
}
Javaここでの重要ポイントは、LocalDateTime → ZonedDateTime(atZone)→ Instant → Date
という流れを一度覚えてしまうことです。
「ローカルな日時」を「世界共通の瞬間」に投影してから、Date に落とし込むイメージです。
Date ↔ ZonedDateTime(タイムゾーン付きで扱う)
タイムゾーンをきちんと意識したいなら、ZonedDateTime を経由するのが素直です。
import java.time.*;
import java.util.Date;
public class DateToZonedDateTimeExample {
public static void main(String[] args) {
Date date = new Date();
Instant instant = date.toInstant();
ZoneId tokyo = ZoneId.of("Asia/Tokyo");
ZonedDateTime zdt = instant.atZone(tokyo);
System.out.println("Date : " + date);
System.out.println("ZonedDateTime: " + zdt);
}
}
Java逆方向も同じパターンです。
import java.time.*;
import java.util.Date;
public class ZonedDateTimeToDateExample {
public static void main(String[] args) {
ZonedDateTime zdt = ZonedDateTime.of(
2025, 3, 26,
9, 0, 0, 0,
ZoneId.of("Asia/Tokyo")
);
Instant instant = zdt.toInstant();
Date date = Date.from(instant);
System.out.println("ZonedDateTime: " + zdt);
System.out.println("Date : " + date);
}
}
Java「外部とのやり取りは Date だが、内部ではタイムゾーン付きで厳密に扱いたい」
というときに、このパターンが効いてきます。
Date ↔ LocalDate / LocalTime 変換
Date → LocalDate(“日付だけ”に落とす)
Date から「日付だけ」を取り出したいときは、
一度 Instant と ZoneId を経由して LocalDate にします。
import java.time.*;
import java.util.Date;
public class DateToLocalDateExample {
public static void main(String[] args) {
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.of("Asia/Tokyo");
LocalDate localDate = instant.atZone(zone).toLocalDate();
System.out.println("Date : " + date);
System.out.println("LocalDate: " + localDate);
}
}
Javaここでのポイントは、
「どのタイムゾーンで日付を切るか」によって、
日付が変わる可能性がある、ということです。
UTC では 2025-03-25 だが、日本時間では 2025-03-26、
ということが普通に起こります。
Date → LocalTime(“時刻だけ”に落とす)
同じように、「時刻だけ」が欲しい場合です。
import java.time.*;
import java.util.Date;
public class DateToLocalTimeExample {
public static void main(String[] args) {
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zone = ZoneId.of("Asia/Tokyo");
LocalTime localTime = instant.atZone(zone).toLocalTime();
System.out.println("Date : " + date);
System.out.println("LocalTime: " + localTime);
}
}
Java「毎日 09:00〜18:00 の間かどうかを判定したい」など、
“日付はどうでもよくて時間帯だけ見たい”ときに、LocalTime に落としてしまうとロジックがすっきりします。
実務でのおすすめパターン
「内部は java.time、境界だけ Date」という設計
業務システムで長く効く設計は、だいたいこんな方針です。
外部(古いライブラリ、既存 DB、他システム)との境界では Date や epoch milli を使う。
アプリケーション内部では、Instant / ZonedDateTime / LocalDateTime / LocalDate / LocalTime を使う。
境界での入出力のときだけ、ここで紹介した変換パターンを使う。
こうしておくと、
「タイムゾーンが分からない Date がアプリ内部に入り込んでくる」
「long のまま時間を扱っていて、何が何だか分からない」
といった混乱をかなり防げます。
まとめ:Date変換で身につけてほしい感覚
java.util.Date は、
もう“主役”ではなく「レガシー世界との窓口」だと割り切るのが、今の Java の考え方です。
Date → Instant(toInstant)で新世界に連れてくる。
Instant → Date(Date.from)で古い API に渡す。
LocalDateTime / ZonedDateTime / LocalDate / LocalTime とは、ZoneId を意識しながら変換する。
アプリ内部は java.time で統一し、Date は境界でだけ使う。
あなたのプロジェクトのどこかに、Date と Calendar と LocalDateTime がごちゃ混ぜになっている箇所があれば、
そこを一度「Instant をハブにして整理できないか?」という目で眺めてみてください。
それが、「古い世界と新しい世界をきちんと橋渡しできるエンジニア」への、
かなり実務的な一歩になります。
