復活節定義為「春分後第一個滿月之後的第一個星期日」。Gauss 演算法(1800 年發表)可精確計算西元 1583 年(格里曆啟用)至 9999 年的復活節日期。
<?php
/**
* 計算指定年份的復活節日期(西方教會/格里曆)
*
* @param int $year 年份(1583-9999)
* @return DateTime|null 復活節日期,失敗回傳 null
*/
function calculateEasterSunday(int $year): ?DateTime
{
if ($year < 1583 || $year > 9999) {
return null;
}
// Gauss 演算法核心步驟
$a = $year % 19; // 黃金數
$b = (int)($year / 100); // 世紀
$c = $year % 100; // 年份在世紀內的位置
$d = (int)($b / 4); // 世紀閏年修正
$e = $b % 4;
$f = (int)(($b + 8) / 25); // 朔望月修正
$g = (int)(($b - $f + 1) / 3);
$h = (19 * $a + $b - $d - $g + 15) % 30; // 朔望月日期
$i = (int)($c / 4);
$k = $c % 4;
$l = (32 + 2 * $e + 2 * $i - $h - $k) % 7; // 星期幾修正
$m = (int)(($a + 11 * $h + 22 * $l) / 451); // 例外修正
$month = (int)(($h + $l - 7 * $m + 114) / 31); // 3=三月, 4=四月
$day = (($h + $l - 7 * $m + 114) % 31) + 1;
return DateTime::createFromFormat('Y-m-d', sprintf('%04d-%02d-%02d', $year, $month, $day));
}
// 批次驗證 2020-2030 年
for ($y = 2020; $y <= 2030; $y++) {
$date = calculateEasterSunday($y);
echo $date ? $date->format('Y-m-d (D)') : "Year $y error", "\n";
}
/*
2020-04-12 (Sun)
2021-04-04 (Sun)
2022-04-17 (Sun)
2023-04-09 (Sun)
2024-03-31 (Sun)
2025-04-20 (Sun)
2026-04-05 (Sun)
2027-03-28 (Sun)
2028-04-16 (Sun)
2029-04-01 (Sun)
2030-04-21 (Sun)
*/
function getLiturgicalDates(int $year): array
{
$easter = calculateEasterSunday($year);
if (!$easter) return [];
$dates = ['easter' => clone $easter];
$intervals = [
'ash_wednesday' => '-46 days', // 聖灰星期三
'palm_sunday' => '-7 days', // 棕枝主日
'maundy_thursday' => '-3 days', // 濯足星期四
'good_friday' => '-2 days', // 受難節
'holy_saturday' => '-1 day', // 聖週六
'ascension' => '+39 days', // 升天節
'pentecost' => '+49 days', // 五旬節
'corpus_christi' => '+60 days', // 基督聖體節
];
foreach ($intervals as $name => $interval) {
$dt = clone $easter;
$dt->modify($interval);
$dates[$name] = $dt;
}
return $dates;
}
演算法純整數運算,極快(微秒級)。若頻繁查詢同年份,建議以陣列或 Redis 快取 year => date 對應。