export type AgenticRectificationDatePrecision = "day" | "month" | "quarter" | "year"; export type AgenticRectificationDateRange = Readonly<{ start: string; end: string; precision: AgenticRectificationDatePrecision; label: string; }>; function isoDate(year: number, month: number, day: number): string { const value = new Date(Date.UTC(year, month - 1, day)); if (value.getUTCFullYear() !== year || value.getUTCMonth() !== month - 1 || value.getUTCDate() !== day) { throw new Error("invalid_calendar_date"); } return value.toISOString().slice(0, 10); } function monthEnd(year: number, month: number): string { return new Date(Date.UTC(year, month, 0)).toISOString().slice(0, 10); } export function dateRangeFromDeclared( value: string, precision: AgenticRectificationDatePrecision, ): AgenticRectificationDateRange { if (precision === "day") { if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) throw new Error("invalid_day"); const [year, month, day] = value.split("-").map(Number); const date = isoDate(year!, month!, day!); return { start: date, end: date, precision, label: value }; } if (precision === "month") { if (!/^\d{4}-\d{2}$/.test(value)) throw new Error("invalid_month"); const [year, month] = value.split("-").map(Number); const start = isoDate(year!, month!, 1); return { start, end: monthEnd(year!, month!), precision, label: value }; } if (precision === "quarter") { const matched = /^(\d{4})-Q([1-4])$/.exec(value); if (!matched) throw new Error("invalid_quarter"); const year = Number(matched[1]); const firstMonth = (Number(matched[2]) - 1) * 3 + 1; return { start: isoDate(year, firstMonth, 1), end: monthEnd(year, firstMonth + 2), precision, label: value, }; } if (!/^\d{4}$/.test(value)) throw new Error("invalid_year"); const year = Number(value); return { start: isoDate(year, 1, 1), end: isoDate(year, 12, 31), precision, label: value }; }