/** * Jalali (Shamsi) date helpers for Design System. * Form/API values stay Gregorian ISO `YYYY-MM-DD`; UI is always Jalali. */ import { toJalaali, toGregorian, jalaaliMonthLength as jMonthLength } from "jalaali-js"; export const WEEKDAYS = ["ش", "ی", "د", "س", "چ", "پ", "ج"] as const; export const MONTHS = [ "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", ] as const; export type JalaliParts = { year: number; month: number; day: number }; function pad(n: number) { return String(n).padStart(2, "0"); } function parseIso(iso: string): { gy: number; gm: number; gd: number } | null { const m = iso.trim().match(/^(\d{4})-(\d{2})-(\d{2})/); if (!m) return null; return { gy: Number(m[1]), gm: Number(m[2]), gd: Number(m[3]) }; } /** Today as Gregorian ISO date for API defaults. */ export function todayIso(): string { const now = new Date(); return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`; } export function isoToJalaliParts(iso?: string | null): JalaliParts { const parsed = iso ? parseIso(iso) : null; const d = parsed ? new Date(parsed.gy, parsed.gm - 1, parsed.gd) : new Date(); const j = toJalaali(d.getFullYear(), d.getMonth() + 1, d.getDate()); return { year: j.jy, month: j.jm, day: j.jd }; } export function jalaliPartsToIso(parts: JalaliParts): string { const g = toGregorian(parts.year, parts.month, parts.day); return `${g.gy}-${pad(g.gm)}-${pad(g.gd)}`; } /** Format Gregorian ISO as Jalali `YYYY/MM/DD`. */ export function formatJalali(value?: string | Date | null): string { if (value === null || value === undefined || value === "") return "—"; let gy: number; let gm: number; let gd: number; if (value instanceof Date) { gy = value.getFullYear(); gm = value.getMonth() + 1; gd = value.getDate(); } else { const parsed = parseIso(String(value)); if (parsed) { gy = parsed.gy; gm = parsed.gm; gd = parsed.gd; } else { const d = new Date(String(value)); if (Number.isNaN(d.getTime())) return String(value); gy = d.getFullYear(); gm = d.getMonth() + 1; gd = d.getDate(); } } const j = toJalaali(gy, gm, gd); return `${j.jy}/${pad(j.jm)}/${pad(j.jd)}`; } /** Jalali date + local time for audit timestamps. */ export function formatJalaliDateTime(value?: string | Date | null): string { if (value === null || value === undefined || value === "") return "—"; const d = value instanceof Date ? value : new Date(String(value)); if (Number.isNaN(d.getTime())) return String(value); return `${formatJalali(d)} ${pad(d.getHours())}:${pad(d.getMinutes())}`; } export function jalaliMonthLength(year: number, month: number): number { return jMonthLength(year, month); } /** Weekday index Sat=0 … Fri=6 for Jalali calendar grid. */ export function jalaliWeekdaySat0(year: number, month: number, day: number): number { const g = toGregorian(year, month, day); const jsDay = new Date(g.gy, g.gm - 1, g.gd).getDay(); // 0=Sun return (jsDay + 1) % 7; } export function shiftJalaliMonth( year: number, month: number, delta: number ): { year: number; month: number } { let m = month + delta; let y = year; while (m < 1) { m += 12; y -= 1; } while (m > 12) { m -= 12; y += 1; } return { year: y, month: m }; }