Hi! I've been writing spec-conformance tests for an AT Protocol project I'm working on, and this SDK is what I cross-check my results against, as it's the most mature Python implementation of the protocol, so disagreeing with it is my cue to go digging. On two datetime edges we disagreed, and after arbitrating against the Lexicon spec and the reference datetime.ts, it turned out these look like rare cases where the divergence is on this side (same digging that surfaced #695 and #696). Filing as an issue rather than a PR because the fix involves a design choice I didn't want to make for you.
1. Year 0000 is rejected (spec lists it as a preferred valid example)
TypeAdapter(DateTime).validate_python('0000-01-01T00:00:00.000Z', context={'strict_string_format': True})
# ValidationError: Invalid datetime: year 0 is out of range
The Lexicon spec's valid examples include 0000-01-01T00:00:00.000Z under "preferred", and the reference datetime.ts accepts years 0000–9999, rejecting only datetimes that normalize to a negative year (e.g. 0000-01-01T00:00:00+01:00, which is in the interop invalid vectors). The root cause is structural: validate_datetime finishes with datetime.fromisoformat, and Python's datetime.MINYEAR is 1, so year 0000 can't even be represented. A fix probably means either special-casing year 0000 (e.g. substituting a stand-in leap year for the parse, plus an explicit check that an offset doesn't normalize below the year floor) or hand-rolling the semantic checks. Happy to send a PR for whichever direction you prefer, if any (in practice, this probably only bites by rejecting a joke post from year 0000 or something).
2. No maximum length — a design question rather than a clear-cut bug
The reference implementation rejects any datetime longer than 64 characters: datetime.ts#L329-L330. Unlike item 1, this rule appears nowhere in the Lexicon spec text — it exists only in the reference code — so it's genuinely your policy call: should strict mode here conform to the spec as written, or to the de-facto behavior of the network (which runs the reference)? Today validate_datetime has no length check at all, so e.g. a 65-char datetime with a 44-digit fractional-seconds part passes strict validation here but is rejected by the reference, and therefore by live PDS/relay/AppView infrastructure.
If you'd like to match the reference, the exact change I'd be happy to submit as a PR:
MAX_DATETIME_LENGTH: int = 64 alongside the other length constants in string_formats.py
- an early check in
validate_datetime: if len(v) > MAX_DATETIME_LENGTH: raise ValueError(...) with a message mirroring the reference's ("too long (64 chars max)")
- parametrized tests pinning the 64/65-char boundary
One heads-up either way: adding the cap means strict mode starts rejecting over-long datetimes it currently accepts. That behavior change is exactly why I'm asking first rather than sending the PR directly.
Thanks again for the library!
Hi! I've been writing spec-conformance tests for an AT Protocol project I'm working on, and this SDK is what I cross-check my results against, as it's the most mature Python implementation of the protocol, so disagreeing with it is my cue to go digging. On two datetime edges we disagreed, and after arbitrating against the Lexicon spec and the reference
datetime.ts, it turned out these look like rare cases where the divergence is on this side (same digging that surfaced #695 and #696). Filing as an issue rather than a PR because the fix involves a design choice I didn't want to make for you.1. Year 0000 is rejected (spec lists it as a preferred valid example)
The Lexicon spec's valid examples include
0000-01-01T00:00:00.000Zunder "preferred", and the referencedatetime.tsaccepts years 0000–9999, rejecting only datetimes that normalize to a negative year (e.g.0000-01-01T00:00:00+01:00, which is in the interop invalid vectors). The root cause is structural:validate_datetimefinishes withdatetime.fromisoformat, and Python'sdatetime.MINYEARis 1, so year 0000 can't even be represented. A fix probably means either special-casing year 0000 (e.g. substituting a stand-in leap year for the parse, plus an explicit check that an offset doesn't normalize below the year floor) or hand-rolling the semantic checks. Happy to send a PR for whichever direction you prefer, if any (in practice, this probably only bites by rejecting a joke post from year 0000 or something).2. No maximum length — a design question rather than a clear-cut bug
The reference implementation rejects any datetime longer than 64 characters: datetime.ts#L329-L330. Unlike item 1, this rule appears nowhere in the Lexicon spec text — it exists only in the reference code — so it's genuinely your policy call: should strict mode here conform to the spec as written, or to the de-facto behavior of the network (which runs the reference)? Today
validate_datetimehas no length check at all, so e.g. a 65-char datetime with a 44-digit fractional-seconds part passes strict validation here but is rejected by the reference, and therefore by live PDS/relay/AppView infrastructure.If you'd like to match the reference, the exact change I'd be happy to submit as a PR:
MAX_DATETIME_LENGTH: int = 64alongside the other length constants instring_formats.pyvalidate_datetime:if len(v) > MAX_DATETIME_LENGTH: raise ValueError(...)with a message mirroring the reference's ("too long (64 chars max)")One heads-up either way: adding the cap means strict mode starts rejecting over-long datetimes it currently accepts. That behavior change is exactly why I'm asking first rather than sending the PR directly.
Thanks again for the library!