Java Tips | 日付・時間:経過時間

Java Java
スポンサーリンク

経過時間のゴールイメージ

「処理に何秒かかった?」「ログインから何時間経過した?」「締切まであと何時間?」
業務システムでは、“2つの日時の差”=経過時間を扱う場面がとても多いです。

ここでまず押さえてほしいのは、
年・月・日・時・分・秒を自分でバラして引き算しない、ということです。
Java には DurationChronoUnit という、“時間差専用の道具”が用意されています。
これを素直に使うと、安全で読みやすい経過時間のコードが書けます。


Duration を使った経過時間の基本

Duration.between(開始日時, 終了日時)

Duration は「時間の長さ」を表すクラスです。
2つの LocalDateTimeInstant から経過時間を求めるときに使います。

import java.time.Duration;
import java.time.LocalDateTime;

public class ElapsedTimeBasic {

    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2025, 3, 26, 10, 0, 0);
        LocalDateTime end   = LocalDateTime.of(2025, 3, 26, 12, 30, 15);

        Duration d = Duration.between(start, end);

        long seconds = d.getSeconds();
        long minutes = d.toMinutes();
        long hours   = d.toHours();

        System.out.println("開始 : " + start);
        System.out.println("終了 : " + end);
        System.out.println("経過秒   : " + seconds); // 9015
        System.out.println("経過分   : " + minutes); // 150
        System.out.println("経過時間 : " + hours);   // 2
    }
}
Java

ここで重要なのは、Duration が「秒」をベースにしていることです。
getSeconds() で総秒数、toMinutes() で総分数、toHours() で総時間数が取れます。

「2時間30分15秒」という“見た目”ではなく、
「9015秒」「150分」「2時間」といった“総量”として扱えるのがポイントです。


経過時間を「時・分・秒」に分解する

総秒数から時・分・秒を計算する

画面表示などでは、「2時間30分15秒」のように分解して見せたいことが多いです。
その場合は、総秒数から自分で割り算していきます。

import java.time.Duration;
import java.time.LocalDateTime;

public class ElapsedTimeBreakdown {

    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2025, 3, 26, 10, 0, 0);
        LocalDateTime end   = LocalDateTime.of(2025, 3, 26, 12, 30, 15);

        Duration d = Duration.between(start, end);

        long totalSeconds = d.getSeconds();

        long hours   = totalSeconds / 3600;
        long remain  = totalSeconds % 3600;
        long minutes = remain / 60;
        long seconds = remain % 60;

        System.out.println("経過時間: " + hours + "時間" + minutes + "分" + seconds + "秒");
        // 2時間30分15秒
    }
}
Java

ここで深掘りしたいのは、
Duration は“総秒数”を持っているので、あとは算数でどうとでも表現できる」
という感覚です。

内部では常に「秒」で持っておき、
表示のときだけ「時・分・秒」に分解する、という設計にすると、
ロジックがシンプルになります。


ChronoUnit を使った経過時間の取得

HOURS.between, MINUTES.between, SECONDS.between

ChronoUnit を使うと、「何時間」「何分」「何秒」といった差を直接取ることもできます。

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public class ElapsedTimeChronoUnit {

    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2025, 3, 26, 10, 0, 0);
        LocalDateTime end   = LocalDateTime.of(2025, 3, 26, 12, 30, 15);

        long hours   = ChronoUnit.HOURS.between(start, end);
        long minutes = ChronoUnit.MINUTES.between(start, end);
        long seconds = ChronoUnit.SECONDS.between(start, end);

        System.out.println("経過時間(時間単位) : " + hours);   // 2
        System.out.println("経過時間(分単位)   : " + minutes); // 150
        System.out.println("経過時間(秒単位)   : " + seconds); // 9015
    }
}
Java

ChronoUnit.HOURS.between は「何時間分進んだか」を返します。
2時間30分15秒なら「2時間」、
分単位なら「150分」、秒単位なら「9015秒」です。

DurationChronoUnit のどちらを使うかは好みですが、
「経過時間を一つのオブジェクトとして扱いたい」なら Duration
「特定の単位だけ欲しい」なら ChronoUnit が読みやすいことが多いです。


Instant / ZonedDateTime と経過時間

Instant で“絶対時間”としての差を取る

サーバ間の通信時間や、タイムゾーンをまたぐ処理時間など、
「世界共通の時間軸」で経過時間を測りたいときは Instant を使います。

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

public class ElapsedTimeInstant {

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

