Skip to content

Commit dc783d4

Browse files
Pin ISO 8601 decoder accepts fractional-second variants
dqlite emits canonical space-separated 6-digit microseconds, but the decoder uses datetime.fromisoformat which is lenient on input shape: 0 / 3 / 6 fractional digits, T or space separator, Z or explicit offset all parse cleanly. A peer client written in Go or C may emit any of those shapes. Pin the accept matrix so a future parser tightening (custom regex, strict validation) can't silently break the no-microsecond case (the canonical whole-second wall-clock form) or the 3-digit millisecond form. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c017d58 commit dc783d4

1 file changed

Lines changed: 47 additions & 0 deletions

File tree

tests/test_iso8601_encoder.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,3 +440,50 @@ def test_decoder_rejects_year_above_python_maxyear(self) -> None:
440440
assert "10000-01-01T00:00:00" in str(ei.value), (
441441
f"DataError must echo original wire text; got {ei.value!s}"
442442
)
443+
444+
445+
class TestIso8601FractionalSecondsVariants:
446+
"""Pin the matrix of fractional-second representations the decoder
447+
accepts. Python's ``datetime.fromisoformat`` is lenient on the
448+
input side: 0 / 3 / 6 fractional digits, ``T`` or space separator,
449+
and ``Z`` / explicit-offset / no-offset are all valid. The dqlite
450+
server emits canonical space-separated 6-digit values, but a peer
451+
client (Go, C, custom) might emit a different shape; the decoder
452+
must accept all of them.
453+
454+
A future change to the parser (custom regex, strict validation)
455+
would silently break the no-µs case which is the canonical wire
456+
form for whole-second wall clocks.
457+
"""
458+
459+
@pytest.mark.parametrize(
460+
("encoded", "expected"),
461+
[
462+
(
463+
"2024-01-01 12:34:56",
464+
datetime.datetime(2024, 1, 1, 12, 34, 56),
465+
),
466+
(
467+
"2024-01-01 12:34:56.000000",
468+
datetime.datetime(2024, 1, 1, 12, 34, 56),
469+
),
470+
(
471+
"2024-01-01 12:34:56.123456",
472+
datetime.datetime(2024, 1, 1, 12, 34, 56, 123456),
473+
),
474+
(
475+
"2024-01-01 12:34:56.123",
476+
datetime.datetime(2024, 1, 1, 12, 34, 56, 123000),
477+
),
478+
(
479+
"2024-01-01T12:34:56.5",
480+
datetime.datetime(2024, 1, 1, 12, 34, 56, 500000),
481+
),
482+
],
483+
)
484+
def test_decoder_accepts_fractional_seconds_variants(
485+
self, encoded: str, expected: datetime.datetime
486+
) -> None:
487+
from dqlitedbapi.types import _datetime_from_iso8601
488+
489+
assert _datetime_from_iso8601(encoded) == expected

0 commit comments

Comments
 (0)