The beauty of all the algorithms is that they are with integer
arithmetic, which can be handled easily by TeX. I have compiled a
sample (see listing below) of how to implement it in LaTeX (not
optimized yet). If anyone is interested I can:
(a) Make a LaTeX package, but I think calender and date packages can
use it better by implementing the relevant stuff in their own
packages.
Have especialy a look at the day of the week algorithms.
(b) Make a package for historical calenders. Who on earth wants
something
like that (maybe a few nuts like I)?
(c) A TeX showcase example of the English calender of September 1752,
which caused a lot of outrage and one of the first industrial
actions in history. Might be nice if set in a "fount" of the
time.
Any comments?
Danie Els
(dnjels at sun dot ac dot za)
%====================================================
\documentclass[a4paper]{article}
\usepackage{color}
\definecolor{CalCol}{gray}{0.8}
%==== Julian Date Algorithms ==================================
\makeatletter
\usepackage{calc}
% Temporary counters. LaTeX makes all counter operations global.
% This prohibits the reuse of counter for nested calculations
% and we end up with a lot of temporary counters. Re-implementation
% of standerd TeX calculations in place of the calc packges may
% be faster and with less counters.
\newcounter{JD@a}
\newcounter{JD@b}
\newcounter{JD@c}
\newcounter{JD@d}
\newcounter{JD@e}
\newcounter{JD@y}
\newcounter{JD@m}
\newcounter{JD@today}
% Define macro that performs modulo arithmetic on <cntr>
% USAGE: \modcounter{<cntr>}{<value>}
\newcommand*{\modcounter}[2]{%
\setcounter{#1}{\value{#1}-(\value{#1}/(#2))*(#2)}}
% Define the command \JDsetlongdate to build a date in
% the yyyymmdd format. Return the counter <date_cntr> with the
% date in long format.
% USAGE: \JDsetlongdate{<date_cntr>}{<year>}{<month>}{<day>}
\newcommand*{\JDsetlongdate}[4]{%
\setcounter{#1}{(#2)*10000+(#3)*100+(#4)}}
% Define counters for the reference Gregorian epoch
% and a command \SetGregEpoch to change them globaly.
% USAGE: \SetGregEpoch{<year>}{<month>}{<day>}
\newcounter{JDGregDate}
\newcounter{JDGregNumb}
\newcommand*{\SetGregEpoch}[3]{%
\JDsetlongdate{JDGregDate}{#1}{#2}{#3}%
\DatetoJD{JDGregNumb}{#1}{#2}{#3}}
% Calculate the Julian day number (days since 1 January 4713BC).
% Note that there are no error checking. The user must ensure that
% all input dates are valid in terms of days in the month and
% months in the year and that the given date does not fall in the
% days that where dropped when the specific calender changed from
% Julian to Gregorian.
% The algorithm is fine for all dates since 1AD. For dates before
% 1AD it must be remembered that there was no year zero and that the
% year before 1AD was 1BC. To use a BC date, the date needs to be
% converted to a negative year, e.g.: 10BC = -9.
% The Julian calender was introduced in 45BC by Julius Caesar,
% but because of administrative errors, the use of leap years was
% only properly implemented after 8AD or 12AD. It makes therefore no
% sense to use this algorithm to calculate a historically correct
% calender for years before 12AD.
% USAGE: \DatetoJD{<JD_cntr>}{<year>}{<month>}{<day>}
\newcommand*{\DatetoJD}[4]{%
\JDsetlongdate{JD@today}{#2}{#3}{#4}%
\setcounter{JD@a}{(14-(#3))/12}%
\setcounter{JD@y}{(#2)+4800-\c@JD@a}%
\setcounter{JD@m}{(#3)+12*\c@JD@a-3}%
\setcounter{#1}{(#4)+(153*\c@JD@m+2)/5+365*\c@JD@y+\c@JD@y/4}%
\ifnum\c@JD@today<\c@JDGregDate
\addtocounter{#1}{-32083}%
\else
\addtocounter{#1}{\c@JD@y/400-\c@JD@y/100-32045}%
\fi}
% For a given Julian day number find the year, month and day.
% USAGE: \JDtoDate{<JDnumber>}{<y_cntr>}{<m_cntr>}{<d_cntr>}
\newcommand*{\JDtoDate}[4]{%
\setcounter{JD@today}{#1}%
\ifnum\c@JD@today<\c@JDGregNumb
\setcounter{JD@b}{0}%
\setcounter{JD@c}{(#1)+32082}%
\else
\setcounter{JD@a}{(#1)+32044}%
\setcounter{JD@b}{(4*\c@JD@a+3)/146097}%
\setcounter{JD@c}{\c@JD@a-(146097*\c@JD@b)/4}%
\fi
\setcounter{JD@d}{(4*\c@JD@c+3)/1461}%
\setcounter{JD@e}{\c@JD@c-(1461*\c@JD@d)/4}%
\setcounter{JD@m}{(5*\c@JD@e+2)/153}%
%
\setcounter{#2}{100*\c@JD@b+\c@JD@d-4800+\c@JD@m/10}%
\setcounter{#3}{\c@JD@m+3-12*(\c@JD@m/10)}%
\setcounter{#4}{\c@JD@e-(153*\c@JD@m+2)/5+1}}
% Calculate the day of the week given <year><month><day>.
% Returns counter <wday_cntr> with 0=Sunday, 1=Monday, ...
% USAGE: \DatetoWeekday{<wday_cntr>}{<year>}{<month>}{<day>}
\newcommand*{\DatetoWeekday}[4]{%
\JDsetlongdate{JD@today}{#2}{#3}{#4}%
\setcounter{JD@a}{(14-(#3))/12}%
\setcounter{JD@y}{(#2)-\c@JD@a}%
\setcounter{JD@m}{(#3)+12*\c@JD@a-2}%
\setcounter{#1}{(#4)+\c@JD@y+\c@JD@y/4+(31*\c@JD@m)/12}%
\ifnum\c@JD@today<\c@JDGregDate
\addtocounter{#1}{5}%
\else
\addtocounter{#1}{\c@JD@y/400-\c@JD@y/100}%
\fi
\modcounter{#1}{7}}
% Calculate the day of the week given the JD number <JDnumber>.
% Returns counter <wday_cntr> with 0=Sunday, 1=Monday, ...
% USAGE: \JDtoWeekday{<wday_cntr>}{<JDnumber>}
\newcommand*{\JDtoWeekday}[2]{%
\setcounter{#1}{(#2)+1}%
\modcounter{#1}{7}}
% Give day and month names. This commands can be added
% to the babel \date<language> token (\extras<language> perhaps?).
\newcommand*{\DayName}[1]{%
\ifcase#1
Sunday\or Monday\or Tuesday \or Wednesday\or
Thursday \or Friday\or Saturday\fi}
\newcommand*{\MonthName}[1]{%
\ifcase#1\or
January\or February\or March\or April\or May\or June\or
July\or August\or September\or October\or November\or
December\fi}
% An extended \today command: \ExtToday. It prefix the
% normal \today command with the name of the weekday.
\newcommand*{\ExtToday}{%
\DatetoWeekday{JD@e}{\the\year}{\the\month}{\the\day}%
\DayName{\value{JD@e}}\space \today}
% A very basic,yet powerful calender commands. It can
% calculate the days of the month including months where a
% change from Julian to Gregorian calender has occurred.
% USAGE: \CalenderMonth{<year>}{<month>}
% \CalenderYear{<year>}
\newcounter{CAL@wd}
\newcounter{CAL@y}
\newcounter{CAL@m}
\newcounter{CAL@d}
\newcounter{CAL@JDi}
\newcounter{CAL@JDii}
\newcommand*{\CalenderMonth}[2]{%
\setcounter{CAL@y}{#1}%
\setcounter{CAL@m}{#2}%
\edef\@tempa{\MonthName{\value{CAL@m}}\space\theCAL@y}%
\JD@Calender{\value{CAL@y}}{\value{CAL@m}}{\@tempa}}
\newcommand*{\CalenderYear}[1]{%
\begin{minipage}{.85\textwidth}
\centerline{{\Large#1}}%
\small
\count@\@ne
\@whilenum \count@<12 \do{%
\JD@Calender{#1}{\count@}{\MonthName{\count@}}%
\hfill
\advance\count@\@ne
\JD@Calender{#1}{\count@}{\MonthName{\count@}}%
\par\smallskip
\advance\count@\@ne}%
\vspace{-\smallskipamount}%
\end{minipage}}
% Low level monthly calender
% USAGE: \JD@Calender{<year>}{<month>}{<heading>}
\newcommand*{\JD@Calender}[3]{%
\setcounter{CAL@y}{#1}%
\setcounter{CAL@m}{#2}%
\DatetoJD{CAL@JDi}{\value{CAL@y}}{\value{CAL@m}}{1}%
\addtocounter{CAL@y}{\value{CAL@m}/12}%
\modcounter{CAL@m}{12}%
\addtocounter{CAL@m}{1}%
\DatetoJD{CAL@JDii}{\value{CAL@y}}{\value{CAL@m}}{1}%
\begin{tabular}[t]{@{}*7{c}@{}}
\multicolumn{7}{c}{#3}\\
\sphline
S & M & T & W & T & F & S\\
\sphline
\JDtoWeekday{CAL@wd}{\value{CAL@JDi}}%
\count@\z@
\toks@{}%
\@whilenum \count@<\c@CAL@wd \do{%
\toks@\expandafter{\the\toks@ &}%
\advance\count@\@ne}%
\@whilenum{\c@CAL@JDi<\c@CAL@JDii}\do{%
\JDtoWeekday{CAL@wd}{\value{CAL@JDi}}%
\JDtoDate{\value{CAL@JDi}}{CAL@y}{CAL@m}{CAL@d}%
\edef\@tempa{\noexpand\toks@={\the\toks@ \theCAL@d}}%
\@tempa
\ifnum\c@CAL@wd<6
\toks@\expandafter{\the\toks@ &}%
\else
\toks@\expandafter{\the\toks@ \\}%
\fi
\advance\c@CAL@JDi\@ne}%
\ifnum\c@CAL@wd=6
\else
\toks@\expandafter{\the\toks@ \\}%
\fi
\the\toks@
\end{tabular}}
% Horizontal table rule with space below
\newcommand*{\sphline}{%
\hline%
\noalign{\vskip3pt}}
% Set the default Gregorian epoch: 15 October 1582.
% By changing this value, you can transform between
% calenders that have changed at different times
% historically. The date is the first date after
% the change.
\SetGregEpoch{1582}{10}{15}
\makeatother
%==================================================================
\begin{document}
\setlength{\parindent}{0pt}
\section*{Testing Calender Commands}
\newcounter{yr}\setcounter{yr}{1960}
\newcounter{mn}\setcounter{mn}{2}
\newcounter{dy}\setcounter{dy}{17}
\newcounter{JD}\DatetoJD{JD}{\value{yr}}{\value{mn}}{\value{dy}}
\newcounter{wday}
\begin{tabular}{@{}ll}
Input date: & \theyr-\themn-\thedy \\
Date $\rightarrow$ JD: & \theJD \\
\JDtoDate{\value{JD}}{yr}{mn}{dy}%
JD $\rightarrow$ date: & \theyr-\themn-\thedy \\
\JDtoWeekday{wday}{\value{JD}}%
JD $\rightarrow$ weekday: & \DayName{\value{wday}} \\
\DatetoWeekday{wday}{\value{yr}}{\value{mn}}{\value{dy}}%
Date $\rightarrow$ weekday: & \DayName{\value{wday}} \\
Today: & \ExtToday
\end{tabular}
\section*{Monthly Calender Samples}
This month:\par\medskip
\colorbox{CalCol}{\CalenderMonth{\the\year}{\the\month}}
\bigskip
Calender during Gregorian change:\par\medskip
\colorbox{CalCol}{\CalenderMonth{1582}{10}}
\section*{Year Calender Sample}
\colorbox{CalCol}{\CalenderYear{2003}}
\end{document}
That would be me, yes. I just designed a perpetual calendar for a TeX
application the other day.