概要

保守案件やフレームワーク連携では、java.util.Date や java.sql.Date が残っている場面に遭遇します。新規コードでは LocalDate を使いたくても、既存の API やライブラリが旧来の Date 型を返してくる限り、変換処理は避けられません。変換自体は数行で書けますが、タイムゾーンの指定を忘れると深夜0時付近で日付がずれる事故が起きます。この記事では java.util.Date、java.sql.Date、LocalDate の相互変換パターンを整理し、タイムゾーン指定の落とし穴、JDBC ドライバー経由でのやりとり、テストで確認すべきポイントを押さえます。

使いどころ

レガシーシステムから取得した java.util.Date を LocalDate に変換して業務ロジックに渡す

JDBC の ResultSet から取得した java.sql.Date を LocalDate として扱い、日付計算に使う

外部 API が返す Date 型のレスポンスを LocalDate に変換して帳票出力に利用する

コード例

java.util.Date / java.sql.Date / LocalDate の相互変換
import java.time.LocalDate;

public class DateConversion {

    private static final ZoneId JST = ZoneId.of("Asia/Tokyo");

    public static LocalDate toLocalDate(Date utilDate) {
        return utilDate.toInstant()
                .atZone(JST)
                .toLocalDate();
    }

    public static Date toUtilDate(LocalDate localDate) {
        return Date.from(
            localDate.atStartOfDay(JST).toInstant());
    }

    public static LocalDate fromSqlDate(java.sql.Date sqlDate) {
        return sqlDate.toLocalDate();
    }

    public static java.sql.Date toSqlDate(LocalDate localDate) {
        return java.sql.Date.valueOf(localDate);
    }

    public static void main(String[] args) {

        var utilDate = new Date();
        var local = toLocalDate(utilDate);
        var sqlDate = toSqlDate(local);
        System.out.println("util.Date  : " + utilDate);
        System.out.println("LocalDate  : " + local);
        System.out.println("sql.Date   : " + sqlDate);

        var back = toUtilDate(local);
        System.out.println("復元 Date  : " + back);
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

変換ロジック自体は Java 8 と同じ。var 宣言で型推論を使えるため記述が若干簡潔になる。

Java 17
// Java 17: var で型推論
var utilDate = new Date();
var fromUtil = utilDate.toInstant()
        .atZone(ZoneId.systemDefault())
        .toLocalDate();
var sqlDate = java.sql.Date.valueOf(fromUtil);

Library Comparison

Pure Java (java.time)日付変換のみなら標準 API で十分。ZoneId の指定を忘れやすいが、コード量は最小限。
Joda-TimeJava 7 以前の保守案件で Date 変換が頻発する場合。Java 8 以降は標準 API で代替可能。移行コストが残る。

注意点

java.util.Date → LocalDate の変換には ZoneId の指定が必須。省略すると JVM のデフォルトタイムゾーンに依存し、環境によって結果が変わる

java.sql.Date.valueOf(LocalDate) で生成した sql.Date は時刻部分が 00:00:00 になる。Timestamp が必要な場面と混同しないこと

java.util.Date は内部的にミリ秒精度、LocalDate は日付のみ。変換時に時刻情報が切り捨てられる点を意識する

深夜0時をまたぐ処理では、タイムゾーン次第で日付が前日になることがある。テストでは UTC と JST の両方で確認する

FAQ

java.util.Date から LocalDate への変換で日付がずれるのはなぜですか?

ZoneId を指定せずにデフォルトタイムゾーンが UTC になっている場合、JST との9時間差で日付が前日になることがあります。

java.sql.Date と java.sql.Timestamp の使い分けは?

日付のみなら sql.Date、日時が必要なら sql.Timestamp を使います。JDBC の setDate / setTimestamp と対応します。

変換のたびに ZoneId.systemDefault() を書くのは冗長では?

定数で ZoneId.of("Asia/Tokyo") を定義しておくと、環境依存を排除でき可読性も上がります。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます