Files
app/api/includes/CzechHolidays.php
2026-03-12 12:43:56 +01:00

118 lines
4.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Czech Holidays & Work Fund Calculator
*
* Provides Czech public holidays (including movable Easter dates)
* and monthly work fund calculations.
*/
declare(strict_types=1);
class CzechHolidays
{
/** @var array<int, list<string>> 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<string>
*/
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);
}
}