feat(web): formatTimestamp helper (instance tz + locale, UTC fallback) (#42)
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import { formatTimestamp } from "./format-timestamp";
|
||||
|
||||
test("formats a UTC timestamp with date and time in the given locale", () => {
|
||||
const out = formatTimestamp("2026-06-08T12:30:00Z", "UTC", "en");
|
||||
expect(out).toContain("2026");
|
||||
expect(out).toContain("12:30");
|
||||
});
|
||||
|
||||
test("applies the timezone — a near-midnight UTC instant shifts the calendar day", () => {
|
||||
// 02:00 UTC on Jun 8 is 22:00 on Jun 7 in New York (EDT, UTC-4)
|
||||
const ny = formatTimestamp("2026-06-08T02:00:00Z", "America/New_York", "en");
|
||||
const utc = formatTimestamp("2026-06-08T02:00:00Z", "UTC", "en");
|
||||
expect(ny).toContain("Jun 7");
|
||||
expect(utc).toContain("Jun 8");
|
||||
});
|
||||
|
||||
test("an invalid IANA zone does not throw and falls back to UTC", () => {
|
||||
const out = formatTimestamp("2026-06-08T12:30:00Z", "Not/AZone", "en");
|
||||
expect(out).toContain("2026");
|
||||
});
|
||||
|
||||
test("null renders the em-dash placeholder; an unparseable string is returned unchanged", () => {
|
||||
expect(formatTimestamp(null, "UTC", "en")).toBe("—");
|
||||
expect(formatTimestamp("not-a-date", "UTC", "en")).toBe("not-a-date");
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/** Formats a UTC ISO timestamp for display in the instance timezone + active locale.
|
||||
* Storage/transmission stay UTC — this is display-only. Falls back to UTC formatting on an
|
||||
* invalid IANA zone (a misconfigured instance) rather than throwing. */
|
||||
export function formatTimestamp(value: unknown, timeZone: string, locale: string): string {
|
||||
if (typeof value !== "string") return value == null ? "—" : String(value);
|
||||
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return value;
|
||||
|
||||
const opts = { dateStyle: "medium", timeStyle: "short" } as const;
|
||||
|
||||
try {
|
||||
return new Intl.DateTimeFormat(locale, { ...opts, timeZone }).format(date);
|
||||
} catch {
|
||||
// Invalid IANA timeZone (misconfigured instance) — fall back to UTC rather than crash.
|
||||
return new Intl.DateTimeFormat(locale, { ...opts, timeZone: "UTC" }).format(date);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user