Every export of clockworktz and clockworktz/tzdb, grouped by purpose. Types are TypeScript; everything is available from both ESM and CommonJS.
type DateInput = string | Date | number // ISO string with offset, Date, or epoch ms
type Disambiguation = 'compatible' | 'earliest' | 'latest' | 'reject'
type EngineKind = 'system-intl' | 'embedded-tzdb'
type LabelStyle = 'original' | 'offset' | 'abbrev' | 'altName'
Conversion#
fromUTC#
function fromUTC(input: DateInput, zone: string, opts?: FormatOptions): ClockworkTime
An instant as seen in zone. Throws TimezoneError for an invalid zone or an input that isn't an instant (including strings without an offset). Guide
fromUTC('2024-07-15T14:00:00Z', 'Asia/Kolkata').local // '2024-07-15 19:30:00.000'
interpretLocal#
function interpretLocal(wallTime: string | WallTime, zone: string, opts?: ConvertOptions): ClockworkTime
type WallTime = {
year: number
month: number // 1–12
day: number
hour?: number
minute?: number
second?: number
millisecond?: number
}
The instant a wall time names in zone. Skipped and repeated times resolve with opts.disambiguation (default compatible). Throws TimezoneError for invalid input, InvalidTimeError or AmbiguousTimeError with reject. Guide
interpretLocal('2024-03-10T02:30:00', 'America/New_York').local // '2024-03-10 03:30:00.000'
convert#
function convert(input: DateInput, fromZone: string, toZone: string, opts?: ConvertOptions): ClockworkTime
A string without an offset is wall time in fromZone; anything else is an instant. Returns the result in toZone.
convert('2024-07-15T16:30:00', 'Asia/Kathmandu', 'America/St_Johns').local // '2024-07-15 08:15:00.000'
ClockworkTime#
| Field |
Type |
Description |
utc |
string |
ISO instant with Z, e.g. '2024-07-15T14:00:00.000Z' |
zone |
string |
The zone you passed |
local |
string |
Local time, formatted with opts.format or yyyy-MM-dd HH:mm:ss.SSS |
offsetMinutes |
number |
Offset from UTC in minutes; fractional only for local mean time |
isDST |
boolean |
Whether DST is in effect |
abbreviation |
string |
e.g. 'EDT', '+0545' |
source |
EngineKind |
Engine that produced the result |
tzdbVersion |
string? |
tzdb release, with embedded-tzdb only |
Options#
interface FormatOptions {
format?: string // pattern for `local`
locale?: string // month/weekday/day-period names, default 'en'
}
interface ConvertOptions extends FormatOptions {
disambiguation?: Disambiguation // default 'compatible'
}
| Function |
Returns |
getZoneInfo(input: DateInput, zone: string) |
ZoneInfo: { offsetSeconds, isDST, abbreviation } |
getOffset(input: DateInput, zone: string) |
number: offset in minutes |
isDST(input: DateInput, zone: string) |
boolean; false for invalid zones and fixed offsets |
isValidTimeZone(zone: string) |
boolean; accepts IANA names, links and fixed offsets |
canonicalizeTimeZone(zone: string) |
string: 'Asia/Calcutta' → 'Asia/Kolkata' |
getUserTimeZone() |
string: the runtime's zone, canonicalized, or 'UTC' |
getZoneCountries(zone: string) |
string[]: ISO 3166 codes, e.g. ['CH', 'DE', 'LI'] |
listTimeZones(opts?: ListOptions) |
TimeZoneItem[], sorted by offset then name |
getZoneInfo('2024-07-01T00:00:00Z', 'Asia/Kathmandu')
// { offsetSeconds: 20700, isDST: false, abbreviation: '+0545' }
getZoneInfo, getOffset, canonicalizeTimeZone and getZoneCountries throw TimezoneError for an invalid zone.
interface ListOptions {
labelStyle?: LabelStyle // default 'original'
at?: DateInput // default now
locale?: string // for 'altName', default 'en'
country?: string // ISO 3166 code
}
interface TimeZoneItem {
value: string
label: string
offsetMinutes: number
countryCodes: string[]
}
Guide
| Function |
Description |
format(input, zone, pattern = DEFAULT_FORMAT_PATTERN, locale = 'en') |
Format an instant in a zone |
formatWithPattern(input, zone, name: keyof typeof FORMAT_PATTERNS) |
Format with a named pattern |
formatNow(zone, pattern?) |
Format the current time |
formatFor(input, zone, useCase) |
'display' | 'log' | 'filename' | 'api' | 'human' |
formatRange(start, end, zone, { pattern?, separator?, sameDay? }) |
Format a range |
formatDuration(start, end, { units?, precision? }) |
'2 days, 2 hours' |
formatRelative(input, zone, relativeTo = new Date()) |
'2 hours ago', 'in 3 days', or a date |
formatOffset(offsetMinutes) |
345 → '+05:45' |
isValidFormatPattern(pattern) |
boolean |
FORMAT_PATTERNS |
Named patterns: default, dateOnly, timeOnly, dateTime, dateTimeMs, amPm, human, short, long, rfc2822, iso8601 |
DEFAULT_FORMAT_PATTERN |
'yyyy-MM-dd HH:mm:ss zzz' |
Guide and token table
Parsing#
| Function |
Returns |
parse(input, opts?: ParseOptions) |
ParseResult; throws in strict mode |
parseDate(input, opts?) |
Date; throws if invalid |
safeParse(input, opts?) |
ParseResult | null; never throws, always non-strict |
isParseable(input, opts?) |
boolean |
parseAny(input, formats?: ParseOptions[]) |
First valid ParseResult; throws if none |
parseRange(input, { separator?, ...ParseOptions }) |
{ start: Date; end: Date } |
parseTime(input) |
{ hour, minute, second, millisecond } |
parseOffset(input) |
Offset in minutes |
parseComponents(input) |
DateComponents (wall-time fields) |
normalize(input: DateInput) |
Date |
isValidISOString(input) |
boolean: ISO with an offset |
isValidLocalISOString(input) |
boolean: ISO without an offset |
interface ParseOptions {
strict?: boolean // default true
defaultTimezone?: string // default 'UTC'
}
interface ParseResult {
date: Date
hasTimezone: boolean
original: string
isValid: boolean
}
interface DateComponents {
year: number
month: number
day: number
hour: number
minute: number
second: number
millisecond: number
}
Guide
Transitions and clocks#
| Function |
Returns |
getNextTransition(zone, from = new Date()) |
TransitionInfo | null |
getPreviousTransition(zone, from = new Date()) |
TransitionInfo | null |
getTransitions(zone, from, to) |
TransitionInfo[] in [from, to) |
isNearTransition(input, zone, engine?, windowHours = 24) |
boolean |
watchZoneTransitions(zone, callback: (at: Date) => void) |
() => void stop function |
onTick(callback: (now: Date) => void, granularityMs = 1000) |
() => void stop function |
cleanupAllWatchers() |
void |
getWatcherStats() |
{ transitionWatchers, tickWatchers } |
getNextTransition and getPreviousTransition search two years by default and also accept engine and searchDays arguments.
interface TransitionInfo {
date: Date
fromOffset: number // minutes
toOffset: number // minutes
isDSTStart: boolean
isDSTEnd: boolean
fromAbbreviation: string
toAbbreviation: string
}
Guide
Lifecycle#
| Function |
Description |
initClockworkTZ(opts?: ClockworkOptions) |
Choose and configure the engine; returns Promise<void> |
dispose() |
Stop auto-updates and cross-tab listening |
engine() |
Current EngineKind |
tzdbVersion() |
tzdb version, or undefined with system-intl |
getStatus() |
{ engine, tzdbVersion?, dataLoaded, lastUpdate? } |
getInitOptions() |
A copy of the last ClockworkOptions |
onDataUpdate(callback: (version: string) => void) |
Unsubscribe function |
refreshTimezoneData() |
Promise<boolean>: true if newer data was loaded |
VERSION |
The library version, e.g. '1.0.1' |
interface ClockworkOptions {
engine?: EngineKind // default 'system-intl'
data?: unknown // tzdb data for 'embedded-tzdb'
manifestURL?: string // signed manifest to load and keep fresh
publicKey?: string // base64 raw Ed25519 key, default DEFAULT_PUBLIC_KEY
autoUpdateIntervalMs?: number // default 24h, 0 disables
}
Guide
Data updates#
| Export |
Description |
DEFAULT_MANIFEST_URL |
'https://cdn.jsdelivr.net/npm/@adamrichardturner/clockworktz-data@1/manifest.json' |
DEFAULT_PUBLIC_KEY |
'DLvL/miUAfXKI8Sae27IuV/o5l3vbg5OIp5QMkeXLkU=' |
ensureFreshTzdb(manifestURL?, { publicKey?, fetch? }) |
Promise<{ version, data } | null>: fetch, verify and cache without changing the engine |
verifyManifestSignature(manifest, publicKey?) |
Promise<boolean>; fails closed without WebCrypto Ed25519 |
compareTzdbVersions(a, b) |
Negative, zero or positive, e.g. ('2026a', '2025z') → 1 |
clearCache(manifestURL?, publicKey?) |
Clear cached data for one manifest, or all |
getCacheStats(manifestURL?, publicKey?) |
{ dataCached, dataVersion?, dataAge? } |
interface TzdbManifest {
version: string // e.g. '2026f'
releaseDate: string
dataUrl: string // absolute, or relative to the manifest URL
sha256: string // hex SHA-256 of the data file
signature: string // base64 Ed25519 over `${version}\n${sha256}`
keyId: string
}
Security & signing
Engines#
| Export |
Description |
getCurrentEngine() |
The active TzEngine |
setEngine(engine: TzEngine) |
Install an engine directly (tests, advanced use) |
createSystemIntlEngine() |
A new SystemIntlEngine |
createEmbeddedTzdbEngine(data?) |
A new EmbeddedTzdbEngine |
SystemIntlEngine, EmbeddedTzdbEngine |
The engine classes |
interface TzEngine {
readonly kind: EngineKind
getVersion(): string | undefined
isValidTimeZone(zone: string): boolean
canonicalize(zone: string): string
listZoneIds(): string[]
getZoneInfo(epochMs: number, zone: string): ZoneInfo
getTransitions?(zone: string, fromMs: number, toMs: number): Transition[]
getCountryCodes?(zone: string): string[]
}
interface LoadableEngine extends TzEngine {
loadTzdbData(data: unknown): void
isDataLoaded(): boolean
}
interface ZoneInfo {
offsetSeconds: number
isDST: boolean
abbreviation: string
}
interface Transition {
at: number // epoch ms
before: ZoneInfo
after: ZoneInfo
}
Errors#
| Class |
Extends |
Thrown for |
TimezoneError |
Error |
Everything: invalid zones, input, patterns, options and data |
InvalidTimeError |
TimezoneError |
Skipped time with disambiguation: 'reject' |
AmbiguousTimeError |
TimezoneError |
Repeated time with disambiguation: 'reject' |
Guide
clockworktz/tzdb#
import { tzdb, TZDB_VERSION } from 'clockworktz/tzdb'
import type { TzdbData, CompactZone } from 'clockworktz/tzdb'
TZDB_VERSION // '2026f'
| Export |
Description |
tzdb |
The compiled IANA tzdb snapshot, for initClockworkTZ({ engine: 'embedded-tzdb', data: tzdb }). About 30 kB gzipped |
TZDB_VERSION |
The IANA release tzdb was built from: '2026f' in 1.0.1 |
TzdbData, CompactZone |
Types describing the data format |