Kalendarz wieczny
Ten artykuł należy dopracować |
Kalendarz wieczny, także kalendarz perpetualny – tabela lub wzór pozwalająca w prosty sposób odnaleźć określony dzień tygodnia w kalendarzu gregoriańskim dla każdej daty w postaci dzień miesiąca, miesiąc, rok. Przy pomocy wiecznego kalendarza nie ma możliwości obliczenia np: roku na podstawie daty w postaci: dzień tygodnia, dzień miesiąca - gdyż dni tygodnia dla danego dnia miesiąca powtarzają się acyklicznie w kalendarzu gregoriańskim.
Uniwersalny wzór Zellera dla lat 1 - 9999 n.e.
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).
Algorytm Zellera został uproszczony przez matematyka Mike'a Keitha do postaci:
- dzień 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 – numer miesiąca (ang. month) (od stycznia = 1 do grudnia = 12)
- d – numer dnia (ang. day) miesiąca
- y – rok (ang. year)
- z – rok z poprawką: z = y - 1 jeżeli m < 3; z = y, jeżeli m >= 3
- c – korekta (ang. correction): c = 0, jeżeli m < 3; c = 2, jeżeli m >= 3
- dni tygodnia ze zbioru {0, 1, 2, 3, 4, 5, 6}, gdzie: 0 – wtorek, 1 – środa, 2 – czwartek, 3 – piątek, 4 – sobota, 5 – niedziela, 6 – poniedziałek
- gdzie
Zaletą wzoru Mike'a Keitha jest możliwość zapisania go w języku programowania C w jednej linii liczącej raptem 45 znaków:
- (d+=m<3?y--:y-2,23*m/9+d+4+y/4-y/100+y/400)%7
która to linia kodu generuje wartość kodującą dzień tygodnia od wtorku (0) do poniedziałku (6)
Aby ten wzór był poprawny dla lat od 1 do 1582 trzeba uwzględnić różnicę pomiędzy kalendarzem juliańskim a gregoriańskim, która wynosi C - C div 4 - 2 (po przesunięciu początku roku do 1 marca)
Wzór ten został opublikowany w Journal of Recreational Mathematics, Vol. 22, No. 4, 1990, p. 280.
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
Oto funkcja napisana na podstawie algorytmu Zellera
char* week[7]={"Poniedzialek","Wtorek","Sroda","Czwartek","Piatek","Sobota","Niedziela"};
int zeller(int d,int m,int y,int s){
int Y,C,M,N,D;
M=1+(9+m)%12;
Y=y-(M>10);
C=Y/100;
D=Y%100;
if (s!=0) 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;
}
Oto funkcja napisana na podstawie analizy tablic zamieszczonych w Małej Encyklopedii Powszechnej PWN z 1959 r.
char* week[7]={"Poniedzialek","Wtorek","Sroda","Czwartek","Piatek","Sobota","Niedziela"};
int dow(int m, int d, int y, int s)
{
int mon[12]={0,1,1,2,5,6,2,3,4,0,1,4};
int leap;
int a,b,c;
leap=(s==0&&y%4==0||s!=0&&(y%4==0&&y%100!=0||y%400==0));
a=(y%100)%28;
b=(s==0)*(4+(y%700)/100+2*(a/4)+6*((!leap)*(1+(a%4))+(leap)*((9+m)/12)))%7+
(s!=0)*(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;
}
Funkcja zwraca indeks do tablicy "week". Parametr s oznacza styl
- s==0 dla stylu juliańskiego
- s!=0 dla stylu gregoriańskiego
Implementacja w Ocamlu
Oto funkcja napisana na podstawie wzoru Mike'a Keitha:
type date = {day : int; month : int; year : int;}
;;
(* funkcja zwraca dzień tygodnia na podstawie podanej daty,
przy czym: 0 - niedziela, 1 - poniedziałek, ..., 6 - sobota *)
let weekday
(time : date) =
let z = if time.month < 3 then time.year - 1 else time.year in
let x =
((23 * time.month) / 9) + time.day + 4 + time.year + (z / 4) - (z / 100) + (z / 400)
in
if time.month < 3 then x mod 7 else (x - 2) mod 7
;;
Kalendarze stuletnie
Na podstawie wzoru Zellera można w prosty sposób utworzyć tabele, które bywają nazywane kalendarzami stuletnimi choć mogą one praktycznie obejmować dowolny okres. Przykładowy "kalendarz stuletni" (a właściwie stuczterdziestoletni) 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 |
Bibliografia
- Omówienie wzoru Zellera. merlyn.demon.co.uk. [zarchiwizowane z tego adresu (2013-07-29)]. na stronie Dr J R Stockton.
Media użyte na tej stronie
Icon of articles which need more text