UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

148 lines • 7.35 kB
/** * time/zone — IANA zone names and offset arithmetic, through `Intl`. * * Pattern: the platform's tz database behind two small questions — "what is * the offset of this zone at this instant?" and "which instant is * this wall time in this zone?" — with Temporal's four DST words for * the second. No dependency: `Intl.DateTimeFormat` carries the * database the runtime ships. * Role: L0 leaf (the time layer's one owner). Imports `instant.ts` only. * Emits: N/A. * * ## A zone is an IANA name, never an abbreviation * * `Intl` is more forgiving than this layer: it accepts `PST` (and reads it as * `America/Los_Angeles`, whose October offset is -07:00, not PST's -08:00), * `EST` (as `America/Panama`), and a bare offset such as `+05:30`. Each of * those turns a person's words into a zone without saying so, so * {@link isZoneName} refuses them: a name is accepted when it has an * `Area/Location` shape (`America/Los_Angeles`, `Etc/GMT+5`) or is `UTC` / * `GMT`, AND `Intl` knows it. An abbreviation reaches a zone only through a * recorded map (a later step), never here. * * ## Cost — one `formatToParts` per (zone, instant), memoised * * Asking `Intl` is the whole cost of this module (a `formatToParts` is * microseconds; everything else is arithmetic), and a resolution asks the * same instants again: every candidate a DST probe validates is then spelled * with its offset. So the formatter is built once per zone and the reading of * each (zone, instant) is kept — the tz database cannot change inside a * process — in a bounded memo cleared when full, like the formatter cache. * `readWall` also skips the one probe it can prove redundant: when the offset * a day before, at, and a day after the wall time agree, no change falls in * that window, so the one candidate needs no check. What this buys is pinned * as a COUNT by the performance case of test/time/resolve.test.ts, not a * duration. * * ## DST — the four words * * | Wall time | `compatible` | `earlier` | `later` | `reject` | * |-----------|--------------|-----------|---------|----------| * | exists once | it | it | it | it | * | exists twice (fall back, `01:30`) | the first | the first | the second | `undefined` | * | never exists (spring forward, `02:30`) | shifted forward by the gap (`03:30`) | shifted back (`01:30`) | shifted forward | `undefined` | * * @example * ```ts * isZoneName('America/Los_Angeles'); // true * isZoneName('PST'); // false — an abbreviation * offsetAt('America/Los_Angeles', Date.parse('2026-10-09T15:00:00Z')); // -420 * readWall({ year: 2026, month: 11, day: 1, hour: 1, minute: 30 }, 'America/Los_Angeles'); * // { kind: 'overlap', earlier: …08:30Z, later: …09:30Z } * ``` */ /** An IANA zone name, checked through `Intl`. Never an abbreviation, never a bare offset. */ export type ZoneName = string; /** A wall-clock reading with no zone. `second` and `millisecond` default to 0. */ export interface WallTime { readonly year: number; readonly month: number; readonly day: number; readonly hour: number; readonly minute: number; readonly second?: number; readonly millisecond?: number; } /** Temporal's four words for a wall time a DST change doubles or skips. */ export type Disambiguation = 'compatible' | 'earlier' | 'later' | 'reject'; /** What a wall time names in a zone: one instant, two (fall back), or none (spring forward). */ export type WallReading = { readonly kind: 'unique'; readonly ms: number; } | { readonly kind: 'overlap'; readonly earlier: number; readonly later: number; } | { readonly kind: 'gap'; readonly earlier: number; readonly later: number; }; /** Whether `value` is an IANA zone name this runtime's tz database knows — see the module header for what is refused. */ export declare function isZoneName(value: unknown): value is ZoneName; /** The runtime's canonical spelling of a zone (`america/los_angeles` → `America/Los_Angeles`), or `undefined` when it is not a zone name. */ export declare function canonicalZone(value: unknown): ZoneName | undefined; /** The wall time an instant shows in `zone` (milliseconds kept). Frozen: the reading is shared through the memo. */ export declare function wallAt(zone: ZoneName, ms: number): WallTime; /** * The offset of `zone` at the instant `ms`, in minutes east of UTC * (`America/Los_Angeles` in October: -420). A historical local-mean-time * offset can be a fraction of a minute. */ export declare function offsetAt(zone: ZoneName, ms: number): number; /** Whether a wall time's fields name a real calendar time (any zone aside). */ export declare function isWallTime(value: unknown): value is WallTime; /** * What `wall` names in `zone`: one instant, two (a fall-back overlap: * `earlier`, `later`), or none (a spring-forward gap: `earlier` is the wall * time read with the offset after the change, `later` with the offset before * it — Temporal's two readings). Throws a `TypeError` on a wall time that is * not a calendar time or a zone that is not a zone name. */ export declare function readWall(wall: WallTime, zone: ZoneName): WallReading; /** * The instant `wall` names in `zone` under `disambiguation` (the module * table), or `undefined` under `'reject'` when the wall time is doubled or * skipped. */ export declare function wallToInstant(wall: WallTime, zone: ZoneName, disambiguation: Disambiguation): number | undefined; /** * The tz database version this runtime's `Intl` carries — Node names it * (`process.versions.tz`, e.g. `'2025b'`); any other runtime answers * `'unknown'`. Recorded on every `time-reading` row: zone data changes * between releases, so a reading is re-derivable only with it. */ export declare function tzdataVersion(): string; /** * The ONE zone a place name names in this runtime's tz database, or * `undefined` when it names none or more than one. `London` → `Europe/London`, * `New York` → `America/New_York`; `India`, `Pacific`, `Eastern` → `undefined` * (no zone ends in them — a region is not a place). Looked up two ways, both * the tz database's own data: the zones whose last segment is the place, and * `Area/Place` under each geographic area (which admits a renamed city's link, * `Kyiv`). Every hit is compared by its canonical name, so a link and its * target are one zone. Memoised; the runtime's database does not change. * * @example * ```ts * zoneOfPlace('London'); // 'Europe/London' * zoneOfPlace('New York'); // 'America/New_York' * zoneOfPlace('India'); // undefined — asked, never guessed * ``` */ export declare function zoneOfPlace(place: string): ZoneName | undefined; /** * The IANA zone that is a fixed offset, for a whole-hour offset the tz * database names (`Etc/GMT+8` is −08:00 — POSIX signs, inverted), else * `undefined`. How a window read at a literal offset, or typed with one no * other zone shows, names its zone without claiming a place's. * * @example * ```ts * fixedOffsetZone(-480); // 'Etc/GMT+8' * fixedOffsetZone(0); // 'UTC' * fixedOffsetZone(330); // undefined — no Etc zone for +05:30 * ``` */ export declare function fixedOffsetZone(minutes: number): ZoneName | undefined; //# sourceMappingURL=zone.d.ts.map