Why GMT?
@northguild/gmt is not the latest entry in a crowded field — it is a
fundamentally different bet about what a date/time library should guarantee.
The pages below make that concrete with data drawn from the library itself.
CI test execution volume
Section titled “CI test execution volume”The most direct measure of testing rigor is how many times the library is actually executed in CI. GMT’s suite is 56,006 tests, and its 17-locale matrix is built into those tests rather than layered on top. On every PR, CI then re-runs the whole suite under 10 timezones and 3 Node versions, so every test runs 30 times.
Test executions per library
One run of each suite, before any CI matrix. GMT: 56,006 tests. Competitors: 386–6,086.
GMT: 56,006 tests, re-run across 10 timezones × 3 Node versions = 1,680,180 executions. Competitors: 386–12,172.
The split matters. On suite size alone, GMT’s 56,006 tests already outnumber the largest alternative’s (Spacetime, 6,086). The matrix is what turns that into 1,680,180 executions: about 138× the most-executed alternative and about 4,353× the least, against a surface area that is itself larger.
API surface
Section titled “API surface”GMT exposes 634 public functions across 12 namespaces, plus 27
regex patterns in regex. 10 namespaces are the core — `plain`, `zoned`, `utc`, `unix`, `calendar`, `precision`, `duration`, `interval`, `instant`, and `span` — and
2 are industry layers built on it: `intermodal` and `transport`. All of it is generated
from the same TypeScript source and verified by the same test suite.
Public functions per namespace
564 core functions across 10 namespaces, plus 27 regex patterns. Every namespace shares one contract: ISO 8601 strings in, typed values out, no Date object.
The core is weighted toward plain and zoned, which hold 345 of its 564 functions.
plain has one set of operations for each wall-clock shape — dates, times and date-times —
and zoned carries them into a real time zone, where a day is not always 24 hours long.
utc and unix apply the same rules to UTC strings and epoch numbers.
70 functions across 2 industry layers. Each names one operation a trade performs, and returns the same ISO 8601 and RFC 9557 strings as the core.
The industry layers are small on purpose. Each is built on the core, so every layer agrees on
what a midnight, a day or an elapsed hour is. The facts come from the caller: GMT does not
map a port to a time zone or look up a carrier’s free days, so it holds no registry that can
go stale. The intermodal layer also reads and writes the timestamp formats freight exchanges:
UN/EDIFACT DTM, ANSI X12 dates and time codes, and GS1 EPCIS 2.0 event times. Read the industry guides →
Timezone coverage
Section titled “Timezone coverage”GMT tests against 10 timezones chosen not for geographic spread but for offset coverage — from UTC−11 (Pacific/Niue) to UTC+13:45 (Pacific/Chatham). That 24.75-hour window means that at any given moment, at least one zone is already living in tomorrow while another is still stuck in yesterday. Date-boundary bugs — off-by-one day, wrong month rollover, DST gaps — only surface when the suite runs across this full offset range. A test that passes at UTC can fail at UTC+14 simply because the calendar date is different.
CI timezones under test
10 timezones spanning UTC−11 to UTC+13:45. Hover a dot for the current local time in ISO 8601. Times refresh every second via Temporal.
Locale matrix
Section titled “Locale matrix”GMT does not test every locale — there are hundreds, and most differ only in formatting minutiae. Instead it tests a curated matrix of 17 locales chosen to stress the dimensions that actually break date libraries: script direction (LTR/RTL Arabic and Hebrew), character sets (CJK, Cyrillic, Latin), pluralization rules, and ICU data variants. The goal is not exhaustive locale coverage — it is representative coverage of every class of bug that locale-aware code can produce.
Locale matrix by script family
17 locales across 5 script families. Latin (9), CJK (4), Arabic/Hebrew (2), Cyrillic (1), Turkic (1). Each cell shows the locale code and English name.
Correct at the edges
Section titled “Correct at the edges”Most date bugs hide at the extremes, where ordinary tests never look. GMT tests them directly:
- Midnight. A time at the last nanosecond of the day stays on that day.
- The ends of time. The first and last moments a date can represent still give exact answers.
- Daylight saving. Days, weeks and months start at the right moment across clock changes, right to the end of the calendar.
- Zones ahead of UTC. Sydney and Kiritimati work right up to the last moment that can be represented.
- Long totals. Adding up time stays exact to the nanosecond, however large the total.
- Touching intervals. A 9–5 meeting and a 5–6 meeting don’t overlap: an interval includes its start, not its end.
- Zero known bugs. None of the 56,006 tests is skipped or switched off.
See the evidence behind each of these → · Where the Temporal polyfill gets one of these wrong, GMT returns the spec’s answer and reports the defect upstream: Helping fix Temporal →
What this means
Section titled “What this means”| Dimension | GMT | Legacy libraries |
|---|---|---|
| CI executions | 1,680,180 | 386 – 12,172 |
| Locales tested | 17, built into every test | 0 – 139 files, tested in isolation from the rest of the API surface |
| Timezones tested | 10 × every public function | 0 – 8 files, tested in isolation |
| Node versions | 22, 24, 26 | 1 – 4 |
| Public functions | 634 | 80 – 246* |
| Time precision | Nanoseconds, exact as bigint | Milliseconds |
| Sentinel contract | "" / null / false / [] | Throws RangeError |
* Public functions and methods, counted from each library’s published TypeScript declarations: every exported function, plus every public method of its exported classes and types. Overloads, aliases and inherited methods count once; constructors, getters and properties do not. date-fns 4.4.0: 246 (245 functions, 1 method); Moment.js 2.30.1: 169 (27 functions, 142 methods); Luxon 3.7.2: 153 (0 functions, 153 methods); Day.js 1.11.23: 128 (16 functions, 112 methods); @internationalized/date 3.12.4: 104 (46 functions, 58 methods); Spacetime 7.13.0: 80 (0 functions, 80 methods). GMT exports functions only.
The table is the point. GMT does not add a few convenience methods to an
existing Date wrapper — it replaces the type system entirely with
TC39 Temporal, then rebuilds the
ergonomic surface on top of that guarantee. Every function takes and returns
ISO 8601 strings,
every zoned value carries its
RFC 9557 time-zone annotation, and
the 17-locale, 10-timezone matrix runs
against the library’s entire public API on every PR, not a sample of it.
That is what 1,680,180 CI executions verify.
The full table, how each figure was measured, and what each library covers are on GMT and the other libraries.
Earth imagery: NASA Earth Observatory (Blue Marble Next Generation).