7Injectable clock service for registry code.
11Centralize all wall-clock and monotonic reads used by the registry so
12tests can pin time and production code never reads time directly.
16- Define the two-read :class:`Clock` contract.
17- Provide :class:`RealClock` backed by Python timers.
18- Provide :class:`FakeClock` for deterministic tests.
19- Provide wall-time arithmetic helpers so other registry modules do not
20 import time or datetime directly.
32- This is the only module under ``oc_chat.registry`` allowed to import
33 ``time`` or ``datetime``.
34- ``now_wall`` returns explicit RFC3339 UTC ending in ``Z``.
35- ``state_version`` ordering is an integer counter; monotonic timing is
36 available for timeout logic and wall time is only stored as text.
40- oc_diagnostics (structured debug logging via _dbg)
41- time (wall-clock and monotonic nanosecond reads for RealClock)
42- datetime (RFC3339 UTC formatting, parsing and wall-time arithmetic)
44from __future__
import annotations
46from abc
import ABC, abstractmethod
47from dataclasses
import dataclass
48from datetime
import datetime, timedelta, timezone
51from oc_diagnostics
import _dbg
54_dbg(
"OC_CHAT.MAIN",
"module",
"loaded", 4)
58 """Two-read clock contract required by the registry spec."""
62 """Return the current wall time as RFC3339 UTC."""
66 """Return a monotonic nanosecond counter."""
70 seconds, nanoseconds = divmod(value, 1_000_000_000)
71 dt = datetime.fromtimestamp(seconds, tz=timezone.utc).replace(
72 microsecond=nanoseconds // 1_000
74 return dt.isoformat(timespec=
"microseconds").replace(
"+00:00",
"Z")
78 """Return ``wall`` plus ``seconds`` as RFC3339 UTC."""
81 raise ValueError(
"seconds must be non-negative")
82 normalized = wall[:-1] +
"+00:00" if wall.endswith(
"Z")
else wall
83 dt = datetime.fromisoformat(normalized)
85 dt = dt.replace(tzinfo=timezone.utc)
87 dt = dt.astimezone(timezone.utc)
89 (dt + timedelta(seconds=seconds))
90 .isoformat(timespec=
"microseconds")
91 .replace(
"+00:00",
"Z")
96 """Clock backed by Python's wall and monotonic timers."""
102 return time.monotonic_ns()
107 """Pinned clock for deterministic registry tests."""
109 wall: str =
"2026-05-20T18:30:41.123456Z"
110 monotonic_ns: int = 0
int now_monotonic_ns(self)
None set_wall(self, str value)
int now_monotonic_ns(self)
None set_monotonic_ns(self, int value)
int now_monotonic_ns(self)
str _rfc3339_from_time_ns(int value)
str wall_plus_seconds(str wall, int seconds)