Java Tips | 日付・時間:日付加算

Java Java
スポンサーリンク

「日付加算」は“カレンダーの面倒を Java に任せる”こと

日付加算は、
「今日から 7 日後」「月末から 1 日後」「3 ヶ月後の同じ日」
といった“カレンダー計算”をコードでやることです。

これを自前で year + 1 とか day + 7 とかやり始めると、
うるう年・月末・月の長さの違いなどで、あっという間にバグだらけになります。

そこで使うのが LocalDateplusXxx 系メソッドと Period です。
「カレンダーのルールは Java に任せる」という感覚を持つのが大事です。


LocalDate の基本的な日付加算

日数を足す:plusDays

一番よく使うのが「何日後」です。

import java.time.LocalDate;

public class PlusDaysExample {

    public static void main(String[] args) {
        LocalDate today = LocalDate.of(2025, 3, 26);
        LocalDate after7 = today.plusDays(7);

        System.out.println("today  : " + today);   // 2025-03-26
        System.out.println("after7 : " + after7);  // 2025-04-02
    }
}
Java

ここで重要なのは、
plusDays が「月をまたぐ」「年をまたぐ」ことも自動で面倒を見てくれる点です。

3 月 26 日に 7 日足したら 4 月 2 日になる、
という当たり前のことを、
自分で「日+7、31 を超えたら月+1…」などと書く必要はありません。

さらに大事なポイントがもう一つあります。
LocalDate は不変(immutable)なので、today 自体は変わりません。

LocalDate today = LocalDate.of(2025, 3, 26);
LocalDate after7 = today.plusDays(7);

System.out.println(today);  // 2025-03-26
System.out.println(after7); // 2025-04-02
Java

「元の値はそのまま、新しい日付を返す」という性質は、
バグを減らすうえでとても強力です。


月・年を足す:plusMonths / plusYears と“月末問題”

月を足す:plusMonths

「1 ヶ月後」「3 ヶ月後」もよく出てきます。

import java.time.LocalDate;

public class PlusMonthsExample {

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

        System.out.println(d1); // 2025-01-15
        System.out.println(d2); // 2025-02-15
    }
}
Java

ここで深掘りしたいのが「月末問題」です。

LocalDate endJan = LocalDate.of(2025, 1, 31);
LocalDate plus1 = endJan.plusMonths(1);

System.out.println(plus1); // 2025-02-28
Java

1 月 31 日に 1 ヶ月足すと、2 月 31 日は存在しないので、
「2 月の末日(28 日)」になります。

これは仕様としてそう決まっています。
「月末に対して plusMonths すると、次の月の“可能な限り同じ日”」になる、
という感覚を持っておくとよいです。

年を足す:plusYears

年も同じように扱えます。

import java.time.LocalDate;

public class PlusYearsExample {

    public static void main(String[] args) {
        LocalDate d1 = LocalDate.of(2024, 2, 29); // うるう年
        LocalDate d2 = d1.plusYears(1);

        System.out.println(d1); // 2024-02-29
        System.out.println(d2); // 2025-02-28
    }
}
Java

うるう年の 2 月 29 日に 1 年足すと、
次の年には 2 月 29 日がないので 2 月 28 日になります。

「存在しない日付は、その月の末日に丸められる」
というルールを覚えておくと、挙動に驚かなくなります。


Period を使った「年+月+日」まとめて加算

Period.of(年, 月, 日) で“期間”を表す

Period は「何年何ヶ月何日」という“期間”を表すクラスです。

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

public class PeriodExample {

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

        Period period = Period.of(1, 2, 10); // 1年2ヶ月10日
        LocalDate result = start.plus(period);

        System.out.println(start);  // 2025-03-26
        System.out.println(result); // 2026-06-05
    }
}
Java

「1 年 2 ヶ月 10 日後」のような複合的な加算を、
一つのオブジェクトにまとめて表現できます。

