Przejdź do treści

Jak obliczyć datę Wielkanocy – computus, algorytm Gaussa i ruchome święta

W poprzednim wpisie – o wiecznym kalendarzu – pojawił się wątek, który aż prosi się o dokończenie. Cała reforma gregoriańska z 1582 roku wzięła się z tego, że Kościołowi „uciekała” równonoc wiosenna, a od niej liczy się datę Wielkanocy. Skoro poprzednio naprawiliśmy kalendarz, policzmy teraz samą Wielkanoc.

To zadanie ma zresztą wyjątkowy rodowód: computus – bo tak przez wieki nazywano rachunek daty Wielkanocy – to prawdopodobnie najstarszy „algorytm” w historii, liczony ręcznie od starożytności, na długo zanim ktokolwiek wymyślił słowo „informatyka”. A przy okazji jest praktyczny do dziś: z daty Wielkanocy wynikają wszystkie ruchome święta, w tym dwa polskie dni wolne od pracy.

Reguła paschalna: Słońce i Księżyc naraz

Sama zasada, ustalona w tradycji soboru nicejskiego (325 r.), mieści się w jednym zdaniu:

Wielkanoc wypada w pierwszą niedzielę po pierwszej wiosennej pełni Księżyca – czyli po pierwszej pełni przypadającej 21 marca lub później.

I to właśnie dlatego jest trudna. Kalendarz gregoriański śledzi Słońce (pory roku), ale reguła paschalna miesza do tego Księżyc – a miesiąc księżycowy (~29,53 dnia) nie składa się w całe dni ani w równe miesiące. Do tego dochodzi dzień tygodnia (niedziela), czyli trzeci, niezależny cykl. Wielkanoc jest punktem przecięcia trzech zegarów naraz – i dlatego skacze po kalendarzu w zakresie aż 35 dni: od 22 marca do 25 kwietnia.

Skrajności zdarzają się rzadko. Wielkanoc 22 marca była ostatnio w 1818 roku (następna dopiero w 2285!), a 25 kwietnia – w 1943 roku. Najbliższy skrajny termin przed nami: 25 kwietnia 2038.

Pełnia kościelna ≠ pełnia astronomiczna. Computus nie patrzy w niebo. Liczy tzw. pełnię ekleziastyczną – wyznaczoną z tablic, a nie z obserwacji – i „równonoc kościelną" sztywno przypisaną do 21 marca (astronomiczna potrafi wypaść 19 lub 20 marca). Zwykle oba księżyce się zgadzają, ale bywają lata, gdy astronomiczna pełnia dałaby inną niedzielę niż tablicowa. Obowiązuje tablica – dzięki temu datę da się policzyć algorytmem, bez efemeryd.

Cykl Metona: dlaczego 19 lat

Podstawą tablic paschalnych jest odkryty w starożytności cykl Metona: 19 lat słonecznych to niemal dokładnie 235 miesięcy księżycowych. Po 19 latach fazy Księżyca wracają więc na te same daty kalendarza – wystarczy znać pozycję roku w tym cyklu, żeby „wiedzieć”, kiedy będzie pełnia. Tę pozycję nazwano złotą liczbą: (rok mod 19) + 1.

Cykl Metona nie jest idealny (myli się o ~1 dzień na 310 lat), a reguła 4/100/400 dokłada własne przesunięcia – dlatego pełny computus gregoriański zawiera jeszcze dwie poprawki (tzw. równanie słoneczne i księżycowe), przesuwające tablice przy niektórych pełnych stuleciach. Z tego zamieszania bierze się zdumiewający fakt: o ile dni tygodnia kalendarza gregoriańskiego powtarzają się co 400 lat, o tyle pełny cykl dat Wielkanocy zamyka się dopiero po… 5 700 000 lat. W kalendarzu juliańskim, bez poprawek stuleci, wystarczały 532 lata (19 × 28) – i takim cyklem do dziś posługują się Kościoły prawosławne, dlatego prawosławna Wielkanoc zwykle wypada w innym terminie (w 2026 roku: 12 kwietnia, tydzień po zachodniej).

Algorytm Gaussa

