From 49ab19b43783e5955a661d310101a38586a43ffa Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Sun, 25 Oct 2026 16:59:55 +0900 Subject: [PATCH 1/2] fix: Reject impossible dates in DateTimeDiff with a format createFromFormat() silently rolls impossible dates over, so "2023-02-30" for "Y-m-d" was compared as 2023-03-02. The input is now checked with isDateTime() first, matching how Date and the other validators behave. --- src/Validators/DateTimeDiff.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Validators/DateTimeDiff.php b/src/Validators/DateTimeDiff.php index 74b52d1fc..c429e3f81 100644 --- a/src/Validators/DateTimeDiff.php +++ b/src/Validators/DateTimeDiff.php @@ -139,6 +139,10 @@ private function createDateTimeObject(mixed $input): DateTimeInterface|null } } + if (!$this->isDateTime($this->format, (string) $input)) { + return null; + } + $format = $this->getExceptionalFormats()[$this->format] ?? $this->format; $dateTime = DateTimeImmutable::createFromFormat($format, (string) $input); if ($dateTime === false) { From 30f047290c6aefb716c7fabbd417ee16c605618f Mon Sep 17 00:00:00 2001 From: Application-drop-up Date: Sun, 25 Oct 2026 16:59:56 +0900 Subject: [PATCH 2/2] test: Cover impossible dates in DateTimeDiff with a format Cover "2023-02-30" with "Y-m-d" and "c", which must be rejected, plus a leap day and an exceptional format that must still pass. --- tests/unit/Validators/DateTimeDiffTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/unit/Validators/DateTimeDiffTest.php b/tests/unit/Validators/DateTimeDiffTest.php index 5ee71bc29..43f214828 100644 --- a/tests/unit/Validators/DateTimeDiffTest.php +++ b/tests/unit/Validators/DateTimeDiffTest.php @@ -43,6 +43,11 @@ public static function providerForValidInput(): array 'years + DateTime' => [new DateTimeDiff('years', Stub::pass(1)), new DateTimeImmutable()], 'years + string' => [new DateTimeDiff('years', Stub::pass(1)), '2020-01-01'], 'years + string + format' => [new DateTimeDiff('years', Stub::pass(1), 'Y-m-d'), '2020-01-01'], + 'years + leap day + format' => [new DateTimeDiff('years', Stub::pass(1), 'Y-m-d'), '2024-02-29'], + 'years + string + exceptional format' => [ + new DateTimeDiff('years', Stub::pass(1), 'c'), + '2020-01-01T00:00:00+00:00', + ], 'years + string + format + now' => [ new DateTimeDiff('years', Stub::pass(1), 'Y-m-d', new DateTimeImmutable()), '2020-01-01', @@ -80,6 +85,14 @@ public static function providerForInvalidInput(): array new DateTimeDiff('years', Stub::fail(1), 'Y-m-d'), 'invalid date', ], + 'impossible date, with passing rule' => [ + new DateTimeDiff('years', Stub::pass(1), 'Y-m-d'), + '2023-02-30', + ], + 'impossible date in exceptional format, with passing rule' => [ + new DateTimeDiff('years', Stub::pass(1), 'c'), + '2023-02-30T00:00:00+00:00', + ], ] + array_map( static fn(array $args): array => [new DateTimeDiff('years', Stub::fail(1)), new DateTimeImmutable()], iterator_to_array(self::providerForNonScalarValues()),