clockworktzTimezones that get the weird zones right.
A zero-dependency timezone library for JavaScript and TypeScript. Half-hour and 45-minute offsets, 30-minute DST, skipped days, negative DST and historical local mean time, with DST gaps and overlaps resolved exactly the way Temporal does. In about 21 kB.
npm install clockworktzimport { fromUTC, convert, format } from 'clockworktz'
fromUTC('2024-07-15T14:00:00Z', 'Australia/Lord_Howe')
// {
// utc: '2024-07-15T14:00:00.000Z',
// zone: 'Australia/Lord_Howe',
// local: '2024-07-16 00:30:00.000',
// offsetMinutes: 630,
// isDST: false,
// abbreviation: '+1030',
// source: 'system-intl'
// }
// Strings without an offset are wall time in the source zone
convert('2024-07-15T16:30:00', 'Asia/Kathmandu', 'America/St_Johns').local
// '2024-07-15 08:15:00.000'
format('2024-07-15T14:00:00Z', 'Pacific/Chatham', 'yyyy-MM-dd HH:mm xxx')
// '2024-07-16 02:45 +12:45'Right now
Eight zones that break naive timezone code
Not every zone is a whole number of hours from UTC, and not every DST shift is one hour. These clocks are live in your browser.
- Australia/Lord_Howe30-minute DST
- Pacific/Chatham+12:45 / +13:45
- Asia/Kathmandu+05:45 all year
- Australia/EuclaUnofficial +08:45
- Pacific/Marquesas−09:30
- Pacific/KiritimatiUTC+14, a day ahead
- America/St_JohnsHalf-hour, with DST
- Antarctica/TrollTwo-hour DST
DST gaps and overlaps
Some local times never happen. Some happen twice.
When clocks spring forward, a wall time is skipped; when they fall back, it repeats. Pick a scenario and a strategy to see how interpretLocal resolves it.
Clocks jump from 02:00 EST to 03:00 EDT, so 02:30 never happens.
interpretLocal('2024-03-10T02:30:00', 'America/New_York', {
disambiguation: 'compatible',
})
// utc: '2024-03-10T07:30:00.000Z'
// local: '2024-03-10 03:30:00.000' (EDT)Why clockworktz
Small, strict and exact
Temporal-exact disambiguation
Skipped and repeated local times resolve with compatible, earliest, latest or reject, exactly as Temporal does.
Host independent
Results never depend on the timezone of the machine running the code. Strings without an offset are never read in the host's zone.
Zero dependencies
The core is built on the platform's Intl API: about 21 kB gzipped, ESM and CommonJS, with complete TypeScript types.
Deterministic tzdb engine
Opt in to a pinned IANA release (30 kB gzipped) for identical results everywhere, with exact DST flags and local mean time.
Signed data updates
Pick up new tzdb releases without redeploying. Updates load only when an Ed25519 signature and SHA-256 hash verify.
Exact transitions & clocks
Find DST transitions to the second, watch for them past the 24.8-day timer limit, and tick clocks on wall-clock boundaries.
Examples
One small API for every timezone job
Convert instants and wall times, format with date-fns-style tokens, find transitions, and drive live clocks. Every output shown is the library's real result.
import { convert, fromUTC } from 'clockworktz'
// 10:00 in London: what is that for everyone else?
const attendees = ['America/Los_Angeles', 'Asia/Kolkata', 'Australia/Sydney']
for (const zone of attendees) {
const { local } = convert('2024-09-12T10:00:00', 'Europe/London', zone, {
format: 'EEE HH:mm',
})
console.log(zone, local)
}
// America/Los_Angeles Thu 02:00
// Asia/Kolkata Thu 14:30
// Australia/Sydney Thu 19:00
// Instants work too: Date, epoch milliseconds, or ISO strings with an offset
fromUTC(new Date('2024-07-15T14:00:00Z'), 'Asia/Kolkata').local
// '2024-07-15 19:30:00.000'
// Fixed offsets are zones as well
fromUTC('2024-07-15T14:00:00Z', '+05:30').local
// '2024-07-15 19:30:00.000'Engines
Use the platform's data, or pin your own
Both engines run the same conversions and are tested against the same fixtures. Switch with one call.
system-intl
DefaultUses the timezone data built into Node.js or the browser (ICU). Nothing to ship; updates arrive with the OS and runtime.
- 0 kB of data
- Always as fresh as the runtime
- isDST derived from January and July offsets
embedded-tzdb
Opt-in · 30 kB gzUses a compiled IANA tzdb release from the clockworktz/tzdb subpath. You only pay for it if you import it.
- Identical results on every platform
- Exact DST flags, abbreviations and local mean time
- Ed25519-signed updates, shared across tabs
Tested
If it's strange, it's in the test suite
Both engines are tested against the same exact fixtures. The embedded data is checked against ICU for every zone from 1970 onwards, and the suite runs under several host timezones and in Chromium, Firefox and WebKit.
- Lord Howe +10:30 / +11
- Chatham +12:45 / +13:45
- Eucla +08:45
- Kathmandu +05:45
- Marquesas −09:30
- Kiritimati +14
- Samoa's skipped day, 2011
- Casablanca's Ramadan DST
- Dublin's negative DST
- Troll's two-hour DST
- Caracas 2016
- Pyongyang 2015 & 2018
- Etc/GMT+5 sign inversion
- Europe/Kiev → Europe/Kyiv
- Local mean time before 1900
- Rules beyond 2100