Skip to content

Formatting Date and Time

  • The core functions, which every industry uses.

GMT’s format functions return locale-sensitive strings. They differ from Intl.DateTimeFormat in scope, locale handling, and sentinel behavior.

HIGH

Using Intl.DateTimeFormat directly

Intl.DateTimeFormat does not cover GMT's extended format set (Rfc2822, HTTP, SQL, Rfc3339, relative), and the order of its formatToParts output varies by locale. GMT's format functions are consistent across locales and formats.

Wrong

const f = new Intl.DateTimeFormat("en-US", { dateStyle: "full" });
f.format(new Date("2024-03-15")); // "Thursday, March 14, 2024" in New York — the Date is UTC midnight

Right

import { formatDate } from "@northguild/gmt";
formatDate("2024-03-15", "en-US", { dateStyle: "full" }); // "Friday, March 15, 2024" — no time zone involved
MEDIUM

Not handling empty string on invalid input

GMT's format functions return an empty string for invalid input. Treating that as a blank format result hides bugs in the input pipeline.

Wrong

const out = formatDate(badInput);
// if badInput is "", out is also "" — indistinguishable from a formatting bug

Right

import { formatDate } from "@northguild/gmt";
const out = formatDate(input);
if (out === "") {
// input was invalid — fix upstream
}
MEDIUM

Assuming getLocale* rejects a locale it has no data for

getLocaleMonthNames, getLocaleEraNames, and friends return an empty array only for a malformed locale tag. A well-formed tag with no locale data is not an error: it falls back to the runtime's default locale, as ECMA-402 locale resolution requires. They never throw.

Wrong

const months = getLocaleMonthNames("xx-XX");
if (months.length === 0) {
// never reached — "xx-XX" is well-formed, so this is the default locale's names
}

Right

import { getLocaleMonthNames } from "@northguild/gmt";
getLocaleMonthNames("not-a-locale-!!"); // [] — only a malformed tag is rejected
// check for locale data yourself, e.g. Intl.DateTimeFormat.supportedLocalesOf(["xx-XX"]) is []
MEDIUM

Looking for a token-pattern formatter (there isn't one, deliberately)

GMT deliberately omits a format(pattern, value) function. Token-pattern formatters either silently ignore unknown tokens or produce garbled output on bad input. GMT's dedicated format functions are safer.

Wrong

// no equivalent of format("{yyyy}-{MM}-{dd}", date)

Right

import { formatDate } from "@northguild/gmt";
formatDate("2024-03-15", "en-US", { dateStyle: "long" }); // "March 15, 2024" — dedicated function, no tokens
MEDIUM

Reassembling formatToParts output in a fixed order

Intl.DateTimeFormat.formatToParts() returns parts in locale-specific order. Joining them in a fixed sequence (month before day, for example) gives wrong output for ja-JP, zh-CN and other locales.

Wrong

const parts = new Intl.DateTimeFormat("ja-JP").formatToParts(new Date());
// parts[0] is the year, not the month — hardcoding en-US order breaks here

Right

import { formatDate } from "@northguild/gmt";
formatDate("2024-03-15", "ja-JP", { dateStyle: "long" }); // "2024年3月15日" — locale-correct order, no manual assembly
MEDIUM

Confusing formatCalendar with formatRelativeDateTime

formatCalendar names the calendar day and the time of a datetime — 'tomorrow at 9:00 AM' — and falls back to an absolute date a week or more away. formatRelativeDateTime states a distance — 'tomorrow', 'in 3 hours', '3 days ago' — with no time of day. They have different output shapes.

Wrong

formatCalendar("2024-03-16T09:00:00", "en-US", { reference: "2024-03-15T08:00:00" });
// "tomorrow at 9:00 AM" — used where a relative distance was wanted

Right

import { formatRelativeDateTime } from "@northguild/gmt";
formatRelativeDateTime("2024-03-16T09:00:00", "en-US", { reference: "2024-03-15T08:00:00" }); // "tomorrow"