Skip to content

Implement fn:parse-ietf-date with a hand-written parser#154

Open
ajbufort wants to merge 1 commit into
Paligo:mainfrom
ajbufort:parse-ietf-date
Open

Implement fn:parse-ietf-date with a hand-written parser#154
ajbufort wants to merge 1 commit into
Paligo:mainfrom
ajbufort:parse-ietf-date

Conversation

@ajbufort

Copy link
Copy Markdown

Fixes #36.

The previous implementation delegated to chrono's parse_from_rfc2822, which rejects most of the formats the spec requires (the asctime layout, two-digit years, named timezones, single-digit fields, liberal whitespace) and accepts some it forbids (three-digit years). 51 conformance tests were filtered as known failures.

Following the direction in #36, this replaces it with a hand-written recursive descent parser for the exact grammar in F&O 3.1 section 9.9.1, adding no dependencies to xee-interpreter.

What's covered

  • Both date layouts (datespec S time and asctime), optional/ignored dayname, comma and hyphen separator forms
  • Two-digit years (+1900), four-digit years taken as given (e.g. 0070)
  • Named timezones (UT/UTC/GMT/EST/...), offsets with and without colons (-05, -05:, -5:00, -500, -0500), the parenthesized-and-ignored tzname after an offset, and the +/-14:00 range check
  • Fractional seconds, 24:00[:00] rolling over to the next day, calendar validation (Feb 29, day 00/32), with FORG0010 on any violation
  • Only x09|x0A|x0D|x20 count as whitespace, per the grammar's S production

Testing

  • New unit tests in library/ietf_date.rs cover the spec examples, the whole conformance truth table, and edge cases the suite doesn't reach (offset boundary +14:00/+14:01, half-hour offsets in every lexical form, tab/CR/LF whitespace, 24:00:00.5 and a nonzero fraction beyond nanosecond precision, leap seconds, non-ASCII whitespace at the edges and interior, lexical year extremes 0001/9999 including 24:00 rollover)
  • A small deterministic mutation-fuzz test (50k inputs derived from valid examples) guards against panics on arbitrary input
  • All 105 tests in fn/parse-ietf-date.xml and the 5 fo-test-fn-parse-ietf-date tests in misc now pass: the XPath check run goes from 20221 to 20272 passed, filters updated via the update/check flow from hacking.md, no regressions in the XPath or XSLT suites (verified in release and in debug mode, as CI runs it)
  • cargo fmt, cargo clippy --all-targets --all-features -- -D warnings, and cargo test are all clean

Notes

  • The old stub trim()ed all Unicode whitespace before parsing; this parser accepts exactly the grammar's S set (x09|x0A|x0D|x20) at the edges, so e.g. a trailing U+00A0 is now FORG0010 where it previously parsed. I read the spec's whitespace note as deliberate about those four characters, so the stricter behavior is pinned by tests - easy to relax if you'd rather keep the lenient trim.
  • parse-ietf-date("Dec 31 24:00 9999") correctly yields the year-10000 xs:dateTime, which exposes a pre-existing serializer quirk: five-digit years print with a leading + (+10000-...), which the engine's own xs:dateTime cast then rejects. That's reachable on main via xs:dateTime("10000-01-01T00:00:00Z") too - happy to file it separately.

The parser lives in its own module (xee-interpreter/src/library/ietf_date.rs) with parse_ietf_date in datetime.rs delegating to it - happy to restructure if you'd prefer it inline or elsewhere.

Generated with Claude Code

The previous implementation delegated to chrono's parse_from_rfc2822,
which rejects most of the formats the spec requires (asctime layout,
two-digit years, named timezones, single-digit fields, liberal
whitespace) and accepts some it forbids (three-digit years). 51
conformance tests were filtered as known failures.

Following the direction in Paligo#36, this is a hand-written recursive
descent parser for the exact grammar in XPath F&O 3.1 section 9.9.1,
adding no parser-library usage to xee-interpreter.

Two deliberate details:

- Edge whitespace is exactly the grammar's S set (x09/x0A/x0D/x20);
  the old stub trimmed all Unicode whitespace, so e.g. a trailing
  U+00A0 now raises FORG0010. Pinned by tests.

- 24:00 requires a completely zero time; a nonzero fractional digit
  beyond nanosecond precision still counts as nonzero.

All 105 tests in fn/parse-ietf-date.xml and the 5
fo-test-fn-parse-ietf-date tests in misc now pass: the XPath
conformance check goes from 20221 to 20272 passed, with the filters
updated and no regressions in the XPath or XSLT suites. A
deterministic mutation-fuzz test guards against panics on arbitrary
input.

Fixes Paligo#36

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

parse-ietf-date improvements

1 participant