/** * Czech public holidays & work fund calculator. * Port of PHP CzechHolidays class. */ const holidayCache = new Map(); /** Easter Sunday using the Anonymous Gregorian algorithm */ function getEasterSunday(year: number): string { const a = year % 19; const b = Math.floor(year / 100); const c = year % 100; const d = Math.floor(b / 4); const e = b % 4; const f = Math.floor((b + 8) / 25); const g = Math.floor((b - f + 1) / 3); const h = (19 * a + b - d - g + 15) % 30; const i = Math.floor(c / 4); const k = c % 4; const l = (32 + 2 * e + 2 * i - h - k) % 7; const m = Math.floor((a + 11 * h + 22 * l) / 451); const month = Math.floor((h + l - 7 * m + 114) / 31); const day = ((h + l - 7 * m + 114) % 31) + 1; return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`; } /** All Czech public holidays for a year (11 fixed + 2 Easter-based) */ export function getHolidays(year: number): string[] { if (holidayCache.has(year)) return holidayCache.get(year)!; const y = String(year); const holidays = [ `${y}-01-01`, // Den obnovy samostatného českého státu `${y}-05-01`, // Svátek práce `${y}-05-08`, // Den vítězství `${y}-07-05`, // Den slovanských věrozvěstů Cyrila a Metoděje `${y}-07-06`, // Den upálení mistra Jana Husa `${y}-09-28`, // Den české státnosti `${y}-10-28`, // Den vzniku samostatného československého státu `${y}-11-17`, // Den boje za svobodu a demokracii `${y}-12-24`, // Štědrý den `${y}-12-25`, // 1. svátek vánoční `${y}-12-26`, // 2. svátek vánoční ]; // Easter-based const easterSunday = getEasterSunday(year); const easterDate = new Date(easterSunday); const goodFriday = new Date(easterDate); goodFriday.setDate(goodFriday.getDate() - 2); const easterMonday = new Date(easterDate); easterMonday.setDate(easterMonday.getDate() + 1); const fmt = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`; holidays.push(fmt(goodFriday)); // Velký pátek holidays.push(fmt(easterMonday)); // Velikonoční pondělí holidays.sort(); holidayCache.set(year, holidays); return holidays; } /** Check if a date string (YYYY-MM-DD) is a Czech public holiday */ export function isHoliday(dateStr: string): boolean { const year = parseInt(dateStr.substring(0, 4), 10); return getHolidays(year).includes(dateStr); } /** Business days in a month (Mon-Fri excluding public holidays) */ export function getBusinessDaysInMonth( year: number, month: number, upToDay?: number, ): number { const holidays = getHolidays(year); let count = 0; const daysInMonth = new Date(year, month + 1, 0).getDate(); const maxDay = upToDay ? Math.min(upToDay, daysInMonth) : daysInMonth; for (let day = 1; day <= maxDay; day++) { const date = new Date(year, month, day); const dow = date.getDay(); if (dow !== 0 && dow !== 6) { const dateStr = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`; if (!holidays.includes(dateStr)) { count++; } } } return count; } /** Monthly work fund in hours (business days × 8) */ export function getMonthlyWorkFund( year: number, month: number, upToDay?: number, ): number { return getBusinessDaysInMonth(year, month, upToDay) * 8; }