日付差分計算は「どれくらい時間が経ったか」を正しく言語化する作業
業務システムでは、「締切まであと何日か」「利用期間は何ヶ月か」「滞在時間は何時間か」といった「差分」を扱う場面が本当に多いです。
このときに、単純に「ミリ秒の差を出して割り算する」ようなやり方をすると、うるう年や月の長さ、サマータイムなどで簡単に破綻します。
Java では、java.time の Period、Duration、ChronoUnit を使うことで、「日付の差」「日時の差」を安全かつ読みやすく計算できます。
ここでは、初心者向けに「どの型をいつ使うか」「どうユーティリティ化すると実務で効くか」を、例題付きでかみ砕いて説明していきます。
日付差分の基本:Period と ChronoUnit を使う
LocalDate 同士の「日数差」を出す
「開始日と終了日の間に何日あるか」を知りたいときは、LocalDate と ChronoUnit.DAYS の組み合わせがシンプルです。
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class DateDiffDaysExample {
public static void main(String[] args) {
LocalDate start = LocalDate.of(2025, 1, 1);
LocalDate end = LocalDate.of(2025, 1, 14);
long days = ChronoUnit.DAYS.between(start, end);
System.out.println(days); // 13
}
}
Javaここでのポイントは、「between(start, end) は『start から end までに何日“挟まっているか”』を返す」という感覚です。
2025-01-01 から 2025-01-14 までは 13 日分の差があるので、結果は 13 になります(両端を含めるかどうかは業務要件次第)。
この ChronoUnit.DAYS.between は、「とにかく日数差が欲しい」というときの鉄板パターンとして覚えておいてください。
年・月・日をまとめて扱いたいときは Period
「何年何ヶ月何日」という形で差分を表現したいときは、Period を使います。
import java.time.LocalDate;
import java.time.Period;
public class PeriodExample {
public static void main(String[] args) {
LocalDate start = LocalDate.of(2020, 1, 1);
LocalDate end = LocalDate.of(2025, 3, 15);
Period p = Period.between(start, end);
System.out.println(p.getYears()); // 5
System.out.println(p.getMonths()); // 2
System.out.println(p.getDays()); // 14
}
}
Javaこの例では、「5 年 2 ヶ月 14 日」という差分になります。
ここで深掘りしたいのは、「Period は『カレンダー的な差分』を表す」という点です。
月の長さは 28〜31 日でバラバラ、うるう年もある──こうした「カレンダーの揺らぎ」をきちんと考慮したうえで、「年・月・日」の差を出してくれるのが Period です。
「契約期間」「勤続年数」「年齢」のように、「カレンダー感覚」が重要な差分には Period を使うのが自然です。
日時差分の基本:Duration と ChronoUnit を使う
LocalDateTime 同士の「時間差」「分差」を出す
「開始日時と終了日時の間に何時間あるか」「何分あるか」を知りたいときは、ChronoUnit.HOURS や ChronoUnit.MINUTES を使います。
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class DateTimeDiffExample {
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2025, 1, 14, 9, 0);
LocalDateTime end = LocalDateTime.of(2025, 1, 14, 18, 30);
long hours = ChronoUnit.HOURS.between(start, end); // 9
long minutes = ChronoUnit.MINUTES.between(start, end); // 570
System.out.println(hours);
System.out.println(minutes);
}
}
Java「勤務時間」「滞在時間」「処理時間」など、時間単位での差分が欲しいときに、この書き方はとても素直です。
Duration で「何時間何分何秒」をまとめて扱う
Duration は、「時間ベースの差分」を表すクラスです。
import java.time.Duration;
import java.time.LocalDateTime;
public class DurationExample {
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2025, 1, 14, 9, 0);
LocalDateTime end = LocalDateTime.of(2025, 1, 14, 18, 30);
Duration d = Duration.between(start, end);
System.out.println(d.toHours()); // 9
System.out.println(d.toMinutes()); // 570
System.out.println(d.getSeconds()); // 34200
}
}
JavaDuration は「秒」や「ナノ秒」を内部表現として持っていて、toHours() や toMinutes() で好きな単位に変換できます。
「処理時間をミリ秒でログに出したい」「API のタイムアウトまでの残り時間を秒で管理したい」といった場面で非常に便利です。
ここでの重要ポイントは、「Period はカレンダー(日付)ベース、Duration は時間ベース」という切り分けです。
「何年何ヶ月何日」は Period、「何時間何分何秒」は Duration、と覚えておくと迷いません。
実務で使える「日付差分ユーティリティ」の形
日数差を出すユーティリティ
業務コードのあちこちで ChronoUnit.DAYS.between と書くのは少しうるさいので、ユーティリティにまとめてしまうのが定番です。
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public final class DateDiffs {
private DateDiffs() {}
public static long daysBetween(LocalDate start, LocalDate end) {
if (start == null || end == null) {
throw new IllegalArgumentException("start and end must not be null");
}
return ChronoUnit.DAYS.between(start, end);
}
}
Java使う側はこうなります。
long days = DateDiffs.daysBetween(startDate, endDate);
Javaここで深掘りしたいのは、「null チェックや例外方針をユーティリティ側に閉じ込める」という設計です。
「null のときは 0 を返すのか、例外にするのか」といったポリシーを、ユーティリティの中で統一しておくと、呼び出し側のコードがシンプルになります。
営業日(平日)だけを数えるユーティリティのイメージ
実務では、「土日を除いた営業日数を知りたい」という要件もよく出てきます。
これは標準 API だけでは一発では出せないので、ループで日付を進めながらカウントする形になります。
import java.time.DayOfWeek;
import java.time.LocalDate;
public final class BusinessDays {
private BusinessDays() {}
public static long businessDaysBetween(LocalDate startInclusive, LocalDate endExclusive) {
if (startInclusive == null || endExclusive == null) {
throw new IllegalArgumentException("dates must not be null");
}
long days = 0;
for (LocalDate d = startInclusive; d.isBefore(endExclusive); d = d.plusDays(1)) {
DayOfWeek dow = d.getDayOfWeek();
if (dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY) {
days++;
}
}
return days;
}
}
Javaこの例では、開始日を含み、終了日は含まない形([start, end))で数えています。
祝日を除外したい場合は、別途「祝日カレンダー」を持っておき、それを参照しながらカウントすることになります。
ここでのポイントは、「業務ルール(営業日定義)をユーティリティに閉じ込める」ことです。
「土日だけ除く」「土日+祝日を除く」「会社独自の休業日も除く」など、プロジェクトごとのルールを一箇所に集約しておくと、仕様変更に強くなります。
差分の向き(マイナス)と端の含み方に注意する
start と end の順番で符号が変わる
ChronoUnit.DAYS.between(start, end) は、「start から end まで」の差を返します。start が end より後ろの日付なら、結果はマイナスになります。
LocalDate a = LocalDate.of(2025, 1, 14);
LocalDate b = LocalDate.of(2025, 1, 10);
long diff1 = ChronoUnit.DAYS.between(a, b); // -4
long diff2 = ChronoUnit.DAYS.between(b, a); // 4
Java業務で「締切まであと何日」といった表示をするときは、マイナスをそのまま出すのか、絶対値を取るのか、0 未満は 0 とみなすのか、といった仕様を決めておく必要があります。
端を含めるかどうかを業務で決める
「2025-01-01 から 2025-01-14 までの期間は何日か?」という問いに対して、
ChronoUnit.DAYS.between(2025-01-01, 2025-01-14) は 13 を返します。
これは「1 日から 14 日の前日(13 日)までで 13 日分」という考え方です。
一方、「1 日と 14 日を両方含めて 14 日間」と数えたい要件もあります。
その場合は、
long daysInclusive = ChronoUnit.DAYS.between(start, end) + 1;
Javaのように、「両端を含めるなら +1 する」というルールをユーティリティ側に明示的に書いておくと、後から読んだ人にも意図が伝わります。
タイムゾーンをまたぐ差分計算の注意点
ZonedDateTime での差分は「どの時間軸で見るか」を意識する
タイムゾーン付きの日時(ZonedDateTime)同士の差分を取るときは、「UTC ベースで見るのか」「ローカル時間ベースで見るのか」を意識する必要があります。
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
public class ZonedDiffExample {
public static void main(String[] args) {
ZonedDateTime tokyo = ZonedDateTime.of(2025, 1, 14, 9, 0, 0, 0, ZoneId.of("Asia/Tokyo"));
ZonedDateTime utc = tokyo.withZoneSameInstant(ZoneId.of("UTC"));
long hours = ChronoUnit.HOURS.between(utc, tokyo);
System.out.println(hours); // 0 ではなく、計算の仕方次第で変わり得る
}
}
Java実務では、「内部では UTC にそろえて差分を計算し、表示だけローカル時間にする」という設計が多いです。
「どの時間軸で差分を取るか」をチームで決め、それをユーティリティに落とし込んでおくと、安全に運用できます。
まとめ:日付差分計算で初心者が身につけるべき感覚
日付差分計算は、「なんとなくミリ秒を引き算する」ではなく、「どの単位で、どのルールで、何を知りたいのか」を設計する作業です。
カレンダー的な差分(年・月・日)は Period、時間的な差分(時間・分・秒)は Duration と ChronoUnit を使う。
日数差は ChronoUnit.DAYS.between を基本形として覚え、端を含めるかどうかは業務要件として明示する。
営業日や会社独自のカレンダーは、ループ+判定ロジックをユーティリティに閉じ込める。
タイムゾーンや UTC をまたぐ差分は、「どの時間軸で見るか」をチームで決めてから実装する。
