> Static cache for holidays by year */ private static array $holidayCache = []; /** * Get all Czech public holidays for a given year. * Returns array of 'Y-m-d' strings (11 fixed + 2 Easter-based). * Results are cached per-request to avoid recalculation. * * @return list */ public static function getHolidays(int $year): array { if (isset(self::$holidayCache[$year])) { return self::$holidayCache[$year]; } // Fixed holidays $holidays = [ sprintf('%04d-01-01', $year), // Den obnovy samostatného českého státu sprintf('%04d-05-01', $year), // Svátek práce sprintf('%04d-05-08', $year), // Den vítězství sprintf('%04d-07-05', $year), // Den slovanských věrozvěstů Cyrila a Metoděje sprintf('%04d-07-06', $year), // Den upálení mistra Jana Husa sprintf('%04d-09-28', $year), // Den české státnosti sprintf('%04d-10-28', $year), // Den vzniku samostatného československého státu sprintf('%04d-11-17', $year), // Den boje za svobodu a demokracii sprintf('%04d-12-24', $year), // Štědrý den sprintf('%04d-12-25', $year), // 1. svátek vánoční sprintf('%04d-12-26', $year), // 2. svátek vánoční ]; // Easter-based holidays (Anonymous Gregorian algorithm) $easterSunday = self::getEasterSunday($year); $goodFriday = date('Y-m-d', strtotime($easterSunday . ' -2 days')); $easterMonday = date('Y-m-d', strtotime($easterSunday . ' +1 day')); $holidays[] = $goodFriday; // Velký pátek $holidays[] = $easterMonday; // Velikonoční pondělí sort($holidays); self::$holidayCache[$year] = $holidays; return $holidays; } /** * Check if a date is a Czech public holiday. */ public static function isHoliday(string $date): bool { $year = (int)date('Y', strtotime($date)); $formatted = date('Y-m-d', strtotime($date)); return in_array($formatted, self::getHolidays($year), true); } /** * Get number of business days (Mon-Fri, excluding holidays) in a month. */ public static function getBusinessDaysInMonth(int $year, int $month): int { $holidays = self::getHolidays($year); $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); $businessDays = 0; for ($day = 1; $day <= $daysInMonth; $day++) { $date = sprintf('%04d-%02d-%02d', $year, $month, $day); $dayOfWeek = (int)date('N', strtotime($date)); // 1=Mon, 7=Sun if ($dayOfWeek <= 5 && !in_array($date, $holidays, true)) { $businessDays++; } } return $businessDays; } /** * Get monthly work fund in hours (business days × 8). */ public static function getMonthlyWorkFund(int $year, int $month): float { return self::getBusinessDaysInMonth($year, $month) * 8.0; } /** * Calculate Easter Sunday date using the Anonymous Gregorian algorithm. * Returns 'Y-m-d' string. */ private static function getEasterSunday(int $year): string { $a = $year % 19; $b = intdiv($year, 100); $c = $year % 100; $d = intdiv($b, 4); $e = $b % 4; $f = intdiv($b + 8, 25); $g = intdiv($b - $f + 1, 3); $h = (19 * $a + $b - $d - $g + 15) % 30; $i = intdiv($c, 4); $k = $c % 4; $l = (32 + 2 * $e + 2 * $i - $h - $k) % 7; $m = intdiv($a + 11 * $h + 22 * $l, 451); $month = intdiv($h + $l - 7 * $m + 114, 31); $day = (($h + $l - 7 * $m + 114) % 31) + 1; return sprintf('%04d-%02d-%02d', $year, $month, $day); } }