What
mcp_server/core/temporal.normalize_date_to_iso silently reinterprets a timezone
abbreviation it cannot resolve as UTC, and persists the wrong instant.
Reproduce
$ python -W error::Warning -c "
from mcp_server.core.temporal import normalize_date_to_iso
print(normalize_date_to_iso('8 May 2023 13:56 EST'))"
dateutil emits UnknownTimezoneWarning: tzname EST identified but not understood
and drops the zone; the default=_DATEUTIL_ANCHOR fill (added in the #239 DTZ
family) then supplies UTC. The value written to memories.created_at is
2023-05-08T13:56:00+00:00 — five hours off, with no signal reaching the caller.
Why it was not fixed in the DTZ PR
The obvious inline fix, wrapping the parse in warnings.catch_warnings() +
simplefilter("error", UnknownTimezoneWarning), mutates process-global
filter state and is documented as not thread-safe. Doing that inside a store
write path is worse than the defect it closes. The correct fix is a tzinfos
policy, and choosing one is a real decision (which EST? which CST?), not a
lint cleanup.
Outside the DTZ family's blast radius: no producer in the repo emits a zone
abbreviation in created_at (LoCoMo emits 1:56 pm on 8 May, 2023, LongMemEval
emits 2023/04/10 (Mon) 17:50 — neither states a zone), and no test, build or
lint step that PR triggers reaches the path. Filed per coding-standards §14.3.
Acceptance criteria
- A
created_at carrying an unresolvable timezone abbreviation either resolves
correctly through an explicit tzinfos mapping, or is rejected as
unparseable — never silently re-anchored to UTC.
- Whichever behaviour is chosen emits an actionable signal, and a test asserts
the emission itself (§13 F1), not only a downstream effect.
- No
warnings.catch_warnings() on a store write path.
- A regression test that fails against the current implementation.
What
mcp_server/core/temporal.normalize_date_to_isosilently reinterprets a timezoneabbreviation it cannot resolve as UTC, and persists the wrong instant.
Reproduce
dateutilemitsUnknownTimezoneWarning: tzname EST identified but not understoodand drops the zone; the
default=_DATEUTIL_ANCHORfill (added in the #239 DTZfamily) then supplies UTC. The value written to
memories.created_atis2023-05-08T13:56:00+00:00— five hours off, with no signal reaching the caller.Why it was not fixed in the DTZ PR
The obvious inline fix, wrapping the parse in
warnings.catch_warnings()+simplefilter("error", UnknownTimezoneWarning), mutates process-globalfilter state and is documented as not thread-safe. Doing that inside a store
write path is worse than the defect it closes. The correct fix is a
tzinfospolicy, and choosing one is a real decision (which
EST? whichCST?), not alint cleanup.
Outside the DTZ family's blast radius: no producer in the repo emits a zone
abbreviation in
created_at(LoCoMo emits1:56 pm on 8 May, 2023, LongMemEvalemits
2023/04/10 (Mon) 17:50— neither states a zone), and no test, build orlint step that PR triggers reaches the path. Filed per coding-standards §14.3.
Acceptance criteria
created_atcarrying an unresolvable timezone abbreviation either resolvescorrectly through an explicit
tzinfosmapping, or is rejected asunparseable — never silently re-anchored to UTC.
the emission itself (§13 F1), not only a downstream effect.
warnings.catch_warnings()on a store write path.