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

Java Java
スポンサーリンク

経過日数のゴールイメージ

「申込日から何日経った?」「納期まであと何日?」「プロジェクト開始からの経過日数は?」
業務システムだと、“2つの日付の差”=経過日数を出す場面が本当に多いです。

ここでまず押さえてほしいのは、
「経過日数は“カレンダー上で何日分進んだか”であって、“年・月・日をバラして計算するものではない” ということです。
Java では ChronoUnit.DAYS.betweenLocalDate の差分を使うことで、
安全かつシンプルに「経過日数」を扱えます。


LocalDate 同士の経過日数を求める基本

ChronoUnit.DAYS.between(開始日, 終了日)

一番よく使うのがこれです。

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class ElapsedDaysBasic {

    public static void main(String[] args) {
        LocalDate start = LocalDate.of(2025, 3, 1);
        LocalDate end   = LocalDate.of(2025, 3, 10);

        long days = ChronoUnit.DAYS.between(start, end);

        System.out.println("開始日 : " + start); // 2025-03-01
        System.out.println("終了日 : " + end);   // 2025-03-10
        System.out.println("経過日数 : " + days); // 9
    }
}
Java

ここで重要なのは、
between(start, end) は「start から end までに“何日分進んだか”」を返す という点です。

3/1 の翌日は 3/2 なので 1 日経過、
3/10 の前日は 3/9 なので、3/1→3/10 は 9 日分進んでいる、という考え方です。

「開始日と終了日を両方含めた日数(=10 日)」が欲しい場合は、
days + 1 のように自分で調整します。
ここは業務仕様とセットで決めるポイントなので、
「経過日数」と「日数(両端含む)」を混同しないように意識してください。


マイナスになるケースと順序の意味

開始日と終了日の順番を間違えるとどうなるか

between は「第1引数 → 第2引数」の差を取ります。
順番を逆にすると、符号も逆になります。

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class ElapsedDaysOrder {

    public static void main(String[] args) {
        LocalDate d1 = LocalDate.of(2025, 3, 1);
        LocalDate d2 = LocalDate.of(2025, 3, 10);

        long a = ChronoUnit.DAYS.between(d1, d2); // 9
        long b = ChronoUnit.DAYS.between(d2, d1); // -9

        System.out.println("d1→d2 : " + a);
        System.out.println("d2→d1 : " + b);
    }
}
Java

ここで押さえておきたいのは、
「どちらを“基準”とみなすかで、経過日数の符号が変わる」 ということです。

多くの業務では「過去→未来」で考えるので、
「開始日」「終了日」という名前で変数を持ち、
between(start, end) の形に統一しておくと、読み間違いが減ります。


LocalDateTime / ZonedDateTime の経過日数

LocalDateTime でも DAYS.between は使える

日時(時刻付き)でも、ChronoUnit.DAYS.between は使えます。

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

public class ElapsedDaysDateTime {

    public static void main(String[] args) {
        LocalDateTime start = LocalDateTime.of(2025, 3, 1, 10, 0);
        LocalDateTime end   = LocalDateTime.of(2025, 3, 3, 9, 0);

        long days = ChronoUnit.DAYS.between(start, end);

        System.out.println("経過日数 : " + days); // 1
    }
}
Java

3/1 10:00 → 3/3 9:00 は、
丸 2 日には届いていないので「1 日分」とカウントされます。

ここでのポイントは、
「DAYS.between は“24 時間単位で何回分進んだか”を見ている」 という感覚です。
「カレンダー上で何日またいだか」ではなく、
「時間として何日分経ったか」に近いイメージになります。

日付ベースで数えたいなら LocalDate に落とす

「とにかく日付だけで考えたい」「時刻は無視して、日付の差だけ知りたい」
という場合は、toLocalDate() で日付に落としてから計算するのが安全です。

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

public class ElapsedDaysByDateOnly {

    public static void main(String[] args) {
        LocalDateTime startDateTime = LocalDateTime.of(2025, 3, 1, 23, 59);
        LocalDateTime endDateTime   = LocalDateTime.of(2025, 3, 3, 0, 1);

        LocalDate start = startDateTime.toLocalDate(); // 2025-03-01
        LocalDate end   = endDateTime.toLocalDate();   // 2025-03-03

        long days = ChronoUnit.DAYS.between(start, end);

        System.out.println("日付ベースの経過日数 : " + days); // 2
    }
}
Java