業務では、
「契約期間 1 年」「無料期間 1 ヶ月」「支払猶予 10 日」
などを Period として持っておき、
start.plus(period) で満了日を計算する、という使い方が自然です。


業務っぽい例1:支払期限(請求日+30日)

「請求日から 30 日後」を求める

import java.time.LocalDate;

public class BillingDueDateExample {

    public static void main(String[] args) {
        LocalDate billingDate = LocalDate.of(2025, 3, 26);
        LocalDate dueDate = billingDate.plusDays(30);

        System.out.println("請求日 : " + billingDate); // 2025-03-26
        System.out.println("支払期限: " + dueDate);     // 2025-04-25
    }
}
Java

ここでのポイントは、
「30 日後=1 ヶ月後ではない」ということです。

「月末締め翌月末払い」のようなルールなら plusMonths(1)
「30 日以内に支払い」のようなルールなら plusDays(30)
というように、ビジネスルールに合わせてメソッドを選びます。


業務っぽい例2:サブスクリプションの更新日(開始日+1ヶ月)

「毎月同じ“日”に更新する」イメージ

import java.time.LocalDate;

public class SubscriptionExample {

    public static void main(String[] args) {
        LocalDate start = LocalDate.of(2025, 1, 31);
        LocalDate next = start.plusMonths(1);

        System.out.println("開始日 : " + start); // 2025-01-31
        System.out.println("次回日: " + next);   // 2025-02-28
    }
}
Java

ここでまた月末問題が出てきます。

「31 日開始の人は、2 月だけ 28 日更新になる」
という仕様でよければ plusMonths(1) で OK です。

もし「必ず月末更新にしたい」なら、
「開始日を一度月末に丸める」「次回も月末に丸める」
といった別ロジックが必要になります。

大事なのは、
「plusMonths は“カレンダー的に自然な動き”をするが、ビジネスルールと完全一致するとは限らない」
と理解しておくことです。


LocalDateTime / ZonedDateTime の日付加算

LocalDateTime でも同じ感覚で使える

LocalDateTime にも plusDays / plusMonths / plusYears があります。

import java.time.LocalDateTime;

public class LocalDateTimePlusExample {

    public static void main(String[] args) {
        LocalDateTime dt = LocalDateTime.of(2025, 3, 26, 9, 0);
        LocalDateTime after = dt.plusDays(3);

        System.out.println(dt);    // 2025-03-26T09:00
        System.out.println(after); // 2025-03-29T09:00
    }
}
Java

「日付だけでなく時刻も一緒に動く」だけで、考え方は LocalDate と同じです。

ZonedDateTime ではタイムゾーンも意識される

ZonedDateTime に対して plusDays などを使うと、
タイムゾーンやサマータイムも考慮されます。

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

public class ZonedDateTimePlusExample {

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

        ZonedDateTime after = zdt.plusDays(1);

        System.out.println(zdt);
        System.out.println(after);
    }
}
Java

サマータイムのある地域では、
「1 日後」が 24 時間後とは限らないこともありますが、
ZonedDateTime はそのあたりも含めて正しく扱ってくれます。


まとめ:日付加算で身につけてほしい感覚

日付加算は、
「カレンダーのルールを自分で実装しない」ことが何より大事です。

LocalDate / LocalDateTime / ZonedDateTimeplusDays / plusMonths / plusYears を使う。
月末やうるう年では「存在しない日付は末日に丸められる」ことを理解しておく。
複合的な期間は Period で表現し、plus(period) で加算する。
ビジネスルール(30 日後なのか、1 ヶ月後なのか、月末なのか)を意識してメソッドを選ぶ。

あなたのコードのどこかに、
year++day += 30 のような“手作りカレンダー計算”があれば、
そこを一度「LocalDate の plusXxx」に置き換えられないか眺めてみてください。

それだけで、日付まわりのバグがかなり減り、
「時間に強いエンジニア」に一歩近づけます。

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