Pierwszą zamkniętą formułę – zamiast tablic – podał w 1800 roku Carl Friedrich Gauss. Jego algorytm wyznacza najpierw d: liczbę dni od 21 marca do pełni paschalnej, a potem e: ile dni brakuje do najbliższej niedzieli. Stałe M i N kodują poprawki stulecia (dla lat 1900–2099 wynoszą M = 24, N = 5, ale liczymy je ogólnie):

from datetime import date

def easter_sunday(year: int) -> date:
    """Data Wielkanocy w kalendarzu gregoriańskim (algorytm Gaussa)."""
    a = year % 19         # pozycja w cyklu Metona
    b = year % 4
    c = year % 7
    k = year // 100
    p = (13 + 8 * k) // 25
    q = k // 4
    M = (15 - p + k - q) % 30      # poprawka księżycowa stulecia
    N = (4 + k - q) % 7            # poprawka dnia tygodnia stulecia
    d = (19 * a + M) % 30          # dni od 21 III do pełni paschalnej
    e = (2 * b + 4 * c + 6 * d + N) % 7   # dni do niedzieli
    # dwa historyczne wyjątki Gaussa:
    if d == 29 and e == 6:
        return date(year, 4, 19)
    if d == 28 and e == 6 and (11 * M + 11) % 30 < 19:
        return date(year, 4, 18)
    day = 22 + d + e               # licząc od 22 marca
    return date(year, 3, day) if day <= 31 else date(year, 4, day - 31)

easter_sunday(2026)  # date(2026, 4, 5)  – 5 kwietnia
easter_sunday(2025)  # date(2025, 4, 20)
easter_sunday(2038)  # date(2038, 4, 25) – najpóźniejszy możliwy termin

Skąd wyjątki? Reguła paschalna mówi „pierwsza niedziela po pełni”, a arytmetyka modulo 30 potrafi w rzadkich konfiguracjach wypchnąć wynik na 26 kwietnia – poza dozwolony zakres – albo dać dwie identyczne daty w jednym cyklu Metona. Tablice kościelne rozstrzygały te przypadki specjalną zasadą i Gauss musiał ją dokleić do wzoru ręcznie.

Algorytm Meeusa – bez wyjątków

W 1876 roku anonimowy czytelnik czasopisma „Nature” nadesłał wersję, która wchłania oba wyjątki w czystą arytmetykę całkowitą – dziś znana jest jako algorytm Meeusa/Jonesa/Butchera i to ją najczęściej spotkasz w bibliotekach. Wygląda jak zaklęcie, ale to dokładnie ten sam computus, tylko sprytniej upakowany (kluczem jest składnik m, który „zjada” przypadki graniczne):

function easterSunday(year) {
	const a = year % 19,
		b = Math.trunc(year / 100), c = year % 100,
		d = Math.trunc(b / 4), e = b % 4,
		f = Math.trunc((b + 8) / 25),
		g = Math.trunc((b - f + 1) / 3),
		h = (19 * a + b - d - g + 15) % 30,   // pełnia paschalna
		i = Math.trunc(c / 4), k = c % 4,
		l = (32 + 2 * e + 2 * i - h - k) % 7, // dni do niedzieli
		m = Math.trunc((a + 11 * h + 22 * l) / 451), // korekta wyjątków
		n = h + l - 7 * m + 114;
	return new Date(year, Math.trunc(n / 31) - 1, (n % 31) + 1);
}

easterSunday(2026); // 5 kwietnia 2026

Oba algorytmy dają identyczne wyniki dla każdego roku kalendarza gregoriańskiego – Gauss jest lepszy do zrozumienia dlaczego to działa, Meeus do wklejenia w produkcyjny kod.

Z jednej daty – wszystkie ruchome święta

Gdy mamy Wielkanoc, resztę ruchomych świąt dostajemy za darmo – to stałe przesunięcia:

ŚwiętoPrzesunięcieW 2026Wolne w PL?
Popielec−46 dni18 lutegonie
Wielki Piątek−2 dni3 kwietnianie
Wielkanoc05 kwietniatak (niedziela)
Poniedziałek Wielkanocny+1 dzień6 kwietniatak
Zielone Świątki+49 dni24 majatak (niedziela)
Boże Ciało+60 dni4 czerwcatak