業務では、「日付単位での経過日数」が欲しいことが多いので、
「LocalDate で扱うか、LocalDateTime で扱うか」を最初に決める のが大事です。


Period を使った「日数」取得との違い

Period.between で days を見るときの注意

Period でも「日」の差を取り出せますが、
これは「年・月・日」に分解された“残り”としての日数です。

import java.time.LocalDate;
import java.time.Period;

public class PeriodDaysExample {

    public static void main(String[] args) {
        LocalDate start = LocalDate.of(2025, 1, 1);
        LocalDate end   = LocalDate.of(2025, 3, 10);

        Period p = Period.between(start, end);

        System.out.println("years : " + p.getYears());  // 0
        System.out.println("months: " + p.getMonths()); // 2
        System.out.println("days  : " + p.getDays());   // 9
    }
}
Java

この days は「2 ヶ月を引いた残りの日数」であって、
「トータルで何日経ったか」ではありません。

「トータルの経過日数」が欲しいときは、ChronoUnit.DAYS.between を使う
と覚えておくと混乱しません。


実務での経過日数の使いどころ

例1:申込から経過日数を出す

「申込から 30 日を過ぎたら自動キャンセル」などの要件です。

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class ApplicationElapsedExample {

    public static void main(String[] args) {
        LocalDate applied = LocalDate.of(2025, 3, 1);
        LocalDate today   = LocalDate.of(2025, 4, 5);

        long elapsed = ChronoUnit.DAYS.between(applied, today);

        System.out.println("経過日数 : " + elapsed);           // 35
        System.out.println("30日超過? : " + (elapsed > 30)); // true
    }
}
Java

ここでのポイントは、
「経過日数を一度変数にしてから、業務ルールに当てはめる」 ことです。
あちこちで between を直接 if 文に書き始めると、
後から仕様変更が入ったときに追いかけるのが大変になります。

例2:締切までの残り日数を出す

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

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class RemainingDaysExample {

    public static void main(String[] args) {
        LocalDate today = LocalDate.of(2025, 3, 20);
        LocalDate due   = LocalDate.of(2025, 3, 25);

        long remaining = ChronoUnit.DAYS.between(today, due);

        System.out.println("締切まで残り日数 : " + remaining); // 5
    }
}
Java

ここでも、「今日を含めるかどうか」「締切日を含めるかどうか」は仕様次第です。
between の結果をそのまま使うのか、+1 するのか、
“どちらが正しいか”ではなく、“業務としてどう数えるか”を決める ことが大事です。


経過日数ユーティリティとしてまとめる

elapsedDays(start, end) を一か所に閉じ込める

経過日数も、あちこちで同じようなコードを書きがちなので、
ユーティリティメソッドにしておくとスッキリします。

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class DateDiffUtils {

    public static long elapsedDays(LocalDate start, LocalDate end) {
        if (start == null || end == null) {
            throw new IllegalArgumentException("start / end は null にできません");
        }
        return ChronoUnit.DAYS.between(start, end);
    }

    public static long inclusiveDays(LocalDate start, LocalDate end) {
        return elapsedDays(start, end) + 1;
    }

    public static void main(String[] args) {
        LocalDate s = LocalDate.of(2025, 3, 1);
        LocalDate e = LocalDate.of(2025, 3, 10);

        System.out.println("経過日数 : " + elapsedDays(s, e));   // 9
        System.out.println("両端含む日数 : " + inclusiveDays(s, e)); // 10
    }
}
Java

ここで深掘りしたいのは、
「経過日数」と「両端を含む日数」を明確に名前で分けている ことです。
名前が分かれていれば、呼び出し側のコードを読んだときに
「どっちの数え方をしているのか」が一目で分かります。


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

経過日数は、「2つの日付(または日時)の間に“何日分進んだか”」を表す値です。

トータルの経過日数が欲しいときは ChronoUnit.DAYS.between を使う。
LocalDate で扱うか、LocalDateTime で扱うかを最初に決める。
「開始日と終了日を両方含めた日数」が欲しい場合は、自分で +1 するなどルールを明示する。
ユーティリティメソッドに閉じ込めて、「経過日数」と「両端含む日数」を名前で区別する。

もしあなたのコードのどこかに、
「年・月・日をバラして無理やり日数を計算している」ような箇所があれば、
そこを一度 ChronoUnit.DAYS.between ベースに書き換えられないか眺めてみてください。

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

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