Skip to content

Strict datetime validation diverges from spec/reference on two edges: year 0000 and max length #697

Description

@jacoblapenna

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!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions