ZoneRules

ZoneRules :: # (opaque)

Immutable finite rules supplied by the caller; no registry or host lookup.

Example

Supply rules explicitly: use from_database with the optional zone-data package, or construct a bounded snapshot for an application's own clock schedule. Offsets are local minus timeline in seconds. A rules snapshot only interprets labels inside its declared validity; it never falls back to the machine's zone.

For an ambiguous label, RequireUnique returns an error; First, Last, or MatchingOffset select an occurrence. classification_cursor separates bounded interpretation from choosing a result. select instead finds all coverage of a local-label range, which may be disconnected.

import time.ZoneRules
import time.FixedOffset
import time.PosixSpan
import time.PosixBoundary
import time.GregorianDate
import time.Calendar.Date
import time.ClockTime
import time.LocalDateTime

expect {
    validity = PosixSpan.from_seconds(-86400, 86400, RejectSubmicrosecond)?
    rules = ZoneRules.new_bounded(
        "UTC", "fixed", validity,
        FixedOffset.from_seconds(0), [],
        { minimum: 0, maximum: 0 },
    )?
    date = GregorianDate.from_fields({ year: 1970, month: 1, day: 1 })?
    clock = ClockTime.from_microseconds_since_midnight(0)?
    local = LocalDateTime.new(Calendar.Date.from_gregorian(date), clock)
    resolved = ZoneRules.resolve_occurrence(rules, local, RequireUnique)
    resolved == Ok(PosixBoundary.from_microseconds(0))
}

When a batch returns Limited, resume the returned cursor with sufficient budgets. The cursor retains partial work; the batch exposes only complete classifications or coverage. Increase the capacity after a buffer limit.

calendar_selection_cursor : ZoneRules, Value -> Try(SelectionCursor, [OutOfRange, EmptySelection, ReversedSelection, OutsideValidity, ..])

Resolve a complete calendar selection through the shared bounded cursor. Construction does not scan transitions; collect applies explicit limits. Fold components remain disconnected and gaps may contribute no coverage.

new : Str, Str, PosixSpan, FixedOffset, List(Transition) -> Try(ZoneRules, [EmptyName, EmptyVersion, TransitionOutsideValidity, UnorderedTransitions, InvalidOffsetBounds, OffsetOutsideBounds, ..])

Validate named finite rules using the full I32 offset range as global bounds. Reject empty names/versions or transitions outside validity or out of order. Prefer new_bounded when the provider supplies tighter authoritative bounds: full-range bounds require wider validity for complete inverse queries.

new_bounded : Str, Str, PosixSpan, FixedOffset, List(Transition), OffsetBounds -> Try(ZoneRules, [EmptyName, EmptyVersion, TransitionOutsideValidity, UnorderedTransitions, InvalidOffsetBounds, OffsetOutsideBounds, ..])

Bounds are a provider guarantee, including outside this finite table. Never infer global bounds merely from the offsets observed in the table.

definition : ZoneRules -> Definition

Constant-time projection. Transition/string storage may remain shared; names and version labels are not substitutes for the returned actual data.

from_definition : Definition -> Try(ZoneRules, [EmptyName, EmptyVersion, TransitionOutsideValidity, UnorderedTransitions, InvalidOffsetBounds, OffsetOutsideBounds, MissingProvenance, ProvenanceNameMismatch, InvalidDatabaseAlignment, ..])

Construct rules from a complete definition, validating names, ordering, validity, offset bounds and provenance in O(n) transition work. The table may remain shared. DatabaseSource requires second-aligned boundaries; Supplied accepts exact microseconds. No provider lookup or authentication of the declared database source or digest occurs.

from_database : Database -> Try(ZoneRules, [UnsupportedSchema(U16), UnsupportedAxis(Str), UnsupportedFutureHandling(Str), MissingProvenance, EmptyName, EmptyVersion, TransitionOutsideValidity, UnorderedTransitions, InvalidOffsetBounds, OffsetOutsideBounds, EmptySpan, ReversedBounds, OutOfRange, ..])

Validate structural zone-data input and retain its database provenance. Reject unsupported schema, axis or future handling rather than guessing their meaning; no provider lookup occurs.

provenance : ZoneRules -> Provenance

Return the retained supplied/database origin metadata without a provider lookup.

name : ZoneRules -> Str

Return the rules snapshot name; equal names do not imply equal transition data.

version : ZoneRules -> Str

Return the supplied version label; it does not identify the rules by itself.

validity : ZoneRules -> PosixSpan

Return the half-open POSIX span over which the transition table is authoritative.

offset_bounds : ZoneRules -> OffsetBounds

Authoritative global offset bounds supplied at construction, not merely the extrema observed inside this finite transition table.

offset_at : ZoneRules, PosixBoundary -> Try(FixedOffset, [OutsideValidity, ..])

A transition's new offset applies at its exact boundary.

project : ZoneRules, PosixBoundary, Calendar -> Try({ local : LocalDateTime, offset : FixedOffset }, [OutsideValidity, OutOfRange, ..])

Project a resolved boundary into an explicitly selected calendar. Returns its local label and actual offset with one offset lookup. A fold needs no occurrence policy here: the supplied boundary selects the instant. Cost is the existing linear transition lookup plus scalar projection.

resolve : ZoneRules, LocalDateTime -> Try(Resolution, [OutsideValidity, OutOfRange, ..])

Complete classification only when every possible candidate is covered.

classification_cursor : ZoneRules, LocalDateTime -> Try(ClassificationCursor, [OutsideValidity, OutOfRange, ..])

Bind a civil label to rules after proving the full inverse range fits validity. Construction does not scan transitions; collect performs bounded work. Return OutsideValidity or OutOfRange instead of an incomplete classification.

resolve_occurrence : ZoneRules, LocalDateTime, OccurrencePolicy -> Try(PosixBoundary, [Gap, Ambiguous, OffsetConflict, OutsideValidity, OutOfRange, ..])

Choose explicitly; gaps never silently move to another local label.

appointment : ZoneRules, LocalDateTime, OccurrencePolicy, LocalDateTime, OccurrencePolicy -> Try(PosixSpan, [Gap, Ambiguous, OffsetConflict, OutsideValidity, OutOfRange, EmptySpan, ReversedBounds, ..])

Appointment between independently chosen occurrences, not a selection.

select : ZoneRules, LocalDateTime, LocalDateTime -> Try(Coverage, [EmptySelection, ReversedSelection, OutsideValidity, OutOfRange, ..])

Preimage of the half-open local selection; never an endpoint hull.

selection_cursor : ZoneRules, LocalDateTime, LocalDateTime -> Try(SelectionCursor, [EmptySelection, ReversedSelection, OutsideValidity, OutOfRange, ..])

Prove the full inverse range is covered before any segment is evaluated. The opaque cursor binds the immutable rules and original local selection.

Database : {
    schema : U16,
    axis : Str,
    requested_name : Str,
    canonical_name : Str,
    source_version : Str,
    source_digest : Str,
    profile : Str,
    future_handling : Str,
    start_second : I64,
    end_second : I64,
    initial_offset : I32,
    minimum_offset : I32,
    maximum_offset : I32,
    transitions : List({ second : I64, offset : I32 }),
}

Structural input from the optional zone-data package. Boundaries and transitions are POSIX seconds; offsets are signed seconds. from_database validates schema, axis, future handling, provenance and finite table bounds.

Provenance : [Supplied, DatabaseSource({ requested_name : Str, canonical_name : Str, source_digest : Str, profile : Str })]

Caller-supplied rules or database origin metadata. Database metadata records the requested alias, canonical name, source digest and extraction profile.

Transition : { at : PosixBoundary, offset : FixedOffset }

An exact POSIX boundary and the new offset active from that boundary onward.

OffsetBounds : { minimum : I32, maximum : I32 }

Provider-guaranteed minimum/maximum offsets in seconds, including outside the finite table. These bounds establish whether inverse queries are complete.

Definition : { name : Str, version : Str, validity : PosixSpan, initial : FixedOffset, bounds : OffsetBounds, provenance : Provenance, transitions : List(Transition) }

A complete rules definition for inspection or reconstruction with from_definition. Boundaries and transition positions retain exact POSIX microseconds; offsets/bounds retain whole seconds. Provenance is preserved in full.

Resolution : [Gap, Unique(PosixBoundary), Fold(List(PosixBoundary))]

