export const ANCHOR_ISO = '2023-12-17'; // ancre date const PERIOD_DAYS = 14; const PERIODS_PER_YEAR = 26; const MS_PER_DAY = 86_400_000; const toUTCDate = (iso: string | Date) => { const d = typeof iso === 'string' ? new Date(iso + 'T00:00:00.000Z') : iso; return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())); }; export const toDateString = (d: Date) => d.toISOString().slice(0, 10); export function payYearOfDate(date: string | Date, anchorISO = ANCHOR_ISO): number { const ANCHOR = toUTCDate(anchorISO); const d = toUTCDate(date); const days = Math.floor((+d - +ANCHOR) / MS_PER_DAY); const cycles = Math.floor(days / (PERIODS_PER_YEAR * PERIOD_DAYS)); return ANCHOR.getUTCFullYear() + 1 + cycles; } //compute labels for periods export function computePeriod(pay_year: number, period_no: number, anchorISO = ANCHOR_ISO) { const ANCHOR = toUTCDate(anchorISO); const cycles = pay_year - (ANCHOR.getUTCFullYear() + 1); const offsetPeriods = cycles * PERIODS_PER_YEAR + (period_no - 1); const start = new Date(+ANCHOR + offsetPeriods * PERIOD_DAYS * MS_PER_DAY); const end = new Date(+start + (PERIOD_DAYS - 1) * MS_PER_DAY); const pay = new Date(end.getTime() + 6 * MS_PER_DAY); return { period_no: period_no, pay_year: pay_year, payday: toDateString(pay), period_start: toDateString(start), period_end: toDateString(end), label: `${toDateString(start)}.${toDateString(end)}`, start, end, }; } //list of all 26 periods for a full year export function listPayYear(pay_year: number, anchorISO = ANCHOR_ISO) { return Array.from({ length: PERIODS_PER_YEAR }, (_, i) => computePeriod(pay_year, i + 1, anchorISO)); }