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

Java Java
スポンサーリンク

JST変換のゴールイメージ

「アプリは日本時間で動いている」「外部APIはUTCで返してくる」「DBにはUTCで保存したいけど画面はJSTで見せたい」。
こういうときに必要になるのが JST(日本標準時)との相互変換=JST変換 です。

Java では、JST は ZoneId.of("Asia/Tokyo") で表現します。
そして「JSTの日時」は ZonedDateTime、「世界共通の瞬間」は Instant で扱うのが基本パターンです。
引用: JavaのZonedDateTimeとZoneIdの基本的な使い方。


JSTとは何かをコードでつかむ

ZoneId.of(“Asia/Tokyo”) が「JST」の正体

JST は「UTC+9、サマータイムなし」のタイムゾーンです。
Java では、IANAタイムゾーンID "Asia/Tokyo" で表現します。

import java.time.ZoneId;
import java.time.ZonedDateTime;

public class JstNowExample {

    public static void main(String[] args) {
        ZoneId jst = ZoneId.of("Asia/Tokyo");

        ZonedDateTime nowJst = ZonedDateTime.now(jst);

        System.out.println("現在の日本時間: " + nowJst);
    }
}
Java

ZonedDateTime.now(jst) と書くことで、「JSTとしての現在時刻」が取れます。
ここには「+09:00」と「[Asia/Tokyo]」が含まれていて、「どこの国の何時か」がはっきり分かる形になっています。
引用: ZonedDateTimeとZoneIdでタイムゾーン付き日時を扱う設計。


UTC → JST 変換の基本

Instant(UTCの瞬間)をJSTで見る

DBや外部APIから Instant(UTC)を受け取り、それをJSTで表示したいケースです。

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class UtcToJst {

    public static void main(String[] args) {
        Instant utcInstant = Instant.parse("2025-03-26T01:00:00Z");

        ZoneId jst = ZoneId.of("Asia/Tokyo");
        ZonedDateTime jstTime = utcInstant.atZone(jst);

        System.out.println("UTC(Instant): " + utcInstant); // 2025-03-26T01:00:00Z
        System.out.println("JST         : " + jstTime);    // 2025-03-26T10:00+09:00[Asia/Tokyo]
    }
}
Java

ここで重要なのは、「保存・通信の基準は Instant(UTC)」「表示するときだけ atZone(Asia/Tokyo) でJSTに変換」という流れです。
このパターンを徹底すると、世界中どこからアクセスされても、時間の整合性が取りやすくなります。


JST → UTC 変換の基本

JSTの日時(ZonedDateTime)をInstantに変換する

今度は逆に、「日本時間で入力された日時をUTCに変換して保存したい」ケースです。

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Instant;

public class JstToUtc {

    public static void main(String[] args) {
        ZoneId jst = ZoneId.of("Asia/Tokyo");

        ZonedDateTime jstTime =
                ZonedDateTime.of(2025, 3, 26, 10, 0, 0, 0, jst);

        Instant utcInstant = jstTime.toInstant();

        System.out.println("JST         : " + jstTime);    // 2025-03-26T10:00+09:00[Asia/Tokyo]
        System.out.println("UTC(Instant): " + utcInstant); // 2025-03-26T01:00:00Z
    }
}
Java

流れはとてもシンプルです。
「これはJSTだ」と分かる ZonedDateTime を作り、toInstant() でUTCの瞬間に変換するだけです。
引用: ZonedDateTimeからInstantへの変換はZoneOffset計算を内部で安全に処理する。


LocalDateTime をそのままJST扱いしないための型の使い分け

LocalDateTime は「どこの国か分からないカレンダー表示」

LocalDateTime は「2025-03-26T10:00」のような“見た目”だけで、「それがどこの国の10:00か」を知りません。
JST変換を正しく行うには、必ず ZoneId.of("Asia/Tokyo") をかぶせて ZonedDateTime にする必要があります。

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.Instant;

public class LocalJstConversion {

