LocalDate.plusDaysで日付が変わらない理由と戻り値の代入を解説する記事のアイキャッチ。

実装・エラー解決

LocalDate.plusDaysで日付が変わらない理由|戻り値を代入する必要がある

AIによる要約

LocalDateは不変オブジェクトです。plusDaysは元のdateを書き換えず、計算後の新しいLocalDateを返します。そのためdate.plusDays(1)だけではdateは変わりません。date = date.plusDays(1)のように戻り値を受け取ります。月末やうるう年はAPIが処理しますが、営業日、締日、タイムゾーンは別の業務ルールなので、単純な日数加算と混同しないことが重要です。

この記事はXへ投稿したJavaクイズの詳しい解説です。まず問題を解きたい方は、Xの元ポストを開くことができます。

新人SE
新人SE
plusDays(1)を呼んだのに、出力した日付が昨日のままです。
ポンコツSE
ポンコツSE
LocalDateは元の値を変更しません。計算結果として返る新しいLocalDateを変数へ代入してください。

日付を1日進めるコードは、バッチ対象日、請求期限、検索範囲などで頻繁に使います。plusDaysという名前から、変数の中身がその場で更新されるように感じますが、LocalDateは不変です。

戻り値を受け取らないコードはコンパイルも通り、例外も出ません。そのため障害時に気付きにくく、ループなら同じ日付を処理し続ける可能性もあります。日付APIの契約と業務日付のルールを分けて確認します。

この記事のポイント

  • LocalDateは不変オブジェクト
  • plusDaysは新しいLocalDateを返す
  • 戻り値を代入しないと元の日付は変わらない
  • 月末・うるう年はAPIが処理する
  • 営業日や締日は別の業務ルールとして実装する

まずはXのJavaクイズを確認する

LocalDateへplusDays(1)を呼んだ後に元の変数を出力した結果を問うJavaクイズ画像。
1日後、元の日付、コンパイルエラーのどれになるでしょうか。
LocalDate date = LocalDate.of(2026, 8, 13);

date.plusDays(1);

System.out.println(date);

答えは2026-08-13です。plusDaysは翌日のLocalDateを返しますが、元のdateは変更しません。

LocalDateは不変でplusDaysの戻り値を代入しないため元の日付のままと説明する画像。
答えは元の2026-08-13です。計算結果を受け取っていません。

元の投稿は、Xの出題ポスト回答ポストでも確認できます。

LocalDateは不変オブジェクト

不変オブジェクトは、作成後に内部の値が変わりません。LocalDateの年・月・日はsetterで変更できず、加算や変更メソッドは新しいインスタンスを返します。

LocalDate original = LocalDate.of(2026, 8, 13);
LocalDate next = original.plusDays(1);

System.out.println(original); // 2026-08-13
System.out.println(next);     // 2026-08-14

文字列のreplaceやtoUpperCaseでも同じ考え方があります。戻り値があるメソッドを呼んだら、その値を使っているか確認する習慣を付けます。

正しくは戻り値を代入する

同じ変数名で次の日付を使うなら、戻り値を再代入します。元の日付も必要なら別変数へ保持します。

date = date.plusDays(1);

// または意図が分かる別名にする
LocalDate nextDate = date.plusDays(1);

メソッドチェーンもすべて新しい値を返します。途中の結果を読みやすくする必要があれば、1行へ詰め込まず変数を分けます。

ループで戻り値を捨てると処理が進まない

日付範囲を1日ずつ処理するループで代入を忘れると、currentが変わらず無限ループになります。テストデータが0件なら処理本体の副作用だけが繰り返される可能性もあります。

for (LocalDate current = from;
     !current.isAfter(to);
     current = current.plusDays(1)) {
    dailyService.execute(current);
}

for文の更新式へ書けば、日付を進める責務が明確です。whileを使う場合も、例外やcontinueで更新処理を飛ばさない構造にします。

月末とうるう年はAPIに任せる

LocalDateはカレンダーを理解しているため、月末やうるう年を手計算する必要はありません。

System.out.println(LocalDate.of(2026, 1, 31).plusDays(1));
// 2026-02-01

System.out.println(LocalDate.of(2024, 2, 28).plusDays(1));
// 2024-02-29

ただしplusMonths(1)の結果は『同じ日番号が存在しなければ月末へ調整』されます。1か月後と翌月末は業務上同じとは限らないため、期待例を仕様へ書きます。

