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

Java Java
スポンサーリンク

秒変換で何をしたいのかイメージする

「DBに“1970年からの秒”で保存されている」「外部APIがエポック秒で送ってくる」「処理時間を秒でログに出したい」。
こういうときに必要になるのが、日付・時間と「秒」の相互変換です。

ミリ秒と同じく、秒も「エポック(1970-01-01T00:00:00Z)」からの経過時間として扱うことが多いです。
Java の日付・時間API(InstantDuration)と、この“エポック秒”をどう行き来するかを押さえておくと、業務コードがかなりスッキリします。


エポック秒とは何か

1970-01-01T00:00:00Z からの経過秒

エポック秒は、「1970-01-01T00:00:00Z(UTC)」からの経過時間を秒単位で表したものです。
0 はちょうど 1970-01-01T00:00:00Z、1 は 1 秒後、60 は 1 分後、60×60×24 は 1 日後、というイメージです。

ミリ秒と違うのは「単位が秒」であることだけで、基準(UTCのエポック)は同じです。
「秒で持つか、ミリ秒で持つか」は、精度とデータ量のトレードオフとして設計で決めます。


Instant と秒の相互変換

Instant からエポック秒へ変換する

Instant は「UTC 上の瞬間」を表すクラスです。
これをエポック秒に変換するには、getEpochSecond() を使います。

import java.time.Instant;

public class InstantToSeconds {

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

        long epochSeconds = now.getEpochSecond();

        System.out.println("Instant     : " + now);
        System.out.println("エポック秒 : " + epochSeconds);
    }
}
Java

ここで深掘りしたいのは、「内部では Instant を使い、保存や通信のときだけ秒にする」という発想です。
Instant はナノ秒精度を持っていますが、getEpochSecond() すると秒単位に丸められます(小数点以下は切り捨て)。

エポック秒から Instant へ変換する

逆に、エポック秒から Instant を作るときは Instant.ofEpochSecond を使います。

import java.time.Instant;

public class SecondsToInstant {

    public static void main(String[] args) {
        long epochSeconds = 1_742_950_000L; // 例

        Instant instant = Instant.ofEpochSecond(epochSeconds);

        System.out.println("エポック秒 : " + epochSeconds);
        System.out.println("Instant   : " + instant);
    }
}
Java

「DBは long(秒)、アプリ内部は Instant」という構成にしておくと、
タイムゾーン変換やフォーマットなどをすべて Instant ベースで統一できて、バグが減ります。


秒とローカル日時(JSTなど)の変換

エポック秒 → 日本時間の LocalDateTime

DB にエポック秒が入っていて、それを日本時間の日時として画面に表示したいケースです。

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

public class SecondsToJstDateTime {

    public static void main(String[] args) {
        long epochSeconds = 1_742_950_000L; // 例

        Instant instant = Instant.ofEpochSecond(epochSeconds);

        ZoneId jst = ZoneId.of("Asia/Tokyo");
        ZonedDateTime jstZdt = instant.atZone(jst);

        LocalDateTime jstLocal = jstZdt.toLocalDateTime();

        System.out.println("エポック秒        : " + epochSeconds);
        System.out.println("Instant(UTC)     : " + instant);
        System.out.println("JST ZonedDateTime: " + jstZdt);
        System.out.println("JST LocalDateTime: " + jstLocal);
    }
}
Java

流れは、エポック秒 → Instant → JST の ZonedDateTimeLocalDateTime です。
ここで重要なのは、「秒はあくまで UTC 基準の“瞬間”であり、JST かどうかは後から決める」という感覚です。

日本時間の LocalDateTime → エポック秒

今度は逆に、「画面で入力された日本時間の日時をエポック秒で保存したい」ケースです。

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

public class JstDateTimeToSeconds {

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

        ZoneId jst = ZoneId.of("Asia/Tokyo");
        ZonedDateTime jstZdt = jstLocal.atZone(jst);

        long epochSeconds = jstZdt.toInstant().getEpochSecond();

        System.out.println("JST LocalDateTime: " + jstLocal);
        System.out.println("JST ZonedDateTime: " + jstZdt);
        System.out.println("エポック秒      : " + epochSeconds);
    }
}
Java

ここで深掘りしたいのは、「LocalDateTime 単体では“どこの国の時間か”が分からない」という点です。
必ず ZoneId をかぶせて ZonedDateTime にし、そのうえで toInstant()getEpochSecond() という流れにします。


処理時間計測と「秒」への変換

nanoTime で測って秒に変換する

処理時間を測るときは、System.nanoTime() を使うのが定番です。
これを秒に変換するのもよくあるパターンです。

public class ElapsedSecondsExample {