        Thread.sleep(1234); // 1.234秒待つ

        Instant end = Instant.now();

        Duration d = Duration.between(start, end);

        System.out.println("経過ミリ秒 : " + d.toMillis());
    }
}
Java

Instant は「UTC 上の一点」を表すクラスです。
Instant.now() で取得した時刻同士の差は、
タイムゾーンに影響されない“純粋な経過時間”になります。

ZonedDateTime でも Duration.between は同じ

タイムゾーン付きの ZonedDateTime でも、Duration.between は使えます。

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

public class ElapsedTimeZoned {

    public static void main(String[] args) {
        ZonedDateTime start = ZonedDateTime.of(
                2025, 3, 26,
                10, 0, 0, 0,
                ZoneId.of("Asia/Tokyo")
        );

        ZonedDateTime end = start.plusHours(5).withZoneSameInstant(ZoneId.of("Europe/London"));

        Duration d = Duration.between(start, end);

        System.out.println("経過時間(時間) : " + d.toHours()); // 5
    }
}
Java

ここでのポイントは、
Duration.between は「同じ瞬間を指しているかどうか」を見ているので、
タイムゾーンが違っていても、同じ“Instant”なら正しい差が取れる、ということです。


実務での経過時間の使いどころ

例1:処理時間の計測(簡易プロファイル)

「この処理にどれくらい時間がかかっているか」を知りたいときです。

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

public class ProcessingTimeExample {

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

        heavyProcess();

        Instant end = Instant.now();

        Duration d = Duration.between(start, end);

        System.out.println("処理時間(ms): " + d.toMillis());
    }

    private static void heavyProcess() {
        long sum = 0;
        for (int i = 0; i < 10_000_000; i++) {
            sum += i;
        }
    }
}
Java

ここでのポイントは、
「処理の前後で Instant を取って、Duration で差を出す」というパターンを覚えることです。
これだけで、簡易的な性能計測ができるようになります。

例2:締切までの残り時間を出す

「締切まであと何時間何分か」をユーザーに見せたいケースです。

import java.time.Duration;
import java.time.LocalDateTime;

public class RemainingTimeExample {

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

        Duration d = Duration.between(now, due);

        long totalMinutes = d.toMinutes();
        long hours   = totalMinutes / 60;
        long minutes = totalMinutes % 60;

        System.out.println("締切まで残り: " + hours + "時間" + minutes + "分");
    }
}
Java

ここでも、「総分数→時と分に分解」という流れを使っています。
内部では常に「一つの単位(秒や分)」で持っておき、
表示のときだけ分解する、というパターンはとても応用が利きます。


経過時間ユーティリティとしてまとめる

measure(Runnable) のような“計測用メソッド”を作る

経過時間の計測は、あちこちで同じようなコードを書きがちなので、
ユーティリティにしておくと便利です。

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

public class TimeMeasureUtils {

    public static long measureMillis(Runnable task) {
        Instant start = Instant.now();
        try {
            task.run();
        } finally {
            Instant end = Instant.now();
            return Duration.between(start, end).toMillis();
        }
    }

    public static void main(String[] args) {
        long ms = measureMillis(() -> {
            long sum = 0;
            for (int i = 0; i < 5_000_000; i++) {
                sum += i;
            }
        });

        System.out.println("処理時間(ms): " + ms);
    }
}
Java

ここで深掘りしたいのは、
「経過時間の計測パターンを一か所に閉じ込めると、
どこで何を測っているかが分かりやすくなる」という点です。


まとめ:経過時間で絶対に覚えてほしいこと

経過時間は、「2つの日時の間に“どれだけの時間が流れたか”」を表す値です。

Duration.between(開始, 終了) で経過時間オブジェクトを作り、
toSeconds(), toMinutes(), toHours(), toMillis() などで必要な単位に変換する。
ChronoUnit.SECONDS/HOURS/... .between で、特定の単位だけを直接取ることもできる。
内部では「秒」や「分」のような一つの単位で持ち、表示のときだけ「時・分・秒」に分解する。
処理時間計測などのパターンはユーティリティメソッドにまとめておく。

もしあなたのコードのどこかに、
「時・分・秒を自前で引き算して、繰り下がりを頑張って処理している」ような箇所があれば、
そこを一度 DurationChronoUnit ベースに書き換えられないか眺めてみてください。

その小さな置き換えが、
“時間とカレンダーに強いエンジニア”への、確かな一歩になります。

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