52 lines
2.0 KiB
TypeScript
52 lines
2.0 KiB
TypeScript
// Date helpers for human-friendly display. All task dates are local ISO
|
|
// "YYYY-MM-DD" strings; comparisons are lexicographic.
|
|
|
|
const DAY_MS = 86_400_000;
|
|
|
|
export function todayIso(): string {
|
|
const d = new Date();
|
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
}
|
|
|
|
export function fromIso(s: string): Date {
|
|
const [y, m, d] = s.split("-").map(Number);
|
|
return new Date(y, m - 1, d);
|
|
}
|
|
|
|
export function toIso(d: Date): string {
|
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
}
|
|
|
|
/** Deferred = hidden from action lists until the date arrives. */
|
|
export function isDeferred(deferDate: string | null, today = todayIso()): boolean {
|
|
return !!deferDate && deferDate > today;
|
|
}
|
|
|
|
export function daysFromToday(dateIso: string): number {
|
|
return Math.round((fromIso(dateIso).getTime() - fromIso(todayIso()).getTime()) / DAY_MS);
|
|
}
|
|
|
|
/** "Today" / "Tomorrow" / "Mon 18 Aug" — no ISO date math for the reader. */
|
|
export function friendlyDate(dateIso: string): string {
|
|
const diff = daysFromToday(dateIso);
|
|
if (diff === 0) return "Today";
|
|
if (diff === 1) return "Tomorrow";
|
|
if (diff === -1) return "Yesterday";
|
|
const d = fromIso(dateIso);
|
|
const wd = d.toLocaleDateString("en-AU", { weekday: "short" });
|
|
const mo = d.toLocaleDateString("en-AU", { month: "short" });
|
|
const yr = d.getFullYear() !== new Date().getFullYear() ? ` ${d.getFullYear()}` : "";
|
|
return `${wd} ${d.getDate()} ${mo}${yr}`;
|
|
}
|
|
|
|
export type DueTone = "overdue" | "today" | "later";
|
|
|
|
/** Row label for a due date, with a tone the UI can color. */
|
|
export function dueMeta(dueIso: string): { label: string; tone: DueTone } {
|
|
const diff = daysFromToday(dueIso);
|
|
if (diff < 0) return { label: diff === -1 ? "1d overdue" : `${-diff}d overdue`, tone: "overdue" };
|
|
if (diff === 0) return { label: "due today", tone: "today" };
|
|
if (diff === 1) return { label: "due tomorrow", tone: "later" };
|
|
return { label: `due ${friendlyDate(dueIso)}`, tone: "later" };
|
|
}
|