/** * Date formatting utilities for the WealthX design system. * Uses date-fns for consistent, locale-independent formatting. */ import { format, parseISO, startOfDay, differenceInCalendarDays, } from "date-fns"; /** * Parse an ISO date string safely, handling both full ISO and date-only strings. * Date-only strings ("2026-01-15") are parsed via parseISO to avoid timezone shifts. */ function safeParse(iso: string): Date { return parseISO(iso); } /** * Format an ISO date string as "15 Jan 2026". * Returns "—" for falsy input, raw string on parse failure. */ export function formatDateShort(iso?: string): string { if (!iso) return "—"; try { return format(safeParse(iso), "dd MMM yyyy"); } catch { return iso; } } /** * Format an ISO date string as "15 Jan" (day + month only). * Returns raw string on parse failure. */ export function formatDateDayMonth(iso: string): string { try { return format(safeParse(iso), "dd MMM"); } catch { return iso; } } /** * Format an ISO date string with weekday: "Wed, 15 Jan 2026". * Returns raw string on parse failure. */ export function formatDateWithWeekday(iso: string): string { try { return format(safeParse(iso), "EEE, dd MMM yyyy"); } catch { return iso; } } /** * Format a Date or ISO string as a long human-readable string: "Wednesday, 15 January 2026". * Used for booking confirmations and summary displays. */ export function formatDateLong(date: Date | string): string { try { const d = typeof date === "string" ? safeParse(date) : date; return format(d, "EEEE, d MMMM yyyy"); } catch { return String(date); } } /** * Current month and year as "June 2026". Used as a default "last updated" label * when no real freshness date is supplied (e.g. Policy AI coverage block). */ export function formatCurrentMonthYear(): string { return format(new Date(), "MMMM yyyy"); } /** * Format a Date or ISO string as an uppercase short weekday: "MON", "TUE", etc. * Used for calendar day headers. */ export function formatWeekdayShort(date: Date | string): string { try { const d = typeof date === "string" ? safeParse(date) : date; return format(d, "EEE").toUpperCase(); } catch { return String(date); } } /** * Returns the number of calendar days remaining until the given ISO date. * Returns 0 if the date has already passed or is today. */ export function daysUntil(iso: string): number { try { const target = startOfDay(parseISO(iso)); const today = startOfDay(new Date()); return Math.max(0, differenceInCalendarDays(target, today)); } catch { return 0; } }