@burglekitt/gmt
Version:
Temporal-based date and time utilities with timezone support and polyfill integration
69 lines (68 loc) • 2.8 kB
JavaScript
import { Temporal } from "@js-temporal/polyfill";
import { joinDateTimeConnector, normalizeDateTime } from "../../internal/index.js";
import { normalizeTimeZone } from "../../internal/normalizeTimeZone.js";
import { toInstantFromUtc } from "../../internal/toInstantFromUtc.js";
import { isValidUtc } from "../validate/index.js";
const ABS_DAY_THRESHOLD = 6;
/**
* Format a UTC ISO string as a relative day label plus time-of-day, e.g.
* "Tomorrow at 2:30 PM" — the UTC counterpart of `formatCalendar`. See that
* function's JSDoc for the day-label/threshold/connector design; this
* variant compares calendar days and renders the clock time in `timeZone`
* (default `"UTC"`).
*
* @param value UTC ISO string to format
* @param locale optional: BCP 47 locale tag
* @param options optional: { reference, timeZone, timeStyle }
* @returns the formatted calendar string, or "" on invalid input
*
* @example formatCalendarUtc("2026-03-16T18:30:00Z", "en-US", { timeZone: "America/New_York", reference: "2026-03-15T13:00:00Z" }) // "tomorrow at 2:30 PM"
* @example formatCalendarUtc(value, "fr-FR", { timeZone: "Europe/Paris" }) // "demain à 14:30"
* @example formatCalendarUtc("not-a-date") // ""
*/
export function formatCalendarUtc(value, locale, options = {}) {
if (!isValidUtc(value))
return "";
if (options.reference !== undefined && !isValidUtc(options.reference))
return "";
const target = toInstantFromUtc(value);
if (target === null)
return "";
let reference;
if (options.reference) {
const ref = toInstantFromUtc(options.reference);
if (ref === null)
return "";
reference = ref;
}
else {
try {
reference = Temporal.Now.instant();
}
catch {
return "";
}
}
try {
const timeZone = normalizeTimeZone(options.timeZone);
const timeStyle = options.timeStyle ?? "short";
const targetDate = target.toZonedDateTimeISO(timeZone).toPlainDate();
const referenceDate = reference.toZonedDateTimeISO(timeZone).toPlainDate();
const diffDays = targetDate.since(referenceDate).days;
const epochMilliseconds = Number(target.epochMilliseconds);
if (Math.abs(diffDays) > ABS_DAY_THRESHOLD) {
return normalizeDateTime(new Intl.DateTimeFormat(locale, {
dateStyle: "long",
timeStyle,
timeZone,
}).format(epochMilliseconds));
}
const dayLabel = new Intl.RelativeTimeFormat(locale, {
numeric: "auto",
}).format(diffDays, "day");
return normalizeDateTime(joinDateTimeConnector(epochMilliseconds, timeZone, locale, dayLabel, timeStyle));
}
catch {
return "";
}
}