66 lines
2.7 KiB
Markdown
66 lines
2.7 KiB
Markdown
|
|
---
|
||
|
|
id: RMGR-ADR-002
|
||
|
|
type: architecture-decision-record
|
||
|
|
title: "Canonical UTC timestamps; local time only at I/O boundaries"
|
||
|
|
status: accepted
|
||
|
|
decided: "2026-08-21"
|
||
|
|
deciders: ["Bernd Worsch"]
|
||
|
|
related:
|
||
|
|
- RMGR-WP-0008
|
||
|
|
- RMGR-WP-0005
|
||
|
|
- docs/observation-command-contracts_v0.1.md
|
||
|
|
---
|
||
|
|
|
||
|
|
# ADR-002: Canonical UTC timestamps; local time only at I/O boundaries
|
||
|
|
|
||
|
|
## Status
|
||
|
|
|
||
|
|
**Accepted** (2026-08-21).
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
Repo Manager emits timestamps into repository records, indexes, mutation
|
||
|
|
meters, events, evidence, and compatibility contracts. Host-local clocks and
|
||
|
|
implicit calendar conversion make identical events serialize differently,
|
||
|
|
create daylight-saving ambiguity, and make deterministic reconciliation harder.
|
||
|
|
|
||
|
|
Users and external systems still need local calendars, wall-clock times, and
|
||
|
|
locale-specific formatting. Those are presentation and ingestion concerns, not
|
||
|
|
stored identity.
|
||
|
|
|
||
|
|
## Decision
|
||
|
|
|
||
|
|
1. Every value representing an **instant** is created, stored, compared, and
|
||
|
|
exchanged in UTC.
|
||
|
|
2. New textual timestamps use RFC 3339/ISO 8601 with the canonical `Z` suffix.
|
||
|
|
Readers may accept legacy explicit offsets and normalize them to UTC before
|
||
|
|
comparison or re-emission. Naive datetimes are rejected at boundaries.
|
||
|
|
3. Database timestamp columns use timezone-aware types and UTC values. Epoch
|
||
|
|
values, where a foreign contract requires them, denote UTC instants.
|
||
|
|
4. Conversion to or from a local timezone, locale, calendar, or wall-clock
|
||
|
|
representation occurs only in UI and I/O adapters. The converted value never
|
||
|
|
replaces the canonical instant.
|
||
|
|
5. Inputs that omit a timezone are invalid unless the specific I/O contract
|
||
|
|
also supplies an explicit timezone. DST gaps and folds must be resolved at
|
||
|
|
that boundary before producing UTC.
|
||
|
|
6. A genuine **date-only domain value**—for example a contractual due date—is
|
||
|
|
not a timestamp and keeps its declared calendar semantics. A date derived
|
||
|
|
from the current instant for repository metadata uses the UTC calendar date.
|
||
|
|
7. Tests, fixtures, logs, filenames, events, and idempotency material follow the
|
||
|
|
same rule; test convenience is not an exception.
|
||
|
|
|
||
|
|
## Enforcement
|
||
|
|
|
||
|
|
- Repo Manager timestamp creation goes through `repo_manager.time`.
|
||
|
|
- Ruff `DTZ` and `UP` rules remain explicitly selected so naive datetime use
|
||
|
|
and non-canonical UTC APIs fail lint even if Ruff defaults change.
|
||
|
|
- Contract tests require UTC-aware values and canonical `Z` serialization.
|
||
|
|
|
||
|
|
## Consequences
|
||
|
|
|
||
|
|
Stored and exchanged instants are byte-stable across hosts. Ordering and
|
||
|
|
reconciliation no longer depend on operator locale or daylight-saving rules.
|
||
|
|
Presentation clients must retain the canonical value and treat local rendering
|
||
|
|
as a reversible view. Legacy offset-bearing timestamps remain readable but are
|
||
|
|
normalized when rewritten.
|