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 edytuj

Metoda konwergencji Zellera edytuj

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 18821886 (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 edytuj

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 edytuj

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 edytuj

Poniżej podane są przykładowe implementacje w podstawowych językach programowania.

Implementacja w Pascalu edytuj

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 edytuj

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;
}

Kalendarze stuletnie edytuj

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[potrzebny przypis].

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
Lata Miesiące
Pochyła czcionka oznacza lata przestępne I II III IV V VI VII VIII IX X XI XII
1901 1929 1957 1985 2013 1 4 4 0 2 5 0 3 6 1 4 6
1902 1930 1958 1986 2014 2 5 5 1 3 6 1 4 0 2 5 0
1903 1931 1959 1987 2015 3 6 6 2 4 0 2 5 1 3 6 1
1904 1932 1960 1988 2016 4 0 1 4 6 2 4 0 3 5 1 3
1905 1933 1961 1989 2017 6 2 2 5 0 3 5 1 4 6 2 4
1906 1934 1962 1990 2018 0 3 3 6 1 4 6 2 5 0 3 5
1907 1935 1963 1991 2019 1 4 4 0 2 5 0 3 6 1 4 6
1908 1936 1964 1992 2020 2 5 6 2 4 0 2 5 1 3 6 1
1909 1937 1965 1993 2021 4 0 0 3 5 1 3 6 2 4 0 2
1910 1938 1966 1994 2022 5 1 1 4 6 2 4 0 3 5 1 3
1911 1939 1967 1995 2023 6 2 2 5 0 3 5 1 4 6 2 4
1912 1940 1968 1996 2024 0 3 4 0 2 5 0 3 6 1 4 6
1913 1941 1969 1997 2025 2 5 5 1 3 6 1 4 0 2 5 0
1914 1942 1970 1998 2026 3 6 6 2 4 0 2 5 1 3 6 1
1915 1943 1971 1999 2027 4 0 0 3 5 1 3 6 2 4 0 2
1916 1944 1972 2000 2028 5 1 2 5 0 3 5 1 4 6 2 4
1917 1945 1973 2001 2029 0 3 3 6 1 4 6 2 5 0 3 5
1918 1946 1974 2002 2030 1 4 4 0 2 5 0 3 6 1 4 6
1919 1947 1975 2003 2031 2 5 5 1 3 6 1 4 0 2 5 0
1920 1948 1976 2004 2032 3 6 0 3 5 1 3 6 2 4 0 2
1921 1949 1977 2005 2033 5 1 1 4 6 2 4 0 3 5 1 3
1922 1950 1978 2006 2034 6 2 2 5 0 3 5 1 4 6 2 4
1923 1951 1979 2007 2035 0 3 3 6 1 4 6 2 5 0 3 5
1924 1952 1980 2008 2036 1 4 5 1 3 6 1 4 0 2 5 0
1925 1953 1981 2009 2037 3 6 6 2 4 0 2 5 1 3 6 1
1926 1954 1982 2010 2038 4 0 0 3 5 1 3 6 2 4 0 2
1927 1955 1983 2011 2039 5 1 1 4 6 2 4 0 3 5 1 3
1928 1956 1984 2012 2040 6 2 3 6 1 4 6 2 5 0 3 5
Pochyła czcionka oznacza lata przestępne I II III IV V VI VII VIII IX X XI XII
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

Zobacz też edytuj

Przypisy edytuj

  1. a b 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.).
  2. Journal of Recreational Mathematics, Vol. 22, No. 4, 1990, p. 280
  3. Perpetual Calendar Algorithm [online], c2.com, 2013 [dostęp 2023-06-22] (ang.).
  4. Albert Van Helden, Gregorian Calendar - Chronology - The Galileo Project [online], galileo.rice.edu, 1995 [dostęp 2023-06-22].
  5. Gregorian calendar - Definition & Facts [online], www.britannica.com, 20 czerwca 2023 [dostęp 2023-06-22] (ang.).
  6. Andrew Smith, The kalendarium Package [online], Comprehensive TeX Archive Network, s. 23 [dostęp 2023-06-23] (ang.).
  7. Allan Kochis, COSC 1315 Fundamentals of Programming [online], www.austincc.edu [dostęp 2023-06-22] (ang.).

Bibliografia edytuj