Formatting Date and Time
GMT’s format functions return locale-sensitive strings. They differ from
Intl.DateTimeFormat in scope, locale handling, and sentinel behavior.
Mistakes
Section titled “Mistakes”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 midnightRight
import { formatDate } from "@northguild/gmt";
formatDate("2024-03-15", "en-US", { dateStyle: "full" }); // "Friday, March 15, 2024" — no time zone involvedNot 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 bugRight
import { formatDate } from "@northguild/gmt";
const out = formatDate(input);if (out === "") {// input was invalid — fix upstream}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 []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 tokensReassembling 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 hereRight
import { formatDate } from "@northguild/gmt";
formatDate("2024-03-15", "ja-JP", { dateStyle: "long" }); // "2024年3月15日" — locale-correct order, no manual assemblyConfusing 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 wantedRight
import { formatRelativeDateTime } from "@northguild/gmt";
formatRelativeDateTime("2024-03-16T09:00:00", "en-US", { reference: "2024-03-15T08:00:00" }); // "tomorrow"