From 87e7bc50267af70056252dffea1ac2b4540cccbe Mon Sep 17 00:00:00 2001 From: Dorian Villet Date: Sat, 10 Oct 2026 10:24:11 +0200 Subject: [PATCH 1/2] Add the years and months of a Period at once in LocalDate::plusPeriod() plusPeriod() added the years, then the months, so a Feb 29 was clamped by both steps, and an intermediate year could be out of range even when the result was valid: LocalDate::of(2020, 2, 29)->plusPeriod(Period::of(1, 1, 0)); // 2021-03-28 instead of 2021-03-29 LocalDate::of(999995, 2, 26)->plusPeriod(Period::of(5, -29, 5)); // "Invalid year: 1000000" It now adds the total number of months in one step, as java.time does. LocalDateTime and ZonedDateTime::plusPeriod() go through it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fm9bvgKFasEGzpK48bQrxA --- src/LocalDate.php | 3 +-- tests/LocalDateTest.php | 6 ++++++ tests/LocalDateTimeTest.php | 6 ++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/LocalDate.php b/src/LocalDate.php index 6387b44a..fbcd5b27 100644 --- a/src/LocalDate.php +++ b/src/LocalDate.php @@ -455,8 +455,7 @@ public function withDay(int $day): LocalDate public function plusPeriod(Period $period): LocalDate { return $this - ->plusYears($period->getYears()) - ->plusMonths($period->getMonths()) + ->plusMonths($period->getYears() * 12 + $period->getMonths()) ->plusDays($period->getDays()); } diff --git a/tests/LocalDateTest.php b/tests/LocalDateTest.php index 8f134910..a6a7f79d 100644 --- a/tests/LocalDateTest.php +++ b/tests/LocalDateTest.php @@ -984,6 +984,12 @@ public static function providerPeriod(): array [2001, 2, 3, 0, 30, 50, 2003, 9, 22], [2001, 2, 3, 0, 0, -30, 2001, 1, 4], [2001, 2, 3, 0, -30, -50, 1998, 6, 14], + [2020, 2, 29, 1, 0, 0, 2021, 2, 28], + [2020, 2, 29, 1, 1, 0, 2021, 3, 29], + [2020, 2, 29, 1, -1, 0, 2021, 1, 29], + [2020, 2, 29, 1, 6, 0, 2021, 8, 29], + [2020, 2, 29, -1, -1, 0, 2019, 1, 29], + [999995, 2, 26, 5, -29, 5, 999997, 10, 1], ]; } diff --git a/tests/LocalDateTimeTest.php b/tests/LocalDateTimeTest.php index 8eedf007..0caf2812 100644 --- a/tests/LocalDateTimeTest.php +++ b/tests/LocalDateTimeTest.php @@ -652,6 +652,12 @@ public static function providerPeriod(): array [2001, 2, 3, 0, 30, 50, 2003, 9, 22], [2001, 2, 3, 0, 0, -30, 2001, 1, 4], [2001, 2, 3, 0, -30, -50, 1998, 6, 14], + [2020, 2, 29, 1, 0, 0, 2021, 2, 28], + [2020, 2, 29, 1, 1, 0, 2021, 3, 29], + [2020, 2, 29, 1, -1, 0, 2021, 1, 29], + [2020, 2, 29, 1, 6, 0, 2021, 8, 29], + [2020, 2, 29, -1, -1, 0, 2019, 1, 29], + [999995, 2, 26, 5, -29, 5, 999997, 10, 1], ]; } From 846b73e01310a31516c45495371fd18cd2ff9c76 Mon Sep 17 00:00:00 2001 From: Dorian Villet Date: Sat, 10 Oct 2026 10:25:48 +0200 Subject: [PATCH 2/2] Explain why plusPeriod() does not chain plusYears() and plusMonths() Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Fm9bvgKFasEGzpK48bQrxA --- src/LocalDate.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LocalDate.php b/src/LocalDate.php index fbcd5b27..cc5336b3 100644 --- a/src/LocalDate.php +++ b/src/LocalDate.php @@ -455,6 +455,7 @@ public function withDay(int $day): LocalDate public function plusPeriod(Period $period): LocalDate { return $this + // Not plusYears()->plusMonths(), which would clamp Feb 29 twice: 2020-02-29 + P1Y1M would give 2021-03-28. ->plusMonths($period->getYears() * 12 + $period->getMonths()) ->plusDays($period->getDays()); }