Co-authored-by: dev_agent <17+dev_agent@code.cubecraftcreations.com> Co-committed-by: dev_agent <17+dev_agent@code.cubecraftcreations.com>
38 lines
1.2 KiB
TypeScript
38 lines
1.2 KiB
TypeScript
/**
|
||
* Date labels for the Schedule tab. Calendar dates are formatted in UTC from
|
||
* their components, so the printed day never shifts with the viewer's offset.
|
||
*/
|
||
import { dayNumber } from "./cycle";
|
||
|
||
const LOCALE = "en-US";
|
||
|
||
function utcDate(iso: string): Date {
|
||
return new Date(dayNumber(iso) * 86_400_000);
|
||
}
|
||
|
||
function format(iso: string, options: Intl.DateTimeFormatOptions): string {
|
||
return new Intl.DateTimeFormat(LOCALE, { ...options, timeZone: "UTC" }).format(utcDate(iso));
|
||
}
|
||
|
||
/** "Tuesday, Sep 1" */
|
||
export function anchorLabel(iso: string): string {
|
||
return `${format(iso, { weekday: "long" })}, ${format(iso, { month: "short", day: "numeric" })}`;
|
||
}
|
||
|
||
/** "September 2026" */
|
||
export function monthLabel(iso: string): string {
|
||
return format(iso, { month: "long", year: "numeric" });
|
||
}
|
||
|
||
/** "Sep 4" */
|
||
export function shortDate(iso: string): string {
|
||
return format(iso, { month: "short", day: "numeric" });
|
||
}
|
||
|
||
/** "Sep 4 – 7", "Sep 28 – Oct 2", or "Sep 4" for a single day. */
|
||
export function dateRange(start: string, end: string): string {
|
||
if (start === end) return shortDate(start);
|
||
const sameMonth = start.slice(0, 7) === end.slice(0, 7);
|
||
return `${shortDate(start)} – ${sameMonth ? Number(end.slice(8)) : shortDate(end)}`;
|
||
}
|