Kalendarz wieczny
Kalendarz wieczny, także kalendarz perpetualny – tabela lub wzór pozwalająca obliczyć określony dzień tygodnia w kalendarzu gregoriańskim dla każdej daty w postaci: dzień miesiąca, miesiąc, rok.
Wzory liczące dni tygodnia
Metoda konwergencji Zellera
Kalendarz stuletni daje sprowadzić się do dość prostego algorytmu, który w pierwotnej wersji został zaproponowany przez Christiana Zellera w kolejnych publikacjach, które ukazywały się w latach 1882–1886 (m.in. w Acta Mathematica, vol.9 (1886–1887), pp.131–6). Formuła zaproponowana dla Zellera działa dla kalendarza gregoriańskiego oraz juliańskiego, jednak była przeznaczona do liczenia przez ludzi, co może rodzić problemy przy liczeniu reszty z dzielenia dla niektórych dat[1].
Uproszczony wzór Keitha
Algorytm Zellera został uproszczony i opublikowany w 1990 roku przez matematyka Mike'a Keitha do postaci[2][1]:
- nr dnia tygodnia = ([23m/9] + d + 4 + y + [z/4] - [z/100] + [z/400] - c) mod 7
- gdzie
- [ ] oznacza część całkowitą liczby
- mod – funkcja modulo (reszta z dzielenia)
- m – (ang. month) numer miesiąca (od stycznia = 1 do grudnia = 12)
- d – (ang. day) numer dnia miesiąca
- y – (ang. year) rok
- z – rok z poprawką: z = y - 1 jeżeli m < 3; z = y, jeżeli m ⩾ 3
- c – (ang. correction) korekta: c = 0, jeżeli m < 3; c = 2, jeżeli m ⩾ 3
- nr dnia tygodnia należy przeliczać następująco: 0 – niedziela, 1 – poniedziałek, 2 – wtorek, 3 – środa, 4 – czwartek, 5 – piątek, 6 – sobota.
Zaletą wzoru Mike'a Keitha jest możliwość zapisania go w języku programowania C w jednej linii liczącej raptem 45 znaków[3], co w funkcji wygląda tak:
int keith(int d, int m, int y)
{
return (d+=m<3?y--:y-2,23*m/9+d+4+y/4-y/100+y/400)%7;
}
Formuła ta dotyczy obliczeń kalendarza gregoriańskiego.
Wady uproszczonych wzorów
Należy zwrócić uwagę, że kalendarz gregoriański został wprowadzony w katolickich krajach w XVI wieku oraz stopniowo w innych krajach Europy w kolejnych wiekach[4][5]. Obliczenia kalendarza w uproszczonych wzorach nie biorą tego pod uwagę i zakładają, że liczy się dni wstecznie niezależnie tego, czy dany kalendarz obowiązywał, czy nie. Analogiczne obliczenia można wykonać dla kalendarza juliańskiego, a faktyczny dzień tygodnia musiałby być obliczony zależnie od tego który kalendarz obowiązywał w danym tygodniu[6].
Mimo ew. uwzględnienia obowiązującego kalendarza algorytm Zellera i pokrewne nie biorą pod uwagę anomalii takich jak krótki wrzesień w 1752 roku, który również musiałby być uwzględniony przy datach sprzed XIX wieku m.in. w Wielkiej Brytanii[7].
Przykładowe implementacje
Poniżej podane są przykładowe implementacje w podstawowych językach programowania.
Implementacja w Pascalu
Zapis w języku Pascal algorytmu obliczania dnia tygodnia w kalendarzu gregoriańskim (bez ww. poprawki dla kalendarza juliańskiego):
function dzien_tygodnia(Year,Month,Day:word):string;
var M,C,D,N:integer;
const week:array[0..6]of string[12]=('Niedziela','Poniedziałek','Wtorek','Środa','Czwartek','Piątek','Sobota');
begin
M := 1 + (Month + 9) mod 12 ; if M>10 then Dec(Year) ;
C := Year div 100 ; D := Year mod 100 ;
N := ((13*M-1) div 5 + D + D div 4 + C div 4 + 5*C + Day) mod 7 ;
dzien_tygodnia:=week[N];
end;
- gdzie Month, Day = numer miesiąca i dnia miesiąca, Year = czterocyfrowy zapis roku, N = kod dnia tygodnia poczynając od niedzieli (0) do soboty (6),
- mod = funkcja modulo, div = funkcja dzielenia liczb całkowitych bez reszty z zaokrągleniem w dół, if ... then - funkcja warunkowa
Często wzór Zellera jest podawany w formie, w której występuje wartość 2*C zamiast 5*C, która to forma prowadzi jednak przy niektórych latach do wartości N - ujemnych oraz nie sprawdza się dla niektórych dat.
Implementacja w C
Funkcja napisana w C na podstawie algorytmu Zellera:
char* week[7]={"Niedziela","Poniedzialek","Wtorek","Sroda","Czwartek","Piatek","Sobota"};
int zeller(int d, int m, int y, int gr){
int Y,C,M,N,D;
M=1+(9+m)%12;
Y=y-(M>10);
C=Y/100;
D=Y%100;
if (gr==1) N=((13*M-1)/5+D+D/4+C/4+5*C+d)%7;
else N=((13*M-1)/5+D+D/4+6*C+d+5)%7;
return (7+N)%7;
}
Funkcja zwraca indeks do tablicy „week” (0 dla niedzieli). Parametr „gr” oznacza rodzaj kalendarza:
- gr=1 dla gregoriańskiego,
- gr≠1 dla juliańskiego.
Inny algorytm (z tym samym efektem) na podstawie analizy tablic zamieszczonych w Małej Encyklopedii Powszechnej PWN z 1959 r.
char* week[7]={"Niedziela","Poniedzialek","Wtorek","Sroda","Czwartek","Piatek","Sobota"};
int dow(int d, int m, int y, int gr)
{
int mon[12]={0,1,1,2,5,6,2,3,4,0,1,4};
int leap;
int a,b,c;
leap=(gr!=1&&y%4==0||gr==1&&(y%4==0&&y%100!=0||y%400==0));
a=(y%100)%28;
b=(gr!=1)*(4+(y%700)/100+2*(a/4)+6*((!leap)*(1+(a%4))+(leap)*((9+m)/12)))%7+
(gr==1)*(2*(1+(y%400)/100+(a/4))+6*((!leap)*(1+(a%4))+(leap)*((9+m)/12)))%7;
c=(3*mon[m-1]+d)%7;
return (c+6*b)%7;
}
Tabele
Na podstawie wzoru Zellera można w prosty sposób utworzyć tabele, które mogą one praktycznie obejmować dowolny okres.
Przykładowy kalendarz dla lat 1901-2040:
| Opis | Przykład dla: 31 I 1901 | |
| 1. W tabeli Lata - Miesiące szukaj cyfry na przecięciu roku i miesiąca wybranej daty. | 1901/I → 1 | |
| 2. Do odszukanej cyfry dodaj dzień miesiąca otrzymując kod. | 1+31=32 | |
| 3. W tabeli Dni tygodnia szukaj kodu wskazującego dzień tygodnia. | 32 → czwartek |
| Dni tygodnia | ||||||
| Poniedziałek | 1 | 8 | 15 | 22 | 29 | 36 |
| Wtorek | 2 | 9 | 16 | 23 | 30 | 37 |
| Środa | 3 | 10 | 17 | 24 | 31 | |
| Czwartek | 4 | 11 | 18 | 25 | 32 | |
| Piątek | 5 | 12 | 19 | 26 | 33 | |
| Sobota | 6 | 13 | 20 | 27 | 34 | |
| Niedziela | 7 | 14 | 21 | 28 | 35 | |
Istnieje też inna wersja tabeli, mogąca obejmować wiele stuleci, począwszy od 1 roku n.e. Aby odnaleźć dzień tygodnia dla dowolnej daty, odnajdujemy:
1. W tablicy I a wiek, w tablicy I b rok, na skrzyżowaniu linii odczytamy cyfrę.
2. W tablicy II odczytamy cyfrę na skrzyżowaniu odnalezionej z tablicy I liczby oraz miesiąca. Dla lat przestępnych, oznaczonych kolorem czerwonym, styczeń i luty bierzemy również w ten sposób oznaczone.
3. W tablicy III na skrzyżowaniu liczby z tablicy II oraz daty, odnajdujemy dzień tygodnia poszukiwanej daty.
| b) Lata | |||||||||||||||
| 00 | 01 | 02 | 03 | 04 | 05 | ||||||||||
| 06 | 07 | 08 | 09 | 10 | 11 | ||||||||||
| 12 | 13 | 14 | 15 | 16 | |||||||||||
| 17 | 18 | 19 | 20 | 21 | 22 | ||||||||||
| 23 | 24 | 25 | 26 | 27 | |||||||||||
| 28 | 29 | 30 | 31 | 32 | 33 | ||||||||||
| 34 | 35 | 36 | 37 | 38 | 39 | ||||||||||
| 40 | 41 | 42 | 43 | 44 | |||||||||||
| 45 | 46 | 47 | 48 | 49 | 50 | ||||||||||
| 51 | 52 | 53 | 54 | 55 | |||||||||||
| 56 | 57 | 58 | 59 | 60 | 61 | ||||||||||
| 62 | 63 | 64 | 65 | 66 | 67 | ||||||||||
| 68 | 69 | 70 | 71 | 72 | |||||||||||
| 73 | 74 | 75 | 76 | 77 | 78 | ||||||||||
| 79 | 80 | 81 | 82 | 83 | |||||||||||
| 84 | 85 | 86 | 87 | 88 | 89 | ||||||||||
| a) Wieki | 90 | 91 | 92 | 93 | 94 | 95 | |||||||||
| I | Juliański | Gregoriański | 96 | 97 | 98 | 99 | |||||||||
| 0 | 7 | 14 | 17 | 21 | 25 | 6 | 0 | 1 | 2 | 3 | 4 | 5 | |||
| 1 | 8 | 15 | 5 | 6 | 0 | 1 | 2 | 3 | 4 | ||||||
| 2 | 9 | 18 | 22 | 26 | 4 | 5 | 6 | 0 | 1 | 2 | 3 | ||||
| 3 | 10 | 3 | 4 | 5 | 6 | 0 | 1 | 2 | |||||||
| 4 | 11 | 15 | 19 | 23 | 27 | 2 | 3 | 4 | 5 | 6 | 0 | 1 | |||
| 5 | 12 | 16 | 20 | 24 | 28 | 1 | 2 | 3 | 4 | 5 | 6 | 0 | |||
| 6 | 13 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | |||||||
| II | Miesiące | III | Daty | ||||||||||||
| Maj |
Sie Lut |
Lut Mar Lis |
Cze | Wrz Gru |
Kwi Lip Sty |
Sty Paź |
1 | 2 | 3 | 4 | 5 | 6 | 7 | ||
| 8 | 9 | 10 | 11 | 12 | 13 | 14 | |||||||||
| 15 | 16 | 17 | 18 | 19 | 20 | 21 | |||||||||
| 22 | 23 | 24 | 25 | 26 | 27 | 28 | |||||||||
| 29 | 30 | 31 | |||||||||||||
| 1 | 2 | 3 | 4 | 5 | 6 | 0 | 1 | 1 | Nie | Pon | Wto | Śro | Czw | Pią | Sob |
| 2 | 3 | 4 | 5 | 6 | 0 | 1 | 2 | 2 | Pon | Wto | Śro | Czw | Pią | Sob | Nie |
| 3 | 4 | 5 | 6 | 0 | 1 | 2 | 3 | 3 | Wto | Śro | Czw | Pią | Sob | Nie | Pon |
| 4 | 5 | 6 | 0 | 1 | 2 | 3 | 4 | 4 | Śro | Czw | Pią | Sob | Nie | Pon | Wto |
| 5 | 6 | 0 | 1 | 2 | 3 | 4 | 5 | 5 | Czw | Pią | Sob | Nie | Pon | Wto | Śro |
| 6 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 6 | Pią | Sob | Nie | Pon | Wto | Śro | Czw |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 0 | 0 | Sob | Nie | Pon | Wto | Śro | Czw | Pią |
Przykład: 18 listopada 2010.
Z tablicy I skrzyżować a) Wieki „20” i b) Lata „10” (tj. 2010) = 6.
Z tablicy II skrzyżować cyfrę 6 i miesiąc „Lis” (listopad) = 2.
Z tablicy III skrzyżować cyfrę 2 z cyfrą 18 tj. dzień = „Czw”, czyli że dzień 18 listopada 2010 był czwartkiem.
Zobacz też
Przypisy
- 1 2 J R Stockton, Zeller's Calendrical Works [online], merlyn.demon.co.uk, 7 września 2015 [dostęp 2023-06-22] [zarchiwizowane z adresu 2015-09-07] (ang.).
- ↑ Journal of Recreational Mathematics, Vol. 22, No. 4, 1990, p. 280
- ↑ Perpetual Calendar Algorithm [online], c2.com, 2013 [dostęp 2023-06-22] (ang.).
- ↑ Albert Van Helden, Gregorian Calendar - Chronology - The Galileo Project [online], galileo.rice.edu, 1995 [dostęp 2023-06-22].
- ↑ Gregorian calendar - Definition & Facts [online], www.britannica.com, 20 czerwca 2023 [dostęp 2023-06-22] (ang.).
- ↑ Andrew Smith, The kalendarium Package [online], Comprehensive TeX Archive Network, s. 23 [dostęp 2023-06-23] (ang.).
- ↑ Allan Kochis, COSC 1315 Fundamentals of Programming [online], www.austincc.edu [dostęp 2023-06-22] (ang.).
Bibliografia
- Omówienie wzoru Zellera. merlyn.demon.co.uk. [zarchiwizowane z tego adresu (2013-07-29)]. (ang.). na stronie Dr J R Stockton.