diet-guard/diet_guard/sync_merge/_clock.py
Krzysztof kuhy Rudnicki 42d15b6a91 Grade against 3.14 too, not just run on it
`requires-python` and the CI pin already said 3.14, but ruff, mypy and pylint
were still grading against 3.10 -- three tools judging an interpreter nothing
runs any more, and ruff withholding the pyupgrade fixes that only become safe
at the newer target. `pin_ci_python.py` rewrites exactly these three knobs.

The fixes ruff then had to make are what the stale target was hiding: 18 files
on `datetime.timezone.utc` instead of `datetime.UTC` (UP017), plus ruff's
stable formatter applying PEP 758 -- `except (A, B):` becomes `except A, B:`.
That second one makes the source 3.14-only at the syntax level, which is
exactly what `requires-python = ">=3.14"` already promised; kuhy chose it
deliberately over holding ruff a release back (2026-08-29).

ruff, ruff-format, mypy --strict and pylint all clean at the new target.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
2026-08-29 11:35:06 +02:00

31 lines
1.1 KiB
Python

"""Wall-clock helpers shared by every adapter in this package.
All three adapter modules derive an ``Hlc`` from some ISO timestamp -- an
entry's birth ``time``, a budget's ``t`` edit stamp, a curated food's
``editedAt``. They therefore share one parsing rule, and it lives here rather
than in whichever module happened to be written first: a second copy is how
two adapters start disagreeing about what an unparsable stamp means, which
would silently change merge outcomes rather than raising.
"""
from __future__ import annotations
from datetime import UTC, datetime
_EPOCH = datetime(1970, 1, 1, tzinfo=UTC)
def _wall_time_ms(stamp: str) -> int:
"""Return ``stamp``'s epoch milliseconds, or the epoch when unparsable.
Falling back to the epoch rather than raising keeps a malformed stamp from
dropping the record entirely: it sorts early in a last-writer-wins merge,
so a well-formed copy from any other device wins, which is the outcome the
user wants.
"""
try:
moment = datetime.fromisoformat(stamp)
except ValueError:
moment = _EPOCH
return int(moment.timestamp() * 1000)