Kalendarz wieczny

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

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:

OpisPrzykł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
LataMiesiące
Pochyła czcionka oznacza lata przestępneIIIIIIIVVVIVIIVIIIIXXXIXII
19011929195719852013144025036146
19021930195819862014255136140250
19031931195919872015366240251361
19041932196019882016401462403513
19051933196119892017622503514624
19061934196219902018033614625035
19071935196319912019144025036146
19081936196419922020256240251361
19091937196519932021400351362402
19101938196619942022511462403513
19111939196719952023622503514624
19121940196819962024034025036146
19131941196919972025255136140250
19141942197019982026366240251361
19151943197119992027400351362402
19161944197220002028512503514624
19171945197320012029033614625035
19181946197420022030144025036146
19191947197520032031255136140250
19201948197620042032360351362402
19211949197720052033511462403513
19221950197820062034622503514624
19231951197920072035033614625035
19241952198020082036145136140250
19251953198120092037366240251361
19261954198220102038400351362402
19271955198320112039511462403513
19281956198420122040623614625035
Pochyła czcionka oznacza lata przestępneIIIIIIIVVVIVIIVIIIIXXXIXII
Dni tygodnia
Poniedziałek1815222936
Wtorek2916233037
Środa310172431
Czwartek411182532
Piątek512192633
Sobota613202734
Niedziela714212835

Bibliografia

Media użyte na tej stronie

Add more text icon.svg
Icon of articles which need more text