D-01: java.util.Date ↔ LocalDate ↔ java.sql.Date 相互変換
レガシーコードとモダンコードが混在する現場で頻出の型変換パターン。Instant を経由した確実な変換方法を解説します。
いつ使うか
- 古いコード(Java 8 以前)が返す
java.util.Dateを モダンな処理で使いたいとき - JDBC の ResultSet から取得した
java.sql.Dateを ビジネスロジックで扱いたいとき - 計算結果の
LocalDateを JDBC / 古い API に渡すとき
サンプルコード
DateConversionSample.java
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
public class DateConversionSample {
public static void main(String[] args) {
// ① java.util.Date → LocalDate
Date utilDate = new Date();
LocalDate fromUtilDate = utilDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
System.out.println("util.Date → LocalDate: " + fromUtilDate);
// ② LocalDate → java.util.Date
LocalDate localDate = LocalDate.of(2024, 4, 1);
Date toUtilDate = Date.from(
localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
System.out.println("LocalDate → util.Date: " + toUtilDate);
// ③ java.sql.Date → LocalDate
java.sql.Date sqlDate = java.sql.Date.valueOf("2024-04-01");
LocalDate fromSqlDate = sqlDate.toLocalDate();
System.out.println("sql.Date → LocalDate: " + fromSqlDate);
// ④ LocalDate → java.sql.Date
java.sql.Date toSqlDate = java.sql.Date.valueOf(localDate);
System.out.println("LocalDate → sql.Date: " + toSqlDate);
// ⑤ java.util.Date → java.sql.Date
java.sql.Date utilToSql = new java.sql.Date(utilDate.getTime());
System.out.println("util.Date → sql.Date: " + utilToSql);
}
}Java 8 で追加された java.time パッケージを使った変換です。
よくあるミス・注意点
⚠️ タイムゾーンを省略すると環境依存になる
ZoneId.systemDefault() はJVMのデフォルトタイムゾーンを使います。 サーバーの環境によって UTC や JST が変わると日付がズレます。 本番環境では ZoneId.of("Asia/Tokyo") のように明示するのが安全です。
⚠️ java.sql.Date に対して toInstant() を呼ぶと例外
java.sql.Date.toInstant() は UnsupportedOperationException をスローします。 必ず toLocalDate() を使ってください。
テストする観点
- 日付の値が変換前後で一致すること(年・月・日)
- タイムゾーンが JST の環境と UTC の環境で同じ結果になること
- うるう年(2月29日)が正しく変換されること
nullを渡したときに NullPointerException が発生すること(または null を返すこと)