営業日計算はplusDaysだけでは足りない

土日、祝日、会社休日、銀行営業日を飛ばす締切計算は、単純なplusDaysでは表せません。休日カレンダーを参照する業務サービスとして分けます。

LocalDate nextBusinessDay(LocalDate date) {
    LocalDate candidate = date.plusDays(1);
    while (holidayCalendar.isHoliday(candidate)) {
        candidate = candidate.plusDays(1);
    }
    return candidate;
}

祝日マスタの更新時期、地域差、臨時休日などもあります。日付APIの正しさと休日データの正しさは別に監視します。

LocalDateと日時・タイムゾーンを混同しない

LocalDateには時刻とタイムゾーンがありません。利用者の現地日付、サーバー日付、UTC基準の締切を扱う場合、LocalDateだけでは情報が足りないことがあります。

LocalDate todayInTokyo = LocalDate.now(ZoneId.of("Asia/Tokyo"));
LocalDate todayInUtc = LocalDate.now(ZoneOffset.UTC);

テストでLocalDate.now()へ直接依存すると日付境界で不安定になります。現在日を使うServiceではClockを注入するなど、プロジェクトの方針に合わせて固定可能にします。

JUnitで月末と境界日を確認する

通常日だけでなく、月末、年末、うるう日、範囲の開始日と終了日をテストします。営業日なら祝日前後も必要です。

@ParameterizedTest
@CsvSource({
    "2026-01-31, 2026-02-01",
    "2024-02-28, 2024-02-29",
    "2026-12-31, 2027-01-01"
})
void 翌日を計算する(LocalDate source, LocalDate expected) {
    assertEquals(expected, source.plusDays(1));
}

APIそのものではなく、請求期限や処理対象日の自作ロジックをこの境界値で検証します。テスト名と期待値に業務用語を入れると、仕様変更時に直す場所が分かります。

現場レビューでよくある指摘

日付処理のレビューでは、戻り値の代入だけでなく、暦日、営業日、締日、タイムゾーンのどれを計算しているかを確認します。

// レビューコメント例
plusDaysは元のLocalDateを変更しません。
戻り値をdateへ代入するか、nextDateとして利用してください。

// レビューコメント例
このwhileではcurrentが更新されないため処理が終わりません。
ループ更新式でcurrent = current.plusDays(1)としてください。

// レビューコメント例
仕様は翌営業日ですが、plusDays(1)では休日を考慮できません。
休日カレンダーを使う業務メソッドへ分けてください。

日付が1日ずれた障害は、データ修正や再請求につながります。手元の今日だけで確認せず、月末・年末・休日・タイムゾーンを含む具体例で説明します。

提出前のセルフチェック

レビュー前に確認すること

  • plusDaysの戻り値を使っているか
  • 元の日付を残す必要があるか
  • ループで日付が必ず進むか
  • 月末・年末・うるう年をテストしたか
  • 暦日と営業日を区別したか
  • 締日や月末調整の仕様を確認したか
  • タイムゾーンが必要な日時ではないか
  • 現在日に依存するテストを固定できるか

Javaの日付APIを基本から学ぶ参考書

LocalDate、LocalDateTime、ZoneIdの役割を体系的に整理しておくと、単純な日数加算と業務日付計算を混同しにくくなります。

書籍「スッキリわかるJava入門 実践編 第5版」の表紙
商品画像:Amazon.co.jp
PR Java基礎学習後の若手向け

スッキリわかるJava入門 実践編 第5版

中山 清喬 (著), 株式会社フレアリンク (監修)

基礎文法の次に必要な、現場寄りのJava知識を補う。

コレクション、ジェネリクス、ラムダ式、ストリームなど、業務コードで出会いやすい機能を入門編の次に学べます。

  • Java基礎の次に何を学ぶか迷っている
  • コレクションやStreamを整理したい

当サイトはAmazonアソシエイト・プログラムの参加者です。価格・在庫・配送条件はAmazonでご確認ください。

この記事とあわせて読みたい

まとめ

LocalDateは不変オブジェクトなので、plusDaysは元の日付を書き換えず、新しいLocalDateを返します。日付を進めるなら戻り値を変数へ代入してください。

月末とうるう年はAPIへ任せられますが、営業日、締日、タイムゾーンは業務ルールです。ループの更新漏れと境界日をテストし、何の日付を計算しているか明確にします。

-実装・エラー解決
-,