日付の加減算

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

日付の加減算 とは

LocalDate の plusDays や minusDays を使って日付を前後に動かす方法を学び、不変オブジェクトとしての扱い方を身につけよう。

日付を前後に動かす — LocalDate の加減算

業務システムでは「予約日の 3 日前にリマインドを送る」「契約開始日から 30 日後の更新日を計算する」「過去 7 日間のログを集計する」など、日付を前後に動かしたい場面が山ほどあります。Java の LocalDate には、こうした計算をシンプルに書ける plusDays plusMonths plusYears といった一連のメソッドが用意されています。本章では、この日付の加減算をしっかり身につけて、月またぎやうるう年に怯えないコードを書けるようになりましょう。

昔の Java では java.util.Calendaradd(Calendar.DAY_OF_MONTH, 10) のような呼び出しで日付を動かしていました。書き方が冗長で Calendar のインスタンスが書き換わってしまうため、バグの温床として悪名高い API でした。Java 8 以降に登場した java.time パッケージの LocalDate は、それを反省して不変オブジェクトとして設計されています。

plusDays の基本

まずは一番よく使う plusDays から見ていきます。LocalDate のインスタンスに対して plusDays(n) を呼ぶと、n 日後の LocalDate新しいオブジェクトとして返ってきます。

Java

