From e65718e4e62afdda8ec807acb2c99a3a6eeba9cc Mon Sep 17 00:00:00 2001 From: Alex Castillo Date: Tue, 20 Oct 2026 08:20:42 -0400 Subject: [PATCH] Validate the month, day and hour ranges of the date-time format The date-time regex used [01]\d for the month, [0-3]\d for the day and [0-2]\d for the hour, so values such as 2020-19-39T29:00:00Z were accepted, while the leap second 23:59:60Z allowed by RFC 3339 was rejected. Use 01-12, 01-31, 00-23 (also for the offset hours) and 00-60 for the seconds. The day is not checked against the length of the month, and the +hhmm offset form stays accepted. Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.txt | 1 + fastjsonschema/draft04.py | 2 +- tests/test_format.py | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 843ea6f..b896386 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -5,6 +5,7 @@ * Fixed `propertyNames: false` crashing on non-object values * Added type annotations for public API and `py.typed` marker (PEP 561) * Added `message` to `JsonSchemaValuesException` joining messages of all collected errors +* Fixed the `date-time` format accepting months, days and hours out of range (such as `2020-19-39T29:00:00Z`) and rejecting a leap second (`23:59:60Z`) === 2.22.2 (2026-08-15) diff --git a/fastjsonschema/draft04.py b/fastjsonschema/draft04.py index f4640c2..b1345bd 100644 --- a/fastjsonschema/draft04.py +++ b/fastjsonschema/draft04.py @@ -26,7 +26,7 @@ class CodeGeneratorDraft04(CodeGenerator): # vs. 9 ms with a regex! Other modules are also ineffective or not available in standard # library. Some regexps are not 100% precise but good enough, fast and without dependencies. FORMAT_REGEXS = { - 'date-time': r'^\d{4}-[01]\d-[0-3]\d(t|T)[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:[+-][0-2]\d:[0-5]\d|[+-][0-2]\d[0-5]\d|z|Z)\Z', + 'date-time': r'^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])(t|T)(?:[01]\d|2[0-3]):[0-5]\d:(?:[0-5]\d|60)(?:\.\d+)?(?:[+-](?:[01]\d|2[0-3]):[0-5]\d|[+-](?:[01]\d|2[0-3])[0-5]\d|z|Z)\Z', 'email': r'^(?!.*\.\..*@)[^@.][^@]*(?