ums

ADR-0076: UTC Date Storage, Browser Timezone Detection, and Language Resolution

Promoted to Evolith: This ADR has been elevated to Evolith ADR-0072 — UTC Date Storage, Browser Timezone Detection, and Language Resolution. UMS retains this document as implementation reference.

Status: Accepted
Date: 2026-06-02
Context: Evolith Platform — Root Standard (applies to all child systems: UMS, and future products)
Supersedes: None
Related: ADR-0052 (Immutable Audit Trail), ADR-0056 (Clean Architecture Frontend)


Context

Distributed systems that span multiple countries and time zones must handle dates, times, and locale preferences consistently across all layers. Without an explicit standard, individual teams make incompatible choices: some store local times, some use browser-default formatting, some hardcode language identifiers. When data crosses system boundaries or is shown to users in different time zones, these inconsistencies cause display errors, audit discrepancies, and compliance failures.

Three independent concerns must be addressed together because they interact at session initialization:

  1. Date and time storage: Should the database store UTC or local time?
  2. Timezone detection: How does the system know where the user is, and which timezone to apply when displaying timestamps?
  3. Language resolution: Which language should the UI use, and what is the priority chain when multiple sources provide a preference?

All Evolith child systems (current: UMS; future: billing, logistics, port operations, ERP integrations) must follow the same standard so that cross-system audit trails, delegations, and events are interpretable without time-zone ambiguity.


Decisions

D1 — All dates are stored and transmitted as UTC

Rule: Every date or timestamp stored in any database table, domain event, outbox message, or API response body must be UTC.

Rationale: UTC is the only unambiguous anchor for distributed systems. Local times introduce DST gaps, wall-clock ambiguity, and cross-datacenter inconsistency. Storing UTC and converting at display time is the industry standard (IANA, RFC 3339, ISO 8601).


D2 — Browser timezone is detected at session start and stored in the session

Rule: On login, the frontend detects the browser’s IANA timezone using Intl.DateTimeFormat().resolvedOptions().timeZone and stores it in the authenticated session state. It is sent to the backend as the X-Timezone request header on every API call.

Rationale: The browser knows the user’s actual timezone without requiring manual configuration. Defaulting to the tenant timezone handles edge cases (kiosk terminals, SSO sessions from foreign browsers) while still providing a sensible default for most users in the same region as the tenant.


D3 — Language resolution follows a strict priority chain

Rule: The UI language is resolved at session initialization in this order:

Priority Source Mechanism
1 (highest) Browser Accept-Language HTTP header Read by backend CultureMiddleware, validated against supported locales
2 Tenant parameter UI_LANGUAGE_DEFAULT Read from in-memory configuration cache at login
3 (lowest) Platform hardcoded default "es" Final fallback

Rationale: Browser preference reflects what the user has configured at the OS level. Respecting it requires no manual configuration and covers the majority of cases. The tenant admin can control the fallback for environments where browser settings are not representative (shared kiosks, managed devices).


Consequences

Positive

Negative / Trade-offs


Implementation Checklist (per child system)


References