Skip to content

formatCalendar

formatCalendar(value: string, locale?: string, options?: FormatCalendarOptions): string
import { formatCalendar } from "@northguild/gmt/plain/format";

Format a plain date-time as a relative day label plus time-of-day, e.g.

  • “Tomorrow at 2:30 PM” — Moment’s .calendar(), which the existing formatRelativeDateTime family does not cover (that family renders “in 1 day”, an elapsed-time phrase, not a day label + clock time).
  • Within ±6 days of reference (default: now), renders <day label> joined to the localized time using the locale’s own connector — never a hardcoded “at”. The day label comes from Intl.RelativeTimeFormat (numeric: "auto"), so it reads “Today”/“Tomorrow”/“Yesterday” near the boundary and “in N days”/“N days ago” further out, all locale-native.
  • Beyond ±6 days, falls back to an absolute dateStyle: "long" + timeStyle string with no relative wording, matching Moment’s sameElse behavior.
  • The connector between the day label and the time is read from CLDR’s own combined date+time pattern for the locale (see internal/joinDateTimeConnector.ts), not hardcoded — this is what lets formatCalendar avoid the i18n objection that excludes a token formatter (Decision 1 in context/roadmap/issues/J.md).
  • Use formatCalendar for user-facing schedules (“Tomorrow at 2:30 PM”); use formatRelativeDateTime for elapsed-time displays (“in 1 day”).
Parameter Type Description
value string ISO PlainDateTime string to format
locale string optional: BCP 47 locale tag

options

Option Type Default
reference? string
timeStyle? "short" | "medium"

the formatted calendar string, or “” on invalid input

formatCalendar("2026-03-16T14:30:00", "en-US", { reference: "2026-03-15T09:00:00" }) // "tomorrow at 2:30 PM"
formatCalendar("2026-03-08T14:30:00", "en-US", { reference: "2026-03-15T09:00:00" }) // "March 8, 2026 at 2:30 PM" (7 days out — beyond the threshold, absolute fallback)
formatCalendar("not-a-date") // ""

plain/format/formatCalendar.ts