Java Tips | 日付・時間:Date変換

Java Java
スポンサーリンク

古い 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 から LocalDateTimeZonedDateTime への変換は、すでに整った 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 を要求してくるとき、
内部では InstantLocalDateTime で扱い、
最後に 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

ここでの重要ポイントは、
LocalDateTimeZonedDateTimeatZone)→ InstantDate
という流れを一度覚えてしまうことです。

「ローカルな日時」を「世界共通の瞬間」に投影してから、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 から「日付だけ」を取り出したいときは、
一度 InstantZoneId を経由して 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 は境界でだけ使う。

あなたのプロジェクトのどこかに、
DateCalendarLocalDateTime がごちゃ混ぜになっている箇所があれば、
そこを一度「Instant をハブにして整理できないか?」という目で眺めてみてください。

それが、「古い世界と新しい世界をきちんと橋渡しできるエンジニア」への、
かなり実務的な一歩になります。

タイトルとURLをコピーしました