    public static void main(String[] args) {
        LocalDateTime local = LocalDateTime.of(2025, 3, 26, 10, 0);

        ZoneId jst = ZoneId.of("Asia/Tokyo");
        ZonedDateTime jstTime = local.atZone(jst);

        Instant utcInstant = jstTime.toInstant();

        System.out.println("LocalDateTime : " + local);    // 2025-03-26T10:00
        System.out.println("JST           : " + jstTime);  // 2025-03-26T10:00+09:00[Asia/Tokyo]
        System.out.println("UTC(Instant)  : " + utcInstant);
    }
}
Java

ここで深掘りしたいのは、
「LocalDateTime は“カレンダー上の日時”であって、“世界のどの瞬間か”は決まっていない」という感覚です。
“瞬間”として扱いたいなら、必ず ZoneId をかぶせて ZonedDateTime にし、そのうえで toInstant() します。
引用: LocalDateTimeとZonedDateTimeの役割の違いに関する設計指針。


JST変換とセキュリティ・ログ設計

保存はUTC、表示はJSTという二段構え

セキュリティインシデント調査では、複数システム・複数国のログを突き合わせることがよくあります。
このとき、各システムがバラバラのローカル時間でログを書いていると、「どのイベントが先か後か」が非常に分かりにくくなります。

そこでよく採用されるのが、「ログ保存はUTC(Instant)」「画面表示はJST(またはユーザーのタイムゾーン)」という設計です。

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class SecurityLogJst {

    public static void main(String[] args) {
        Instant eventTimeUtc = Instant.now();

        System.out.println("ログ保存用(UTC): " + eventTimeUtc);

        ZoneId jst = ZoneId.of("Asia/Tokyo");
        ZonedDateTime viewJst = eventTimeUtc.atZone(jst);

        System.out.println("日本での表示(JST): " + viewJst);
    }
}
Java

こうしておけば、世界中のログを「UTC」という一本の時間軸で比較しつつ、日本の運用担当者にはJSTで直感的に見せることができます。
「保存の軸はUTC、見るときの軸はJST」という分離は、セキュリティ的にも運用的にも非常に強いパターンです。


JST変換ユーティリティとしてまとめる

「Instant ⇔ JST」を一か所に閉じ込める

JST変換はあちこちで同じようなコードを書きがちなので、ユーティリティにまとめておくと安全で読みやすくなります。

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class JstUtils {

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

    public static Instant toUtcFromJst(LocalDateTime localJst) {
        return localJst.atZone(JST).toInstant();
    }

    public static ZonedDateTime fromUtcToJst(Instant utcInstant) {
        return utcInstant.atZone(JST);
    }

    public static ZonedDateTime nowJst() {
        return ZonedDateTime.now(JST);
    }
}
Java

使う側は、例えばこう書けます。

import java.time.LocalDateTime;
import java.time.Instant;

public class JstUtilsUsage {

    public static void main(String[] args) {
        LocalDateTime jstInput = LocalDateTime.of(2025, 3, 26, 10, 0);

        Instant utc = JstUtils.toUtcFromJst(jstInput);

        System.out.println("入力(JST LocalDateTime): " + jstInput);
        System.out.println("保存用UTC(Instant)     : " + utc);
        System.out.println("表示用JST              : " + JstUtils.fromUtcToJst(utc));
    }
}
Java

「JSTは必ず JstUtils 経由で扱う」と決めてしまえば、アプリ全体でJST変換のルールが統一され、タイムゾーン絡みのバグが一気に減ります。


まとめ:JST変換で絶対に覚えてほしいこと

JST変換は、「JST(Asia/Tokyo)のローカルな日時」と「UTC(Instant)の世界共通の瞬間」を行き来することです。
JSTは ZoneId.of("Asia/Tokyo") で表現し、JSTの日時は ZonedDateTime として扱う。
保存・通信の“真実の時間”は Instant(UTC)で持ち、表示や入力のときにJSTへ変換する。
LocalDateTime 単体ではタイムゾーンが分からないので、「これはJSTだ」と決めてから atZone(Asia/Tokyo)toInstant() という流れにする。

もしあなたのコードのどこかに、「+9時間してJSTにする」「-9時間してUTCに戻す」といった手書きの時差計算があれば、
そこを一度 InstantZonedDateTime、そして ZoneId.of("Asia/Tokyo") ベースのJST変換に置き換えられないか眺めてみてください。

それが、時間にもセキュリティにも強いエンジニアへの、確かな一歩になります。

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