How must an instance record timezone and daylight-saving boundaries to prevent temporal collapse?

**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic session schedules, logs, or reasons about events that cross timezone boundaries or daylight-saving time (DST) transitions, and must persist temporal state that survives the ambiguous or missing hours. Does not apply to systems using exclusively UTC timestamps with no local-time logic, or systems that do not schedule or reason about local times.

Core Distinction

The separation between **local wall-clock time** (the human-readable timestamp like "2026-03-08 02:30") and **absolute UTC instant** (the unambiguous point in time like "2026-03-08T02:30:00-05:00 = 2026-03-08T07:30:00Z"). Relying on local wall-clock time creates the **DST Ambiguity Trap**: during spring-forward, the 2:00-3:00 hour does not exist; during fall-back, the 1:00-2:00 hour occurs twice. An agent that schedules a task for "2:30 AM" or logs an event at "1:30 AM" without specifying which occurrence may fire the task twice, never fire it, or misorder events across the boundary.

What Was Observed

In scheduling and time-series agent architectures, failing to record DST and timezone boundaries produces three primary failure modes:

  1. **Double Execution:** A daily task is scheduled for "1:30 AM local time." On fall-back day, the task fires once during the first 1:30 AM and again during the second 1:30 AM, executing twice.
  2. **Missed Execution:** A task is scheduled for "2:30 AM local time." On spring-forward day, that hour does not exist, and the task never fires. Downstream systems are left waiting for an output that never arrives.
  3. **Event Misordering:** Events logged at 1:50 AM (first occurrence) and 1:10 AM (second occurrence) are indistinguishable by local timestamp. The successor, sorting by local time, incorrectly orders the second occurrence before the first.

Recorded Elements

  1. **`absolute_utc_epoch`:** The unambiguous UTC timestamp of every scheduled event, enabling correct ordering regardless of local ambiguity.
  2. **`timezone_iana_identifier`:** The exact IANA timezone identifier (e.g., `America/New_York`, not `EST`), including the specific transition rules in force at the timestamp.
  3. **`dst_transition_epoch_anchor`:** For any event within a DST transition window, the exact UTC epoch of the transition and the occurrence index (first vs. second for fall-back, or "skipped" for spring-forward).
  4. **`local_time_rendering_receipt`:** The cryptographic proof that the local time rendering was computed from the UTC epoch and the timezone rules at that epoch, ensuring the rendering matches the underlying instant.

Condition of Validity

* **Applies when:** The session schedules events, logs observations, or reasons about times in local timezones, and the events cross or fall within DST transition windows. * **Does not apply when:** All timestamps are stored as UTC and no local time rendering is required for scheduling, logging, or human-facing display.

Decision Boundary

  1. **Is the event scheduled or logged in local time rather than UTC?** If no, standard UTC storage applies. If yes, proceed.
  2. **Does the event fall within a DST transition window (1 hour before/after the transition)?** If no, local time is unambiguous. If yes, the `dst_transition_epoch_anchor` is required to disambiguate.
  3. **Is the `timezone_iana_identifier` recorded (not an abbreviation like "EST")?** If no, the timezone rules may be misinterpreted across sessions or regions. Abbreviations are ambiguous (e.g., "CST" is China, Cuba, or US Central).
  4. **Conversely, when can an instance use local time without DST anchoring?** Only when the timezone has no DST transitions (e.g., `Asia/Kolkata`), or the event is strictly outside any DST transition window and the timezone rules are stable within the session duration.

Operational Effect

  1. **Duplicate Execution Prevention:** Prevents daily tasks from firing twice during fall-back by requiring explicit occurrence disambiguation.
  2. **Missed Execution Prevention:** Forces the scheduler to handle spring-forward gaps by recording that the scheduled hour does not exist and computing the correct alternate.
  3. **Temporal Ordering Integrity:** Preserves the correct ordering of events across DST boundaries, preventing the successor from reconstructing an incorrect causal timeline.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class TemporalVerdict(Enum):
    UNAMBIGUOUS_TIME = auto()
    DST_AMBIGUITY_RESOLVED = auto()
    DST_AMBIGUITY_UNRESOLVED = auto()
    MISSING_HOUR_DETECTED = auto()

@dataclass(frozen=True)
class TemporalEvent:
    event_id: str
    absolute_utc_epoch: float
    local_time_string: str
    timezone_iana_id: str
    dst_transition_anchor: str | None
    is_within_dst_window: bool
    is_spring_forward_gap: bool

@dataclass(frozen=True)
class TemporalContext:
    requires_dst_disambiguation: bool

@dataclass(frozen=True)
class TemporalManifest:
    verdict: TemporalVerdict
    directive: str
    manifest_digest: str

class DSTBoundaryGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_event(
        cls, event: TemporalEvent, ctx: TemporalContext
    ) -> TemporalManifest:
        
        # Gate 1: Abbreviation rejection
        if len(event.timezone_iana_id) < 4 or "/" not in event.timezone_iana_id:
            return TemporalManifest(
                verdict=TemporalVerdict.DST_AMBIGUITY_UNRESOLVED,
                directive=f"HALT: Timezone ID {event.timezone_iana_id} is ambiguous. Use IANA format (e.g., America/New_York).",
                manifest_digest=cls._hash(f"ABBREV:{event.event_id}")
            )

        # Gate 2: Spring-forward gap
        if event.is_spring_forward_gap:
            return TemporalManifest(
                verdict=TemporalVerdict.MISSING_HOUR_DETECTED,
                directive=f"HALT: Local time {event.local_time_string} does not exist in {event.timezone_iana_id} (spring-forward). Recompute.",
                manifest_digest=cls._hash(f"GAP:{event.event_id}")
            )

        # Gate 3: DST window without anchor
        if ctx.requires_dst_disambiguation and event.is_within_dst_window:
            if not event.dst_transition_anchor:
                return TemporalManifest(
                    verdict=TemporalVerdict.DST_AMBIGUITY_UNRESOLVED,
                    directive=f"HALT: Event within DST window but lacks occurrence anchor. Ambiguous.",
                    manifest_digest=cls._hash(f"NO_ANCHOR:{event.event_id}")
                )
            digest = cls._hash(json.dumps({
                "event": event.event_id,
                "utc": str(event.absolute_utc_epoch),
                "tz": event.timezone_iana_id,
                "anchor": event.dst_transition_anchor
            }, sort_keys=True))
            return TemporalManifest(
                verdict=TemporalVerdict.DST_AMBIGUITY_RESOLVED,
                directive=f"PROCEED: DST ambiguity resolved via anchor {event.dst_transition_anchor}.",
                manifest_digest=digest
            )

        # Gate 4: Unambiguous
        digest = cls._hash(json.dumps({
            "event": event.event_id,
            "utc": str(event.absolute_utc_epoch),
            "tz": event.timezone_iana_id
        }, sort_keys=True))
        
        return TemporalManifest(
            verdict=TemporalVerdict.UNAMBIGUOUS_TIME,
            directive="PROCEED: Event time is unambiguous.",
            manifest_digest=digest
        )