[comp.text.tex] Yet another Calendar

juergen@julien.informatik.uni-dortmund.de (Juergen Stuber) (04/10/91)

Recently someone posted a tex file to generate a calendar.
It didn't suit me, so I hacked up my own. It is in german,
but customization to other languages should be easy.

---------- cut here ----------
% calendar.sty - print calendar
% Copyright (C) 1991 J"urgen Stuber
% 
%     This program is free software; you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation; either version 1, or (at your option)
%     any later version.
% 
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
% 
%     You should have a copy of the GNU General Public License;
%     if not, write to the Free Software Foundation, Inc.,
%     675 Mass Ave, Cambridge, MA 02139, USA.
%
% History:
%   10.4.1991 js - posted
%
% Description:
% This will generate a calendar in the following format:
% +----------+----------+  ...  +----------+
% |  Januar  | Februar  |       | Dezember |
% +----------+----------+  ...  +----------+
% + 1 DI     | 1 FR     |       | 1 SO     |
% +----------+----------+  ...  +----------+
% + 2 MI     | 2 SA     |       | 2 MO     |
% +----------+----------+  ...  +----------+
% .
% .
% .
%
% +----------+----------+  ...  +----------+
% |31 DO     |          |       |31 DI     |
% +----------+----------+  ...  +----------+
%
% Because of space limitations the calendar is printed on 3 pages.
%
% Features:
% - number of week in year
% - handles holidays and birthdays in a flexible way
% - computes day of week & easter date
%
% Limitations:
% - does not print year due to lack of space
% - number of annotations per date is limited to two due to lack of space
% - does not compute moon phases
% - does not compute beginning of spring, summer, etc.
%
% Usage:
% generate a LaTex file calendar.tex like the following:
%
%--- beginning of calendar.tex ------------------------------
% \documentstyle[german]{calendar}
% 
% \def\weekdayname#1{%
%   \ifcase#1 \relax
%     MO\or DI\or MI\or DO\or FR\or SA\or SO%
%   \fi
% }
% 
% \def\monthname#1{%
%   \ifcase#1 \relax
%     Januar\or Februar\or M"arz\or April\or Mai\or Juni\or
%     Juli\or August\or September\or Oktober\or November \or Dezember%
%   \fi
% }
% 
% \begin{document}%
% \setYear{1991}%
% \input{holiday}%
% \input{birthday}%
% \typesetYear%
% \end{document}
%--- end of calendar.tex ------------------------------
%
% holiday.tex and birthday.tex contain declarations in the following
% formats:
%
% \annual{text}{day.month.}{holidayflag}
% \annualMovable{text}{day.month.}{dayOfWeek}{holidayflag}
% \easterRelative{text}{offset}{holidayflag}
% \anniversary{text}{day.month.year}{holidayflag}
%
% \annual will generate an annotation for the specified day containing
%     the given text. If holidayflag is 1 the day will be marked as
%     a holiday, i.e., printed in boldface.
% \annualMovable will generate the annotation for the next day with
%     matching day of the week on or after the specified date.
%     The day of the week is encoded as 0 for monday up to 6 for sunday.
% \easterRelative will generate the annotation for the day with
%     the specified offset from easter.
% \anniversary will generate an annotation consisting of text and the
%     number of the anniversary.
%
% Here is a sample file containing declarations of holidays for
% Northrhine-Westfalia. Some other german holidays are included but
% commented out.
%
%--- beginning of holiday.tex ------------------------------
% \annual{Neujahr}{1.1.}{1}%
% \annual{Heilige Drei K"onige}{6.1.}{0}%
% %\annual{Valentinstag}{14.2.}{0}%
% \easterRelative{Rosenmontag}{-48}{0}%
% %\easterRelative{Fastnacht}{-47}{0}%
% \easterRelative{Aschermittwoch}{-46}{0}%
% \easterRelative{Karfreitag}{-2}{1}%
% \easterRelative{Ostern}{0}{1}%
% \easterRelative{Ostern}{1}{1}%
% \annual{Tag der Arbeit}{1.5.}{1}%
% \easterRelative{Himmelfahrt}{39}{1}%
% \easterRelative{Pfingsten}{49}{1}%
% \easterRelative{Pfingsten}{50}{1}%
% \easterRelative{Fronleichnam}{60}{1}%
% %\annual{Mari"a Himmelfahrt}{15.8.}{0}%
% %\annual{Reformationstag}{31.10.}{0}%
% \annual{Allerheiligen}{1.11.}{1}%
% %\annualMovable{Volkstrauertag}{13.11.}{6}{0}%
% \annualMovable{Bu"s- und Bettag}{16.11.}{2}{1}%
% %\annualMovable{Totensonntag}{20.11.}{6}{0}%
% %\annualMovable{1. Advent}{27.11.}{6}{0}%
% %\annualMovable{2. Advent}{4.12.}{6}{0}%
% %\annualMovable{3. Advent}{11.12.}{6}{0}%
% %\annualMovable{4. Advent}{18.12.}{6}{0}%
% %\annual{Mari"a Empf"angnis}{8.12.}{0}%
% \annual{Heiligabend}{24.12.}{0}%
% \annual{Weihnachten}{25.12.}{1}%
% \annual{Weihnachten}{26.12.}{1}%
% \annual{Silvester}{31.12.}{0}%
%--- end of holiday.tex ------------------------------
%
% You may change the following parameters:
% \columnheight  height of the entire column, including title
% \titleheight   height of title containing month name
% \itemwidth     width of one column
% \daywidth      position of right edge of day number in column
% \weekdaywidth  width reserved for day of week
%
%----------------------------------------------------------------------
\input{article.sty}
\pagestyle{empty}%
\evensidemargin=0pt
\oddsidemargin=\evensidemargin
\topmargin=0pt
\topmargin 0pt
\headheight 0pt
\headsep 0pt
\footskip 0pt
% 
\textheight 280mm
%----------------------------------------------------------------------
% list macros (see TeXBook, Dirty Tricks)

\let\sep\relax

\toksdef\tb=2

\def\leftlist#1{%
  \def\sep##1{\relax##1\cr}%
  \vbox{\halign{##\hfil\cr#1}}%
}

\def\@ifundef#1{\expandafter\ifx\csname#1\endcsname\relax}
\def\@nameedef#1{\expandafter\edef\csname #1\endcsname}

\newcount\tmpcnt

\def\remainder#1by#2{%
  \tmpcnt=#1 %
  \divide\tmpcnt by #2 %
  \multiply\tmpcnt by #2 %
  \advance #1 by -\tmpcnt}

%----------------------------------------------------------------------

\newcount\curday % day
\newcount\curmonth % month
\newcount\curweekday % day of week, 0=Monday
\newcount\curyear % year
\newcount\daycount % day of year
\newcount\weekcount % week of year
\newcount\leapyear % 0=normal year, 1=leap year
\newcount\easter % easter date, as day of year
\newcount\startweekday % first day of week in year

\def\setYear#1{
  \global\curyear=#1 %
  {% compute leap year
  \newcount\rf \rf=\curyear \remainder\rf by{4}%
  \newcount\rh \rh=\curyear \remainder\rh by{100}%
  \newcount\rfh \rfh=\curyear \remainder\rfh by{400}%
  \ifnum\rfh=0 %
    \global\leapyear=1 %
  \else
    \ifnum\rh=0 %
      \global\leapyear=0 %
    \else
      \ifnum\rf=0 %
        \global\leapyear=1 %
      \else
        \global\leapyear=0 %
      \fi
    \fi
  \fi
  }%
  {% compute easter date
  % this is the easter formula by C.F.Gauss -- valid up to 8201
  % (no limitation, considering the current rate of environmental destruction)
  \newcount\tmpa \tmpa=\curyear \remainder\tmpa by{19}%
  \newcount\tmpb \tmpb=\curyear \remainder\tmpb by{4}%
  \newcount\tmpc \tmpc=\curyear \remainder\tmpc by{7}%
  \newcount\yh \yh=\curyear \divide\yh by 100 %
  \newcount\yfh \yfh=\curyear \divide\yfh by 400 %
  \newcount\m \m=\yh 
    \multiply\m by 8 %
    \advance\m by 13 %
    \divide\m by 25 %
    \advance\m by -2 %
  \newcount\s \s=\yh \advance\s by -\yfh \advance\s by -2 %
  \newcount\M \M=15 %
    \advance\M by \s
    \advance\M by -\m
    \remainder\M by{30}%
  \newcount\N \N=6 %
    \advance\N by \s
    \remainder\N by{7}%
  \newcount\d \d=\tmpa
    \multiply\d by 19 %
    \advance\d by \M
    \remainder\d by{30}%
  \newcount\D
    \ifnum\d=29 %
      \D=28 %
    \else
      \ifnum\d=28 %
        \ifnum\d>10 %
          \D=27 %
        \else
          \D=\d
        \fi
      \else
        \D=\d
      \fi
    \fi
  \multiply\tmpb by 2 %
  \multiply\tmpc by 4 %
  \newcount\e \e=\D
    \multiply\e by 6 %
    \advance\e by \tmpb
    \advance\e by \tmpc
    \advance\e by \N
    \remainder\e by{7}%
  \advance\e by 80 %
  \advance\e by \leapyear
  \advance\e by \D
  \global\easter=\e
  }% pooh -- done it
  {% compute starting day of week --- we know easter is on a sunday
  \newcount\fsd \fsd=\easter \remainder\fsd by{7}%
  \newcount\wd \wd=6 \advance\wd by -\fsd
  \global\startweekday=\wd
  }%
}

%----------------------------------------------------------------------

\def\monthlength#1{%
  \ifnum\leapyear=0
    \ifcase#1 %
      31\or28\or31\or30\or31\or30\or
      31\or31\or30\or31\or30\or31%
    \fi
  \else
    \ifcase#1 %
      31\or29\or31\or30\or31\or30\or
      31\or31\or30\or31\or30\or31%
    \fi
  \fi
}

\def\monthoffset#1{%
  \ifnum\leapyear=0 %
    \ifcase#1 %
      0\or31\or59\or90\or120\or151\or
      181\or212\or243\or273\or304\or334%
    \fi
  \else
    \ifcase#1 %
      0\or31\or60\or91\or121\or152\or
      182\or213\or244\or274\or305\or335%
    \fi
  \fi
}

%----------------------------------------------------------------------
% typesetting individual days

% #1 : day in month
% #2 : Holiday flag (0=workday, 1=holiday)
\def\typesetDay#1#2{\relax%
  \ifnum#2=0 %
    {#1}%
  \else
    {\bf #1}%
  \fi
}

% #1 : weekday code  0..7, 0=Monday
% #2 : Holiday flag (0=workday, 1=holiday)
\def\typesetWeekday#1#2{%
  \ifnum#2=0 %
    {\weekdayname{#1}}%
  \else
    {\bf\weekdayname{#1}}%
  \fi
}

% #1 : annotations, format a1\sepa 2\sep..\sep an
\def\typesetAnnotations#1{{\leftlist{#1}}}

\newdimen\columnheight \columnheight=247mm
\newdimen\titleheight \titleheight=10mm
\newdimen\itemwidth \itemwidth=43mm
\newdimen\daywidth \daywidth=6mm
\newdimen\weekdaywidth \weekdaywidth=9mm

\newdimen\itemheight \itemheight=\columnheight
  \advance\itemheight by -\titleheight
  \divide\itemheight by 31
  \advance\itemheight by -2.4pt

\def\typesetItem{%
  \@ifundef{ann\the\daycount}%
    \def\ann{}%
  \else
    \def\ann{\@nameuse{ann\the\daycount}}%
  \fi
  \@ifundef{hol\the\daycount}%
    \ifnum\curweekday=6 %
      \def\hol{1}%
    \else
      \def\hol{0}%
    \fi
  \else
    \def\hol{\@nameuse{hol\the\daycount}}%
  \fi
  \vbox{%
    \kern1pt%
    \hbox to \itemwidth{%
      \vbox to \itemheight{%
        \vfil
        \hbox to\daywidth{\hfil{\large\typesetDay{\the\curday}{\hol}}}%
        \vfil}%
      \kern2mm%
      \vbox to \itemheight{%
        \vfil
        \hbox to\weekdaywidth{%
          {\large\typesetWeekday{\the\curweekday}{\hol}}%
          \hfil}%
        \vfil}%
      \vbox to \itemheight{%
        {\scriptsize\typesetAnnotations{\ann}}%
        \vfil
      }%
      \hfil
      \ifnum\curweekday=0 %
        \vbox to \itemheight{%
          \kern1pt%
          \hbox{{\scriptsize\the\weekcount}\kern1pt}%
          \vfil
        }%
        \global\advance \weekcount by 1 %
      \else\relax\fi
    }%
    \kern1pt%
    \hrule}}

\def\typesetDummyItem{%
  \vbox{%
    \kern1pt%
    \hbox to \itemwidth{%
      \vbox to \itemheight{%
        \vfil
      }%
      \hfil
    }%
    \kern1pt
    \hrule}}

\newcount\lastday

% #1 : number of month, 1=January
\def\typesetMonth#1{%
  \vbox{%
    \vbox to \titleheight{%
      \hrule
      \kern2pt%
      \vfil
      \hbox to \itemwidth{\hfil{\LARGE\bf\strut\monthname{#1}}\hfil}%
      \vfil
      \hrule height0.8pt%
    }
    \curday=0 %
    \edef\lastday{\monthlength{#1} }%
    \loop\ifnum\curday<\lastday
      \global\advance \curday by 1 %
      \typesetItem
      \global\advance \daycount by 1 %
      \global\advance \curweekday by 1 %
      \ifnum\curweekday>6 %
        \global\advance \curweekday by -7 %
      \else\relax\fi
    \repeat
    \loop\ifnum\curday<31 %
      \global\advance \curday by 1 %
      \typesetDummyItem
    \repeat
    \vfil
  }%
}

% #2 : first month
% #3 : last month+1
\def\typesetPartialYear#1#2{%
  \hrule
  \hbox{%
    \curmonth=#1 %
    \loop\ifnum\curmonth<#2 %
      \vrule %
      \typesetMonth{\the\curmonth}%
      \global\advance\curmonth by 1 %
    \repeat
    \vrule
  }%
  \hrule
}

\def\typesetYear{%
  \curweekday=\startweekday
  \weekcount=1 %
  \daycount=0 %
  \newcount\firstmonth
  \newcount\endmonth
  \firstmonth=0 %
  \loop\ifnum\firstmonth<12 %
    \endmonth=\firstmonth \advance\endmonth by 4 %
    \typesetPartialYear{\the\firstmonth}{\the\endmonth}%
    \eject
    \advance\firstmonth by 4 %
  \repeat
}

%----------------------------------------------------------------------

\def\dayOf#1.#2.#3 {#1}
\def\monthOf#1.#2.#3 {#2}
\def\yearOf#1.#2.#3 {#3}

\def\addAnn#1#2#3{%
  \@ifundef{ann#1}%
    \@namedef{ann#1}{}%
  \else
    \relax
  \fi
  \tb=\expandafter{#2}%
  \@nameedef{ann#1}{\@nameuse{ann#1}\noexpand\sep{\the\tb#3}}%
}

\def\addHol#1#2{%
  \ifnum#2=1 %
    \@namedef{hol#1}{1}%
  \else\relax\fi
}

%----------------------------------------------------------------------
% entries for holidays

% #1 : text
% #2 : date in format d.m.
% #3 : holiday flag
\def\annual#1#2#3{%
  \edef\dof{\dayOf#2 }%
  \edef\mof{\monthOf#2 }%
  \tmpcnt=\mof
  \advance\tmpcnt by -1 %
  \edef\moff{\monthoffset{\the\tmpcnt}}%
  \tmpcnt=\moff
  \advance\tmpcnt by \dof
  \advance\tmpcnt by -1 %
  \edef\doy{\the\tmpcnt}%
  \addAnn{\doy}{#1}{}%
  \addHol{\doy}{#3}%
}

% #1 : text
% #2 : date in format d.m.
% #3 : day of week
% #4 : holiday flag
\def\annualMovable#1#2#3#4{%
  \edef\dof{\dayOf#2 }%
  \edef\mof{\monthOf#2 }%
  \curday=\mof
  \advance\curday by -1 %
  \edef\moff{\monthoffset{\the\curday}}%
  \curday=\moff %
  \advance\curday by \dof
  \advance\curday by -1 %
  \curweekday=700 %
  \advance\curweekday by -\curday
  \advance\curweekday by -\startweekday
  \advance\curweekday by#3 %
  \remainder\curweekday by{7}%
  \advance\curday by \curweekday
  \edef\doy{\the\curday}%
  \addAnn{\doy}{#1}{}%
  \addHol{\doy}{#4}%
}

% #1 : text
% #2 : offset to easter (may be negative)
% #3 : holiday flag
\def\easterRelative#1#2#3{%
  \tmpcnt=\easter %
  \advance\tmpcnt by #2 %
  \edef\doy{\the\tmpcnt}%
  \addAnn{\doy}{#1}{}%
  \addHol{\doy}{#3}%
}

% #1 : text
% #2 : date in format d.m.y
% #3 : holiday flag
\def\anniversary#1#2#3{%
  \edef\dof{\dayOf#2 }%
  \edef\mof{\monthOf#2 }%
  \edef\yof{\yearOf#2 }%
  \tmpcnt=\mof
  \advance\tmpcnt by -1 %
  \edef\moff{\monthoffset{\the\tmpcnt}}%
  \tmpcnt=\moff
  \advance\tmpcnt by \dof
  \advance\tmpcnt by -1 %
  \edef\doy{\the\tmpcnt}%
  \tmpcnt=\curyear %
  \advance\tmpcnt by -\yof
  \addAnn{\doy}{#1 }{\the\tmpcnt}%
  \addHol{\doy}{#3}%
}
---------- cut here ----------
Enjoy

-------------------------------------------------------
Juergen Stuber (juergen@ls5.informatik.uni-dortmund.de)