hoangsonww / hoangsonww/Time-Warp-Manipulation-Library
Time-Zone & Calendar Virtualization: `Intl.*`, `Temporal` (polyfill), DST/Offset Drift Simulation
- Dominant language
- TypeScript
- Stars
- 11
- Forks
- 7
- PR merge metrics
- No merged PRs in 30d
Description
**Summary**
Extend time warping beyond `Date` to **time-zone-aware** behavior: virtualize `Intl.DateTimeFormat`, `Intl.RelativeTimeFormat`, and (optionally) the **Temporal polyfill** so tests/simulations can deterministically exercise DST transitions, custom UTC offsets, and clock drift—without mutating the host environment (no global `TZ` side effects).
---
## Why
* Many bugs only appear around **DST boundaries**/offset changes.
* Apps format with `Intl.*` or use **Temporal**; warping `Date` alone isn’t enough.
* CI hosts differ in `TZ`; reproducible tests need a **virtual tz layer**.
---
## Scope
* **Virtual TZ layer**: specify IANA zone (e.g., `"America/New_York"`) or a custom fixed offset (e.g., `UTC+05:45`), independent of host `TZ`.
* **DST simulation**: step over ambiguous/missing local times; expose flags (`isAmbiguous`, `isGap`).
* **Clock drift/skew**: add linear drift (ppm) or step skew (+/- ms) on top of virtual wall clock.
* **`Intl.*` patching**: intercept `new Intl.DateTimeFormat(...).format()` (and `formatToParts`), `Intl.RelativeTimeFormat`, honoring the virtual tz/locale.
* **Temporal (polyfill) hook**: optional adapter so `Temporal.Now.*()` and `ZonedDateTime` use the virtual clock/tz.
* **No global `process.env.TZ` writes**; sandboxed patches only.
---
## API (draft)
```ts
import { installTimeZone, updateTimeZone, uninstallTimeZone } from "time-warp-manipulation/tz";
installTimeZone({
zone: "America/Los_Angeles", // or { fixedOffsetMinutes: -420 }
driftPpm: 0, // e.g., +50 ppm to simulate fast clocks
skewMs: 0, // instantaneous skew applied to wall clock
patch: { intl: true, temporal: true } // temporal hooks optional
});
// Change zone/drift at runtime
updateTimeZone({ zone: "Europe/Berlin", driftPpm: 25 });
// Tear down
uninstallTimeZone();
```
---
## Acceptance Criteria
* `new Date(utcMs)` + `Intl.DateTimeFormat(tz).format()` equals output produced by virtual layer, regardless of host `TZ`.
* Deterministic behavior across DST **gap** (spring forward) and **fold** (fall back); surface `isGap/isAmbiguous`.
* Works in **Node**, **Bun**, and **browser/JSDOM**; graceful no-op where `Intl` lacks zone data.
* Plays nicely with existing **time warp** (no double offsets); documented order of installation.
---
## Tests
* Golden tests for multiple IANA zones across 5+ years (including leap years).
* Property tests around DST transitions (gap/fold windows).
* Drift/skew round-trip: after advancing virtual 1h with +100 ppm drift, expected delta ≈ 3600.36s.
* Temporal polyfill integration tests (`Temporal.Now.zonedDateTimeISO(zone)`).
---
## Implementation Notes
* Use a compact TZ data bundle (e.g., subset of IANA via `@formatjs/intl-timezone` or generated tables) to avoid heavy deps; tree-shake by zone.
* Wrap `Intl.DateTimeFormat.prototype.format` & `formatToParts`; map input instants → virtual local time using our tz table.
* Provide `withVirtualTimeZone(fn)` helper for scoped application in tests.
Contributor guide
Assessment
This issue has not been assessed yet.