日付API
このレッスンを終えると、LocalDate が不変であることによる罠と、Period の読み方を説明できるようになります。
まず予測してください
次のコードは何を表示するでしょうか。
LocalDate d = LocalDate.of(2026, 8, 1);
d.plusDays(30);
System.out.println(d);答えは 2026-08-01 です。30 日進んでいません。ここが日付 API のいちばんの引っ掛けどころです。
日付クラスは全部が不変
LocalDate も LocalTime も LocalDateTime も不変です。plusDays や minusMonths や withDayOfMonth は、新しいオブジェクトを作って返すだけで、呼び出した相手を書き換えません。だから戻り値を受け取らないと何も起きません。String の toUpperCase と同じ考え方です。
正しくは次のように書きます。
LocalDate moved = d.plusDays(30);試験では、戻り値を捨てた行をさりげなく混ぜて「出力は」と聞いてきます。行の先頭が変数への代入になっているかを必ず見てください。
コンストラクタは使えない
new LocalDate(2026, 8, 1) はコンパイルエラーです。コンストラクタが公開されていないので、LocalDate.of や LocalDate.parse といった static メソッドで作ります。of の月は 1 から 12 で、昔の Calendar のような 0 始まりではありません。範囲外の月や日を渡すと DateTimeException が実行時に飛びます。
toString は 2026-08-01 の形になります。ゼロ埋めされる点も出力を答える問題で効いてきます。
Period は年月日に分かれる
Period.between(開始, 終了) は、2つの日付の差を年と月と日に分けて持ちます。getMonths は「差のうちの月の部分」であって、通算の月数ではありません。8 月 1 日から 9 月 15 日までなら、getMonths は 1 で getDays は 14 です。45 という数字はどこにも出てきません。
通算の日数がほしいときは ChronoUnit.DAYS.between を使います。Period の getDays と混同させる問題が繰り返し出ているので、分けて覚えてください。
要件
- 起点は LocalDate.of(2026, 8, 1) とする。実行するたびに変わる値は使わない
- 起点から days 日進めた日付を作る。起点そのものは変えない
- 起点から進めた日付までの Period を求める
- 起点、進めた日付、Period の月、Period の日をこの順にスラッシュでつないで返す
入出力例
shift(30) → "2026-08-01/2026-08-31/0/30"
shift(45) → "2026-08-01/2026-09-15/1/14"
shift(10) → "2026-08-01/2026-08-11/0/10"