Task e0a2b862: Today tab (#192)
This commit was merged in pull request #192.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Date/time formatting for the Today tab.
|
||||
*
|
||||
* Calendar dates ("YYYY-MM-DD") are parsed by their components and formatted
|
||||
* in UTC, so the printed day never shifts with the viewer's offset — unlike
|
||||
* `new Date("YYYY-MM-DD")`, which is UTC midnight and reads as the previous
|
||||
* day in negative-offset zones.
|
||||
*/
|
||||
|
||||
const LOCALE = "en-US";
|
||||
|
||||
function calendarDate(iso: string): Date {
|
||||
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
|
||||
if (!match) throw new Error(`Invalid calendar date: ${iso}`);
|
||||
const [, year, month, day] = match;
|
||||
return new Date(Date.UTC(Number(year), Number(month) - 1, Number(day)));
|
||||
}
|
||||
|
||||
function format(date: Date, options: Intl.DateTimeFormatOptions): string {
|
||||
// Newer ICU puts a narrow no-break space before AM/PM; normalise it.
|
||||
return new Intl.DateTimeFormat(LOCALE, options)
|
||||
.format(date)
|
||||
.replace(/ /g, " ");
|
||||
}
|
||||
|
||||
/** "Wednesday, September 23" */
|
||||
export function formatLongDate(iso: string): string {
|
||||
const date = calendarDate(iso);
|
||||
const weekday = format(date, { weekday: "long", timeZone: "UTC" });
|
||||
const monthDay = format(date, { month: "long", day: "numeric", timeZone: "UTC" });
|
||||
return `${weekday}, ${monthDay}`;
|
||||
}
|
||||
|
||||
/** "Sep 23" */
|
||||
export function formatShortDate(iso: string): string {
|
||||
return format(calendarDate(iso), { month: "short", day: "numeric", timeZone: "UTC" });
|
||||
}
|
||||
|
||||
/** "09:00" -> "9:00 AM" (a wall-clock time, no zone conversion). */
|
||||
export function formatDueTime(hhmm: string): string {
|
||||
const match = /^(\d{2}):(\d{2})/.exec(hhmm);
|
||||
if (!match) return hhmm;
|
||||
const date = new Date(Date.UTC(2000, 0, 1, Number(match[1]), Number(match[2])));
|
||||
return format(date, { hour: "numeric", minute: "2-digit", timeZone: "UTC" });
|
||||
}
|
||||
|
||||
/** A UTC ISO timestamp shown as the viewer's local time, "7:04 AM". */
|
||||
export function formatClockTime(isoTimestamp: string): string {
|
||||
return format(new Date(isoTimestamp), { hour: "numeric", minute: "2-digit" });
|
||||
}
|
||||
Reference in New Issue
Block a user