import java.time.LocalDate; public class Demo { public static void main(String[] args) { LocalDate today = LocalDate.of(2026, 5, 19); LocalDate tenDaysLater = today.plusDays(10); System.out.println(today); // 2026-05-19 (変わらない) System.out.println(tenDaysLater); // 2026-05-29 } }

ここで大事なのは、today.plusDays(10) を呼んでも today 自身は 2026-05-19 のまま変わらない点です。新しい LocalDate が戻り値として返ってくるので、それを変数 tenDaysLater で受け取っています。LocalDate のような 不変 (immutable) オブジェクト は「中身を書き換える」のではなく「新しいインスタンスを作って返す」設計です。

diagram (will load when visible)

plus* ファミリーと月またぎ・うるう年

plusDays の仲間には次のようなメソッドがあります。日 / 月 / 年 / 週それぞれを動かせます。

  • plusDays(long days)days 日後
  • plusWeeks(long weeks)weeks 週後 (7 日 × weeks)
  • plusMonths(long months)months か月後
  • plusYears(long years)years 年後

月またぎや年またぎは Java が自動で面倒を見てくれます。2024-01-311 日足せば 2024-02-01 になりますし、2024-02-281 日足せばうるう年なので 2024-02-29 になります。2025 年なら平年なので、2025-02-281 日足すと 2025-03-01 です。自分で「2 月は 28 日? 29 日?」と判定する必要はありません。

Java

LocalDate a = LocalDate.of(2024, 1, 31).plusDays(1); // 2024-02-01 LocalDate b = LocalDate.of(2024, 2, 28).plusDays(1); // 2024-02-29 (うるう年) LocalDate c = LocalDate.of(2025, 2, 28).plusDays(1); // 2025-03-01 (平年) LocalDate d = LocalDate.of(2026, 1, 31).plusMonths(1); // 2026-02-28 (2/31 は無いので末日に丸め)

最後の plusMonths(1) は要注意です。131 日に 1 か月足すと、231 日は存在しないので、Java は自動的にその月の末日 (2/282/29) に丸めてくれます。便利な反面、「翌月の同じ日」を厳密に求めたいときには、plusMonths だけだと意図しない動きをすることがあるので頭の片隅に置いておきましょう。

LocalDateplus* メソッドは、暦のルールを ISO-8601 (西暦のグレゴリオ暦) に従って正しく処理してくれます。和暦や旧暦などを扱う場合は別の API (JapaneseDate など) が必要ですが、一般的な業務では LocalDate で十分です。

minus* メソッドと、plus に負の数を渡す方法

日付を「前に戻す」場合は、対になる minus* メソッドを使うか、plus* に負の数を渡します。どちらでも結果は同じです。

Java

LocalDate today = LocalDate.of(2026, 5, 19); LocalDate aWeekAgo1 = today.minusDays(7); // 2026-05-12 LocalDate aWeekAgo2 = today.plusDays(-7); // 2026-05-12 (同じ) LocalDate lastYear = today.minusYears(1); // 2025-05-19 LocalDate lastMonth = today.minusMonths(1); // 2026-04-19

コードの読みやすさを考えると、過去を表すときは minusDays(7)、未来を表すときは plusDays(7) のように、意図が分かりやすい書き方を選ぶのがおすすめです。ただし、UI から渡ってきた符号付きの数値 (+10-7) をそのまま渡したいときは plusDays(n) 一発で済むので、用途に応じて使い分けましょう。今回の課題でも、引数 n が負の値になり得る前提で plusDays(n) を使うのがシンプルです。

with* で部分置換

「日付の の部分だけ 1 に揃えたい」「月だけ 12 に変えたい」というときには、with* メソッドが便利です。これも不変オブジェクトなので、新しい LocalDate を返します。

Java

LocalDate d = LocalDate.of(2026, 5, 19); LocalDate firstOfMonth = d.withDayOfMonth(1); // 2026-05-01 LocalDate dec = d.withMonth(12); // 2026-12-19 LocalDate nextYear = d.withYear(2027); // 2027-05-19

月初を求めたいときは withDayOfMonth(1)、月末を求めたいときは with(TemporalAdjusters.lastDayOfMonth()) のように TemporalAdjusters を使うと、その月の末日が自動で入ります。請求書の締め日計算など、月末を扱う処理で大活躍する API です。

diagram (will load when visible)

よくある間違い

LocalDate の加減算は素直な API なのですが、それでも初学者が引っかかりがちな罠がいくつかあります。

  • 戻り値を捨ててしまうtoday.plusDays(10); と書いただけで、戻り値を変数に代入し忘れるのが一番多いミスです。LocalDate は不変なので、戻り値を捨てると計算結果はどこにも残りません。必ず LocalDate later = today.plusDays(10); のように受け取りましょう
  • 月またぎを手で計算しようとする5/305 日足したいから「日 = 30 + 5 = 355 月は 31 日までだから 4 を引いて 6/4」のような計算は不要です。plusDays(5) に任せれば一発です。月またぎ・年またぎ・うるう年は API がすべて面倒を見てくれます
  • 不変オブジェクトを誤解するCalendar 時代の感覚で today.plusDays(10) を呼んだら today 自身が変わると思い込むパターンです。Stringreplace と同じく、LocalDateplus* minus* with* はすべて新しいインスタンスを返します。元のオブジェクトは絶対に変わりません

もし plusMonths の月末丸め (1/31 + 1 か月 → 2/28) で困った場合は、LocalDate.plus(Period.ofMonths(1)) でも同じ動きになります。厳密に「翌月の同じ日番号」を保証したい場合は、plusDays(30) のように日数で動かすか、月末判定を自前で組むことになります。

LocalDate.oftoString のおさらい

今回の課題で使う 2 つのメソッドも軽くおさらいしておきます。LocalDate.of(year, month, day) は、3 つの int から LocalDate を作る static ファクトリメソッドです。月は 112 の自然な数値で指定でき、Calendar 時代の「0 起点」のような罠はありません。

そして toString() を呼ぶと "2026-05-29" のような ISO-8601 形式 (yyyy-MM-dd) の文字列が返ってきます。println に渡したときに自動でこの形式になるのも toString() が呼ばれているからです。今回の課題では、計算後の LocalDatetoString() を呼んで文字列として返すので、フォーマットのことは特に気にしなくて構いません。

やってみよう

それでは今回の課題に挑戦してみましょう。やることは次のとおりです。

  1. ファイル先頭で import java.time.LocalDate; を書く
  2. Solution.addDays(int year, int month, int day, int n) の中で LocalDate.of(year, month, day) を呼んで LocalDate を作る
  3. その LocalDate に対して plusDays(n) を呼び、n 日後 (もしくは n が負なら |n| 日前) の LocalDate を取得する
  4. 取得した LocalDatetoString()return する

動作例は以下のとおりです。

  • addDays(2026, 5, 19, 10)"2026-05-29" (10 日後)
  • addDays(2024, 1, 1, 30)"2024-01-31" (うるう年の 1 月、30 日後はまだ同月末)
  • addDays(2024, 1, 31, 1)"2024-02-01" (月またぎは自動)
  • addDays(2026, 5, 19, -19)"2026-04-30" (負の値で過去に戻る)

plusDays は負の数を渡しても正しく動くので、n の符号を if で場合分けする必要はありません。return LocalDate.of(year, month, day).plusDays(n).toString();1 行で完成できます。完成したら、addDays(2024, 2, 28, 1)"2024-02-29" (うるう年) が返るかなど、自分でも試してみてください。日付計算の便利さを実感できるはずです。

よくある質問

Q. 中級の内容は実務でどれくらい使いますか?

A. Collection(List/Map/Set)、Stream、例外処理、ジェネリクスは毎日のように登場します。Date/Time API、Files、try-with-resources も実務で頻出するため、本コースの内容は実プロジェクトでそのまま役立ちます。

Q. Stream と for ループはどっちで書くべき?

A. 可読性で選んでください。filter + map + collect が綺麗にハマるなら Stream、副作用や複雑な分岐が多いなら for ループの方が読みやすいです。一律にどちらを使うべきという正解はなく、チームのコーディング規約に合わせるのが現実的です。

Q. 次のステップでは何を学ぶべきですか?

A. 中級の基礎が固まったら、Spring Boot で Web API を作る、JUnit でテストを書く、Maven/Gradle でビルドを管理する、といった実プロジェクトのスキルに進むと効果的です。OSS のコードを読む経験も大きく成長を促します。

次のレッスン

次は 日付と時刻 まとめクイズ で、日付と時刻 まとめクイズ を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. plus/minus の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. plus/minus とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. ファイル先頭で import java.time.LocalDate; を書くこと
  2. LocalDate.of(year, month, day)LocalDate を生成し、plusDays(n)n 日後の LocalDate を求めること
  3. 求めた LocalDatetoString() の結果を return すること (yyyy-MM-dd 形式の文字列になる)

入出力例

test-cases.txt

addDays(2026, 5, 19, 10)"2026-05-29" addDays(2024, 1, 1, 30)"2024-01-31" addDays(2024, 1, 31, 1)"2024-02-01" addDays(2026, 5, 19, -19)"2026-04-30" addDays(2024, 2, 28, 1)"2024-02-29"

ヒント

main.java
main.java
学習モード

メモ

日付の加減算

⌘S で保存