Complete inverse classification: no occurrence, one occurrence, or multiple occurrences in increasing POSIX order. A gap is not an adjusted occurrence.

OccurrencePolicy : [RequireUnique, First, Last, MatchingOffset(FixedOffset)]

Require one occurrence, select the earliest/latest, or require the stated offset. These choices never silently adjust a gap.

GapTransition : { at : PosixBoundary, before : FixedOffset, after : FixedOffset }

A forward transition and its offsets before/after, retained as evidence of a skipped local interval containing the classified label.

BoundaryChoice : { boundary : PosixBoundary, adjustment : [Exact, BeforeGap(GapTransition)] }

A chosen POSIX coordinate and whether it was exact or used a pre-gap offset. An adjustment retains the transition that justified it.

Classification

ZoneRules.Classification :: # (opaque)

Complete inverse evidence for one civil label under retained immutable rules. Inspect resolution or choose an occurrence using explicit policies.

resolution : Classification -> Resolution

Return the complete gap/unique/fold result without rescanning transitions.

rules : Classification -> ZoneRules

Return the exact rules used to classify the label.

local : Classification -> LocalDateTime

Return the original civil label being classified.

gap_transitions : Classification -> List(GapTransition)

Forward transitions whose skipped local interval contains the label. A synthetic table can contain several; never silently select one.

choose : Classification, { occurrence : OccurrencePolicy, gap : [RejectGap, UseOffsetBeforeGap] } -> Try(BoundaryChoice, [Gap, Ambiguous, AmbiguousGap, OffsetConflict, OutOfRange, ..])

Apply explicit policies to complete evidence, without rescanning rules. First/last/gap choice is constant work; matching an asserted offset is O(log n) in fold occurrences. Gap adjustment retains its transition. MatchingOffset checks the pre-gap offset for an explicit gap adjustment.

to_inspect : Classification -> Str

Summarize the label, result kind and evidence counts without listing candidates.

ClassificationLimits : { max_segments : U64, max_candidates : U64 }

Per-call segment-work budget and total retained candidate capacity. Capacity counts both matching occurrences and forward-gap evidence.

ClassificationBatch : {
    segments : U64,
    buffered : U64,
    status : [Complete(Classification), Limited({ cursor : ClassificationCursor, reason : [WorkLimit, BufferLimit] })],
}

Segment work for this call, total buffered evidence, and a complete classification or resumable cursor. Limited is not a gap classification.

ClassificationCursor

ZoneRules.ClassificationCursor :: # (opaque)

Immutable progress toward complete classification, retaining candidates and gap evidence. Resume a Limited batch by collecting its returned cursor.

collect : ClassificationCursor, ClassificationLimits -> Try(ClassificationBatch, [OutOfRange, ..])

One work unit visits one constant-offset segment and its ending transition. Capacity includes matches and forward-gap evidence. Shared cursor snapshots may copy retained buffers on append.

to_inspect : ClassificationCursor -> Str

Summarize retained progress and evidence counts without continuing classification.

SelectionLimits : { max_segments : U64, max_members : U64 }

Per-call segment-work budget and total capacity for canonical coverage members. A resume may need a larger member cap to pass a BufferLimit.

SelectionBatch : {
    segments : U64,
    buffered : U64,
    status : [Complete(Coverage), Limited({ cursor : SelectionCursor, reason : [WorkLimit, BufferLimit] })],
}

Per-call segment work and retained member count, with complete coverage or a resumable cursor. Incomplete coverage is not exposed as a successful value.

SelectionCursor

ZoneRules.SelectionCursor :: # (opaque)

Immutable progress through the complete preimage of a half-open civil range. Fold components remain separate and gaps can contribute no coverage.

context : SelectionCursor -> { rules : ZoneRules, start : LocalDateTime, end : LocalDateTime }

Return the exact retained rules and original civil bounds without evaluating them.

collect : SelectionCursor, SelectionLimits -> Try(SelectionBatch, [OutOfRange, ..])

One segment unit performs fixed offset arithmetic and one ordered builder append. Rejected/empty segments still consume work. Shared output appends can copy up to max_members retained spans. Zero budgets return Limited; no incomplete coverage is exposed as a complete value.

to_inspect : SelectionCursor -> Str

Summarize cursor progress and buffered count without evaluating more segments.