Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Julian date algorithms

22 views
Skip to first unread message

Danie Els

unread,
Aug 11, 2003, 4:27:41 AM8/11/03
to
I have a longstanding interest in date calculation (worked on
satellite orbit calculation for a number of years) and has stumbled
across a very good site on this subject:

http://www.tondering.dk/claus/calendar.html

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}

bl...@po.cwru.edu

unread,
Aug 14, 2003, 9:38:06 PM8/14/03
to
Danie Els <dnj...@yahoo.co.uk> wrote:
> (b) Make a package for historical calenders. Who on earth wants
> something like that (maybe a few nuts like I)?

That would be me, yes. I just designed a perpetual calendar for a TeX
application the other day.

0 new messages