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

Java Java
スポンサーリンク

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

「処理が何ナノ秒で終わったか知りたい」「高頻度トレードやIoTでサブミリ秒の世界を扱いたい」「ログの順序を“同じミリ秒の中”でも区別したい」。
こういう“めちゃくちゃ細かい時間”を扱うときに出てくるのが、ナノ秒変換です。

Java の java.time API は、基本的に ナノ秒精度 を持っています。
ただし、DBや外部APIは「ミリ秒まで」「秒まで」しか対応していないことも多いので、
「ナノ秒をどこまで使うか」「どこで丸めるか/捨てるか」を決めることが、実務ではとても重要になります。


Instant とナノ秒の関係を理解する

Instant は「秒+ナノ秒」でできている

Instant は「UTC 上の一点(瞬間)」を表すクラスです。
内部的には、次の2つの値で構成されています。

  • エポック秒(1970-01-01T00:00:00Z からの秒)
  • その秒の中のナノ秒(0〜999,999,999)

コードで見てみましょう。

import java.time.Instant;

public class InstantNanoBasic {

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

        long epochSecond = now.getEpochSecond();
        int nano = now.getNano();

        System.out.println("Instant      : " + now);
        System.out.println("エポック秒   : " + epochSecond);
        System.out.println("秒内ナノ秒   : " + nano);
    }
}
Java

ここで大事なのは、「ナノ秒は“その秒の中のオフセット”」という感覚です。
epochSecondnano の組み合わせで、1秒より細かい位置まで表現している、というイメージを持ってください。


Instant とナノ秒の相互変換

Instant → 「秒+ナノ秒」に分解する

Instant からナノ秒情報を取り出すには、getEpochSecond()getNano() を使います。

import java.time.Instant;

public class InstantToNanoParts {

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

        long seconds = instant.getEpochSecond();
        int nanos = instant.getNano();

        System.out.println("Instant : " + instant);
        System.out.println("秒      : " + seconds);
        System.out.println("ナノ秒  : " + nanos);
    }
}
Java

この形にしておくと、例えば次のようなことができます。

  • DBに「秒」と「ナノ秒」を別カラムで保存する
  • 外部APIに「秒+ナノ秒」として渡す
  • ミリ秒や秒に丸める前の“生の精度”を保持しておく

「秒+ナノ秒」という分解は、ナノ秒変換の基本パターンだと思ってください。

「秒+ナノ秒」→ Instant を組み立てる

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

import java.time.Instant;

public class NanoPartsToInstant {

    public static void main(String[] args) {
        long seconds = 1_742_950_000L;
        int nanos = 123_456_789;

        Instant instant = Instant.ofEpochSecond(seconds, nanos);

        System.out.println("秒      : " + seconds);
        System.out.println("ナノ秒  : " + nanos);
        System.out.println("Instant: " + instant);
    }
}
Java

ここで深掘りしたいのは、「ナノ秒精度のデータを、きちんと Instant に戻せる」という安心感です。
“秒+ナノ秒”の世界と、“Instant”の世界を自由に行き来できるようになると、時間扱いの設計が一気に楽になります。


System.nanoTime と「経過時間としてのナノ秒」

nanoTime は「現在時刻」ではない

System.nanoTime() もナノ秒を返しますが、これは 現在時刻ではありません
「ある任意の基準点からの経過ナノ秒」を返す、単調増加のカウンタです。

public class NanoTimeElapsed {

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

        doHeavyWork();

        long end = System.nanoTime();

        long elapsedNanos = end - start;
        double elapsedMillis = elapsedNanos / 1_000_000.0;
        double elapsedSeconds = elapsedNanos / 1_000_000_000.0;

        System.out.println("経過ナノ秒 : " + elapsedNanos);
        System.out.println("経過ミリ秒 : " + elapsedMillis);
        System.out.println("経過秒     : " + elapsedSeconds);
    }

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

ここで強調したいのは、役割の違いです。

  • InstantSystem.currentTimeMillis()
    → 「いつの時刻か」を表す(壁時計の時間)
  • System.nanoTime()
    → 「どれくらい時間が経ったか」を測るための値(基準は不明でOK)

処理時間計測には nanoTime、ログのタイムスタンプには Instant
という使い分けを徹底すると、設計がかなりクリアになります。


Duration とナノ秒の変換

Duration → ナノ秒に変換する

Duration は「時間の長さ」を表すクラスで、ナノ秒との相性もとても良いです。
toNanos() を使うと、Duration をナノ秒に変換できます。

import java.time.Duration;

public class DurationToNanos {

    public static void main(String[] args) {
        Duration d = Duration.ofSeconds(2).plusMillis(500); // 2.5秒

        long nanos = d.toNanos();

        System.out.println("Duration: " + d);
        System.out.println("ナノ秒  : " + nanos);
    }
}
Java