    public static void main(String[] args) {
        long start = System.nanoTime();

        doHeavyWork();

        long end = System.nanoTime();

        long elapsedNanos = end - start;

        double elapsedSeconds = elapsedNanos / 1_000_000_000.0;

        System.out.println("処理時間(秒): " + elapsedSeconds);
    }

    private static void doHeavyWork() {
        try {
            Thread.sleep(1500); // 1.5秒だけ待つ
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
Java

ここで大事なのは、「時刻そのもの」と「経過時間」は別物だ、という感覚です。
時刻は Instant やエポック秒、経過時間は nanoTimeDuration で扱う、と役割を分けると設計がきれいになります。


Duration と秒の変換

Duration → 秒

Duration は「時間の長さ」を表すクラスで、秒との相性がとても良いです。
getSeconds()toSeconds() 相当の扱いで、秒単位に変換できます。

import java.time.Duration;

public class DurationToSeconds {

    public static void main(String[] args) {
        Duration d = Duration.ofMinutes(2).plusSeconds(30); // 2分30秒

        long seconds = d.getSeconds(); // 全体を秒にした値

        System.out.println("Duration: " + d);
        System.out.println("秒      : " + seconds); // 150
    }
}
Java

秒 → Duration

逆に、秒から Duration を作るときは Duration.ofSeconds を使います。

import java.time.Duration;

public class SecondsToDuration {

    public static void main(String[] args) {
        long seconds = 150L;

        Duration d = Duration.ofSeconds(seconds);

        System.out.println("秒      : " + seconds);
        System.out.println("Duration: " + d); // PT2M30S
    }
}
Java

「処理時間を秒でログに出すけれど、内部では Duration で扱う」といった設計にすると、
「分に直したい」「ミリ秒に直したい」といった変換も簡単になり、コードの意図も読みやすくなります。


セキュリティ・ログ設計と秒

粒度を“秒”にするか“ミリ秒”にするか

セキュリティインシデント調査では、ログの時刻が「どれくらい細かいか」が重要になります。
秒まであれば、だいたいの順序は追えますが、同じ秒の中で大量のイベントが起きるシステムでは、ミリ秒が欲しくなることもあります。

設計としては、例えば次のような考え方があります。

内部的には Instant(ナノ秒精度)で持つ。
ログ出力は秒までにする(あるいはミリ秒までにする、とルール化)。
画面表示は秒まで、あるいは分までに丸める。

「内部の精度」と「外に見せる精度」を分けておくと、
必要なときには高精度な情報を使いつつ、普段は過剰な情報を出さない、というバランスが取りやすくなります。


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

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

秒変換も、あちこちで同じようなコードを書きがちなので、ユーティリティにまとめておくと実務でかなり楽になります。

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

public class SecondsUtils {

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

    public static long instantToEpochSeconds(Instant instant) {
        return instant.getEpochSecond();
    }

    public static Instant epochSecondsToInstant(long epochSeconds) {
        return Instant.ofEpochSecond(epochSeconds);
    }

    public static long jstLocalToEpochSeconds(LocalDateTime localJst) {
        ZonedDateTime zdt = localJst.atZone(JST);
        return zdt.toInstant().getEpochSecond();
    }

    public static LocalDateTime epochSecondsToJstLocal(long epochSeconds) {
        Instant instant = Instant.ofEpochSecond(epochSeconds);
        return instant.atZone(JST).toLocalDateTime();
    }
}
Java

呼び出し側は、例えば次のように書けます。

LocalDateTime jst = LocalDateTime.of(2025, 3, 26, 10, 5, 30);
long epochSeconds = SecondsUtils.jstLocalToEpochSeconds(jst);

LocalDateTime back = SecondsUtils.epochSecondsToJstLocal(epochSeconds);
Java

「秒を触るときは必ず SecondsUtils を通す」と決めてしまえば、
エポックの基準やタイムゾーンの扱いを間違えるリスクが一気に減ります。


まとめ:秒変換で身につけてほしい感覚

秒変換は、「エポック秒」と「日時オブジェクト(Instant や LocalDateTime)」を行き来することです。

エポック秒は「1970-01-01T00:00:00Z からの経過時間」であり、UTC 基準である。
“瞬間”として扱うなら Instant を使い、getEpochSecondofEpochSecond で秒と相互変換する。
ローカル時間(JST など)とは、ZoneIdZonedDateTime を経由して変換する。
処理時間の計測には、nanoTimeDuration を使い、必要に応じて秒に変換してログやメトリクスに出す。

もしあなたのコードのどこかに、「それっぽい long の秒を直接割り算して日付を計算している」ような箇所があれば、
そこを一度 InstantZonedDateTime ベースの秒変換に置き換えられないか眺めてみてください。

その小さな置き換えが、時間にもセキュリティにも強いエンジニアへの、かなり実務的で確かな一歩になります。

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