W kodzie to jedna linijka na święto – i gotowy moduł dni wolnych, którego prędzej czy później potrzebuje każdy system urlopowy czy grafik zmian:

from datetime import timedelta

easter = easter_sunday(2026)         # 5 kwietnia
easter + timedelta(days=-46)         # Popielec – 18 lutego
easter + timedelta(days=+1)          # Poniedziałek Wielkanocny – 6 kwietnia
easter + timedelta(days=+49)         # Zielone Świątki – 24 maja
easter + timedelta(days=+60)         # Boże Ciało – 4 czerwca (zawsze czwartek)

Miła własność arytmetyki modulo 7: skoro Wielkanoc to zawsze niedziela, to Boże Ciało (+60, a 60 mod 7 = 4) wypada zawsze w czwartek, a Zielone Świątki (+49, wielokrotność 7) – zawsze w niedzielę. Kalendarz liturgiczny ma wbudowaną niezmienniczość.

Gotowe funkcje w językach

PHP – jako jedyny popularny język – ma computus wbudowany w bibliotekę standardową (rozszerzenie calendar):

<?php
echo date('d.m.Y', easter_date(2026));  // 05.04.2026
echo easter_days(2026);                 // 15 – dni po 21 marca
// data bez ograniczeń easter_date():
$easter = (new DateTime('2026-03-21'))->modify('+' . easter_days(2026) . ' days');

Uwaga na pułapkę: easter_date() zwraca uniksowy timestamp, więc działa tylko dla lat 1970–2037 (poza zakresem PHP 8 rzuca ValueError). Bezpieczniejsze jest easter_days() – zwraca liczbę dni po 21 marca i nie ma żadnych ograniczeń zakresu.

W Pythonie computus mieszka w praktycznie wszechobecnym pakiecie python-dateutil – z bonusem w postaci wszystkich trzech wariantów obrządku:

from dateutil.easter import easter, EASTER_ORTHODOX

easter(2026)                    # datetime.date(2026, 4, 5)
easter(2026, EASTER_ORTHODOX)   # datetime.date(2026, 4, 12) – prawosławna

JavaScript, Java i C# wbudowanego computusu nie mają – i to dobra okazja, żeby wkleić własnego Meeusa z tego wpisu albo sięgnąć po bibliotekę świąt (w ekosystemie każdego z tych języków istnieją gotowe pakiety „holidays”).

Rok 2038 wraca jak bumerang. Limit easter_date() kończy się na 2037 nieprzypadkowo – to granica 32‑bitowego uniksowego timestampa, który „przekręca się" 19 stycznia 2038. Ten sam rok, w którym Wielkanoc wypadnie w najpóźniejszym możliwym terminie (25 kwietnia). Problemowi roku 2038 – i w ogóle temu, jak komputery liczą sekundy, a nie dni – planuję poświęcić osobny wpis.

Podsumowanie

  • Reguła paschalna – „pierwsza niedziela po pierwszej wiosennej pełni” – splata trzy niezależne cykle: słoneczny, księżycowy i tygodniowy; stąd rozrzut dat od 22 marca do 25 kwietnia.
  • Computus liczy pełnię kościelną (tablicową), nie astronomiczną – dlatego w ogóle da się go zapisać algorytmem.
  • Fundament to cykl Metona (19 lat ≈ 235 miesięcy księżycowych) plus gregoriańskie poprawki stuleci; pełny cykl dat Wielkanocy to aż 5,7 mln lat.
  • Gauss (1800) dał pierwszy wzór (z dwoma wyjątkami), anonimowy algorytm Meeusa (1876) – wersję bez wyjątków, używaną w bibliotekach do dziś.
  • Z jednej daty wynikają wszystkie ruchome święta – od Popielca (−46) po Boże Ciało (+60, zawsze czwartek).
  • W PHP computus jest wbudowany (easter_date/easter_days – z pułapką zakresu 1970–2037), w Pythonie masz dateutil.easter; w JS/Javie/C# – piszesz Meeusa sam.

Skąd wzięły się reguła 4/100/400, wycinane dni i sam kształt naszego kalendarza – o tym w poprzednim wpisie: wieczny kalendarz.