"""UTC ISO-8601 timestamp helpers (E-CC-HELPERS / AZ-264 / AZ-508 + AZ-526). Single Layer-1 source for the FDR record ``ts`` envelope strings across every component. Two variants: - :func:`iso_ts_now` — uses :func:`datetime.now` (wall clock); 6-digit microsecond precision; for components that don't have a ``Clock`` injected (the c6_tile_cache and c7_inference FDR producers). - :func:`iso_ts_from_clock` — uses an injected :class:`Clock` Protocol; 9-digit nanosecond precision; for components that already accept a ``Clock`` constructor parameter (the c2_vpr strategies, the FAISS bridge, the C12 operator-reloc service, and the future C4 / C5 batches). Both produce RFC 3339 UTC with a ``Z`` suffix, matching the canonical FDR ``ts`` fixture in ``tests/unit/test_az272_fdr_record_schema.py`` (``_TS = "2026-05-11T00:00:00.000000Z"``). Changing the format would alter FDR record bit-shape and is explicitly out of scope. Producers that need a Clock-injected payload field (e.g. ``age_seconds`` derived from an injected wall-clock for testability) MUST NOT route through these helpers — they are purely metadata about WHEN the FDR record envelope itself was emitted. """ from __future__ import annotations from datetime import datetime, timezone from typing import TYPE_CHECKING if TYPE_CHECKING: from gps_denied_onboard.clock import Clock __all__ = ["iso_ts_from_clock", "iso_ts_now"] def iso_ts_now() -> str: """Return an RFC 3339 UTC timestamp with microsecond precision and a ``Z`` suffix. Format: ``YYYY-MM-DDTHH:MM:SS.ffffffZ`` (fixed-width, lexicographically monotonic under a non-decreasing wall clock). """ return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ") def iso_ts_from_clock(clock: Clock) -> str: """Return an RFC 3339 UTC timestamp built from ``clock.time_ns()``. Format: ``YYYY-MM-DDTHH:MM:SS.fffffffffZ`` (9-digit nanosecond precision, ``Z`` suffix). Use when the timestamp must follow an injectable :class:`~gps_denied_onboard.clock.Clock` (replay tests, deterministic fixtures); use :func:`iso_ts_now` otherwise. """ ns = int(clock.time_ns()) seconds, fraction_ns = divmod(ns, 1_000_000_000) dt = datetime.fromtimestamp(seconds, tz=timezone.utc) return f"{dt.strftime('%Y-%m-%dT%H:%M:%S')}.{fraction_ns:09d}Z"