Files
silo-server/web/src/lib/calendarWeek.ts
T

98 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** Returns the ISO date string (YYYY-MM-DD) of the Monday for the week containing `date`. */
export function getWeekStart(date: Date): string {
const d = new Date(date);
const day = d.getDay(); // 0=Sun, 1=Mon, ...
const diff = day === 0 ? -6 : 1 - day;
d.setDate(d.getDate() + diff);
return formatDate(d);
}
/** Adds `n` weeks to a week-start date string and returns the new week-start. */
export function addWeeks(weekStart: string, n: number): string {
const d = parseDate(weekStart);
d.setDate(d.getDate() + n * 7);
return formatDate(d);
}
/** Adds `n` days to a date string and returns the new date. */
export function addDays(dateStr: string, n: number): string {
const d = parseDate(dateStr);
d.setDate(d.getDate() + n);
return formatDate(d);
}
/** Returns an array of 7 ISO date strings for the week starting at `weekStart` (Monday). */
export function getWeekDays(weekStart: string): string[] {
const d = parseDate(weekStart);
const days: string[] = [];
for (let i = 0; i < 7; i++) {
const day = new Date(d);
day.setDate(d.getDate() + i);
days.push(formatDate(day));
}
return days;
}
/** Returns a human-readable heading like "Monday, April 7th". */
export function formatDayHeading(dateStr: string): string {
const d = parseDate(dateStr);
const weekday = d.toLocaleDateString(undefined, { weekday: "long" });
const month = d.toLocaleDateString(undefined, { month: "long" });
const day = d.getDate();
return `${weekday}, ${month} ${ordinal(day)}`;
}
/** Returns a short day label like "Mon" and day number like "7". */
export function formatShortDay(dateStr: string): { label: string; day: number } {
const d = parseDate(dateStr);
return {
label: d.toLocaleDateString(undefined, { weekday: "short" }),
day: d.getDate(),
};
}
/** Returns the month + year label like "April 2026". */
export function formatMonthYear(dateStr: string): string {
const d = parseDate(dateStr);
return d.toLocaleDateString(undefined, { month: "long", year: "numeric" });
}
/** Compact range for a Mon–Sun week, e.g. "Apr 6 – 12, 2026" or spanning two months. */
export function formatWeekRangeLabel(weekStart: string): string {
const start = parseDate(weekStart);
const end = new Date(start);
end.setDate(end.getDate() + 6);
const sameMonth =
start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear();
if (sameMonth) {
const month = start.toLocaleDateString(undefined, { month: "short" });
return `${month} ${start.getDate()} – ${end.getDate()}, ${start.getFullYear()}`;
}
return `${start.toLocaleDateString(undefined, { month: "short", day: "numeric" })} – ${end.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" })}`;
}
/** Checks if a date string is today. */
export function isToday(dateStr: string): boolean {
return dateStr === formatDate(new Date());
}
/** Formats a Date to YYYY-MM-DD. */
function formatDate(d: Date): string {
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
/** Parses a YYYY-MM-DD string to a local Date. */
function parseDate(dateStr: string): Date {
const parts = dateStr.split("-").map(Number);
return new Date(parts[0]!, parts[1]! - 1, parts[2]!);
}
function ordinal(n: number): string {
const s = ["th", "st", "nd", "rd"] as const;
const v = n % 100;
return n + (s[(v - 20) % 10] ?? s[v] ?? s[0]);
}