Store a timestamp as an instant, such as 2026-04-04T01:30:00Z, and store the relevant IANA zone separately, such as Australia/Sydney. Do not store only “9am local”: daylight-saving transitions make that phrase ambiguous or impossible.
Model the question you are answering
| Domain question | Persist | Display / execute |
|---|---|---|
| When did an event happen? | UTC instant | Convert to viewer's zone |
| When should a meeting recur? | Local rule + IANA zone | Resolve each occurrence |
| What was the original wall time? | Instant + originating zone | Keep both for audit |
new Intl.DateTimeFormat('en-AU', {
timeZone: 'Australia/Sydney', dateStyle: 'medium', timeStyle: 'short'
}).format(new Date('2026-04-04T01:30:00Z'));
Test the awkward hours
When clocks move forward, some local times never occur. When they move back, one local time occurs twice. A scheduler must define which instant it means; silently using the server's zone is not a definition.
An offset like
+10:00is not a timezone. It does not encode the future daylight-saving rules for Sydney, New York, or any other place.
Use IANA names in APIs and database records, display a zone label beside deadlines, and add tests around a transition for every recurring local-time feature. Time zones stop being mysterious once the model carries both the instant and the human rule that produced it.