「処理時間を Duration で持っておき、必要なときにナノ秒に変換する」というスタイルは、
秒・ミリ秒・分などへの変換も簡単で、コードの意図も読みやすくなります。

ナノ秒 → Duration に変換する

逆に、ナノ秒から Duration を作るときは Duration.ofNanos() を使います。

import java.time.Duration;

public class NanosToDuration {

    public static void main(String[] args) {
        long nanos = 2_500_000_000L; // 2.5秒

        Duration d = Duration.ofNanos(nanos);

        System.out.println("ナノ秒  : " + nanos);
        System.out.println("Duration: " + d); // PT2.5S
    }
}
Java

「ナノ秒で計測 → Duration に変換 → ログには秒で出す」といった流れも簡単に書けます。
“ナノ秒はあくまで内部表現で、外に出すときは丸める”という設計も取りやすくなります。


ナノ秒とローカル日時(JSTなど)の関係

LocalDateTime もナノ秒を持てる

LocalDateTime もナノ秒精度を持っています。
コンストラクタの最後の引数がナノ秒です。

import java.time.LocalDateTime;

public class LocalDateTimeWithNanos {

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

        System.out.println("LocalDateTime: " + dt);
    }
}
Java

ただし、LocalDateTime は「どこの国の時間か」を知りません。
“世界のどの瞬間か”として扱いたい場合は、必ず ZoneId をかぶせて ZonedDateTime にし、
そこから Instant に変換します。

JST の LocalDateTime(ナノ秒付き)→ Instant → 「秒+ナノ秒」

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

public class JstLocalNanosToInstant {

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

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

        Instant instant = jstZdt.toInstant();

        long seconds = instant.getEpochSecond();
        int nanos = instant.getNano();

        System.out.println("JST LocalDateTime: " + jstLocal);
        System.out.println("Instant          : " + instant);
        System.out.println("秒               : " + seconds);
        System.out.println("ナノ秒           : " + nanos);
    }
}
Java

ここでのポイントは、「ナノ秒付きのローカル日時も、最終的には Instant の“秒+ナノ秒”に落とし込める」ということです。
DBや外部APIの仕様に合わせて、「秒+ナノ秒」「ミリ秒」「秒」などに変換していきます。


セキュリティ・ログとナノ秒精度の付き合い方

どこまで細かく記録するかは“設計の意思決定”

セキュリティインシデント調査や高頻度トランザクションの世界では、
「同じミリ秒の中でどちらのイベントが先か」を区別したくなることがあります。
そのときに役立つのがナノ秒精度です。

一方で、ナノ秒までログに出すと、

  • ログの文字列が長くなる
  • 人間が読みづらくなる
  • 行動パターンを非常に細かく追跡できてしまう(プライバシーの観点)

といった側面もあります。

よくある設計は、例えば次のようなものです。

  • 内部的には Instant(ナノ秒精度)で持つ
  • 通常のアプリケーションログはミリ秒まで、あるいは秒までに丸める
  • どうしても必要な監査ログだけ、ナノ秒まで出す

このとき、「ナノ秒をどこで丸めるか/捨てるか」をユーティリティに閉じ込めておくと、
システム全体で一貫したルールを保ちやすくなります。


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

「Instant ⇔ 秒+ナノ秒」「Duration ⇔ ナノ秒」を一か所に集約する

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

import java.time.Duration;
import java.time.Instant;

public class NanoUtils {

    // Instant → 秒+ナノ秒
    public static long instantToEpochSecond(Instant instant) {
        return instant.getEpochSecond();
    }

    public static int instantToNanoOfSecond(Instant instant) {
        return instant.getNano();
    }

    // 秒+ナノ秒 → Instant
    public static Instant ofEpochSecondAndNano(long seconds, int nanos) {
        return Instant.ofEpochSecond(seconds, nanos);
    }

    // Duration ⇔ ナノ秒
    public static long durationToNanos(Duration duration) {
        return duration.toNanos();
    }

    public static Duration nanosToDuration(long nanos) {
        return Duration.ofNanos(nanos);
    }
}
Java

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

Instant now = Instant.now();
long sec = NanoUtils.instantToEpochSecond(now);
int nano = NanoUtils.instantToNanoOfSecond(now);

Instant back = NanoUtils.ofEpochSecondAndNano(sec, nano);
Java

「ナノ秒を触るときは必ず NanoUtils を通す」と決めてしまえば、
丸め方や表現方法を一括でコントロールでき、後からの仕様変更にも強くなります。


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

ナノ秒変換は、「エポック秒+ナノ秒」「Duration のナノ秒」「nanoTime のナノ秒」といった値と、
InstantDuration を行き来することです。

Instant は「秒+ナノ秒」で“瞬間”を表現している。
System.nanoTime は「時刻」ではなく「経過時間測定用のカウンタ」。
Duration はナノ秒とも相互変換できる「時間の長さ」。
ログや外部連携では、ナノ秒をどこまで使うか(丸めるか)を設計として決める。

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

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

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