fix: validate calendar date in fixed-offset due_at

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
This commit is contained in:
nikita.hohlov
2026-09-04 06:52:28 -03:00
parent 7d1df7828a
commit 245ca88af6
2 changed files with 6 additions and 3 deletions

View File

@@ -10,7 +10,7 @@ final class DueAtParser
{ {
private const DATE = '/^(\d{4})-(\d{2})-(\d{2})$/'; private const DATE = '/^(\d{4})-(\d{2})-(\d{2})$/';
private const LOCAL_DATETIME = '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/'; private const LOCAL_DATETIME = '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/';
private const FIXED_DATETIME = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(Z|[+-]\d{2}:\d{2})$/'; private const FIXED_DATETIME = '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(Z|[+-]\d{2}:\d{2})$/';
public static function parse(string $raw): ParsedDue public static function parse(string $raw): ParsedDue
{ {
@@ -30,7 +30,10 @@ final class DueAtParser
); );
} }
if (preg_match(self::FIXED_DATETIME, $raw)) { if (preg_match(self::FIXED_DATETIME, $raw, $m)) {
self::assertDate((int) $m[1], (int) $m[2], (int) $m[3], $raw);
self::assertTime((int) $m[4], (int) $m[5], (int) ($m[6] ?? 0), $raw);
try { try {
return ParsedDue::fixed(CarbonImmutable::parse($raw)); return ParsedDue::fixed(CarbonImmutable::parse($raw));
} catch (\Throwable $e) { } catch (\Throwable $e) {

View File

@@ -43,7 +43,7 @@ class DueAtParserTest extends TestCase
public function test_garbage_is_rejected(): void public function test_garbage_is_rejected(): void
{ {
foreach (['', 'tomorrow', '2026-13-01', '14:00', '2026-09-10 14:00', '2026-09-10T25:00'] as $bad) { foreach (['', 'tomorrow', '2026-13-01', '14:00', '2026-09-10 14:00', '2026-09-10T25:00', '2026-02-30T08:15:00+02:00', '2026-09-10T25:00Z'] as $bad) {
try { try {
DueAtParser::parse($bad); DueAtParser::parse($bad);
$this->fail("accepted: $bad"); $this->fail("accepted: $bad");