21

I wrote some code for finding the date of Easter. Now I want to use it with the TiKZ calendar to mark the Easter related holidays.

Here's what I have so far:

\documentclass{scrartcl}
\usepackage[T1]{fontenc}
\usepackage{libertine}
\usepackage[margin=5pt,a5paper,landscape]{geometry}
\setlength{\parindent}{0pt}

\usepackage{tikz}
\usetikzlibrary{calendar}

\usepackage{expl3}

\ExplSyntaxOn

%% Knuth's AoCP, vol 1, 2nd ed, pp 155--156
\int_new:N \l_easter_Y_int
\int_new:N \l_easter_G_int
\int_new:N \l_easter_C_int
\int_new:N \l_easter_X_int
\int_new:N \l_easter_Z_int
\int_new:N \l_easter_D_int
\int_new:N \l_easter_E_int
\int_new:N \l_easter_N_int
\int_new:N \l_easter_M_int

\cs_new:Nn \easter_sunday:n {

  \int_set:Nn \l_easter_Y_int { #1 }

  \int_set:Nn \l_easter_G_int {
    \int_mod:nn { \l_easter_Y_int } { 19 } + 1
  }

  \int_set:Nn \l_easter_C_int {
    \int_div_truncate:nn { \l_easter_Y_int } { 100 } + 1
  }

  \int_set:Nn \l_easter_X_int {
    \int_div_truncate:nn { 3 * \l_easter_C_int } { 4 } - 12
  }

  \int_set:Nn \l_easter_Z_int {
    \int_div_truncate:nn { 8 * \l_easter_C_int + 5 } { 25 } - 5
  }

  \int_set:Nn \l_easter_D_int {
    \int_div_truncate:nn { 5 * \l_easter_Y_int } { 4 } - \l_easter_X_int - 10
  }

  \int_set:Nn \l_easter_E_int {
    \int_mod:nn { 11 * \l_easter_G_int + 20 + \l_easter_Z_int
      - \l_easter_X_int } { 30 }
  }

  % \int_mod:nn behaves strangely.
  \int_compare:nNnT { \l_easter_E_int } < { 0 }
  {
    \int_add:Nn \l_easter_E_int { 30 }
  }

  \int_compare:nNnTF { \l_easter_E_int } = { 25 }
  { % true
    \int_compare:nNnT { \l_easter_G_int } > { 11 }
    { % true
      \int_incr:N \l_easter_E_int
    }
  }
  { % false
    \int_compare:nNnT { \l_easter_E_int } = { 24 }
    { % true
      \int_incr:N \l_easter_E_int
    }
  }

  \int_set:Nn \l_easter_N_int { 44 - \l_easter_E_int }

  \int_compare:nNnT { \l_easter_N_int } < { 21 }
  { % true
    \int_add:Nn \l_easter_N_int { 30 }
  }

  \int_add:Nn \l_easter_N_int {
    7 - \int_mod:nn { \l_easter_D_int + \l_easter_N_int } { 7 }
  }

  \int_compare:nNnTF { \l_easter_N_int } > { 31 }
  { % true
    \int_sub:Nn \l_easter_N_int { 31 }
    \int_set:Nn \l_easter_M_int { 4 } % April
  }
  { % false
    \int_set:Nn \l_easter_M_int { 3 } % March
  }

}

\ExplSyntaxOff

\begin{document}

\begin{tikzpicture}
  \calendar[dates=2012-04-01 to 2012-05-last, week list];
\end{tikzpicture}

\end{document}
20

The trick was to use the command \pgfkeys.

Here is the full code:

\documentclass{scrartcl}
\usepackage[T1]{fontenc}
\usepackage{libertine}
\usepackage[margin=5pt,a5paper,landscape]{geometry}
\setlength{\parindent}{0pt}

\usepackage{tikz}
\usetikzlibrary{calendar}

\usepackage{expl3}

\ExplSyntaxOn

%% Knuth's AoCP, vol 1, 2nd ed, pp 155--156
\int_new:N \l_easter_Y_int
\int_new:N \l_easter_G_int
\int_new:N \l_easter_C_int
\int_new:N \l_easter_X_int
\int_new:N \l_easter_Z_int
\int_new:N \l_easter_D_int
\int_new:N \l_easter_E_int
\int_new:N \l_easter_N_int
\int_new:N \l_easter_M_int
\int_new:N \l_easter_julian_day_int

\cs_new:Nn \easter_sunday:n {

  \int_set:Nn \l_easter_Y_int { #1 }

  \int_set:Nn \l_easter_G_int {
    \int_mod:nn { \l_easter_Y_int } { 19 } + 1
  }

  \int_set:Nn \l_easter_C_int {
    \int_div_truncate:nn { \l_easter_Y_int } { 100 } + 1
  }

  \int_set:Nn \l_easter_X_int {
    \int_div_truncate:nn { 3 * \l_easter_C_int } { 4 } - 12
  }

  \int_set:Nn \l_easter_Z_int {
    \int_div_truncate:nn { 8 * \l_easter_C_int + 5 } { 25 } - 5
  }

  \int_set:Nn \l_easter_D_int {
    \int_div_truncate:nn { 5 * \l_easter_Y_int } { 4 } - \l_easter_X_int - 10
  }

  \int_set:Nn \l_easter_E_int {
    \int_mod:nn { 11 * \l_easter_G_int + 20 + \l_easter_Z_int
      - \l_easter_X_int } { 30 }
  }

  % \int_mod:nn behaves strangely.
  \int_compare:nNnT { \l_easter_E_int } < { 0 }
  {
    \int_add:Nn \l_easter_E_int { 30 }
  }

  \int_compare:nNnTF { \l_easter_E_int } = { 25 }
  { % true
    \int_compare:nNnT { \l_easter_G_int } > { 11 }
    { % true
      \int_incr:N \l_easter_E_int
    }
  }
  { % false
    \int_compare:nNnT { \l_easter_E_int } = { 24 }
    { % true
      \int_incr:N \l_easter_E_int
    }
  }

  \int_set:Nn \l_easter_N_int { 44 - \l_easter_E_int }

  \int_compare:nNnT { \l_easter_N_int } < { 21 }
  { % true
    \int_add:Nn \l_easter_N_int { 30 }
  }

  \int_add:Nn \l_easter_N_int {
    7 - \int_mod:nn { \l_easter_D_int + \l_easter_N_int } { 7 }
  }

  \int_compare:nNnTF { \l_easter_N_int } > { 31 }
  { % true
    \int_sub:Nn \l_easter_N_int { 31 }
    \int_set:Nn \l_easter_M_int { 4 } % April
  }
  { % false
    \int_set:Nn \l_easter_M_int { 3 } % March
  }

  \pgfcalendardatetojulian { \l_easter_Y_int -
    \l_easter_M_int - \l_easter_N_int
  } { \l_easter_julian_day_int }

}

\pgfkeys{/pgf/calendar/Easter/.default = 0}
\pgfkeys{/pgf/calendar/Easter/.code =
  {
    \easter_sunday:n { \pgfcalendarifdateyear }
    \int_compare:nNnT { \pgfcalendarifdatejulian }
    =  {\l_easter_julian_day_int + #1}
    { \pgfcalendarmatchestrue }
  }
}

\ExplSyntaxOff

\begin{document}

\begin{tikzpicture}
  \calendar[dates=2012-04-01 to 2012-05-last, week list]
    if (Easter=-3, % Maundy Thursday
        Easter=-2, % Good Friday
        Easter,    % Easter Sunday
        Easter=1,  % Easter Monday
        Easter=39, % Feast of the Ascension
        Easter=49, % Pentecost
        Easter=50) % Whit Monday
    [red];
\end{tikzpicture}

\end{document}

Here is the result:

calendar

Here is an expanded version: http://pastebin.com/KYngbPGQ

There is also a version here: http://www.texample.net/tikz/examples/birthday-calendar/

Please note that this solution requires an up-to-date TeX distro. It won’t work with the distro that comes with Ubuntu 12.04 and older.

  • 1
    Shouldn't this be made into a package? (Or incorporated into tikz itself?) – mbork Jun 11 '12 at 12:53
  • @mbork I've already added a feature request. I need to convert the code to something generic (no latex3). – cjorssen Jun 22 '12 at 7:43
  • @cjorssen Would my answer help? :) – Qrrbrbirlbel Nov 10 '13 at 5:11
  • @Qrrbrbirlbel Sure :) I'll add it ASAP. – cjorssen Nov 10 '13 at 15:21
  • @cjorssen was the feature request successful? and if yes, could you add an answer to explain how one can use it to solve this problem? – fifaltra Nov 13 '16 at 10:18
5

The same code but with TeX counts.

The formula from WikiBooks: Algorithmensammlung: Kalendar: Feiertage was used.

Due to the nature of the test, if one uses an argument to Easter (e.g. Eastern=365) so that the actual day to test against lies not in the current year, this will fail. (You cannot test a day from year 2013 that lies somehow relatively to Eastern in 2012.)

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calendar}
\makeatletter
\def\pgfcalendar@Easter#1{%
 \begingroup
   \count0=#1\relax              % Yeah 0
   \count1=\count0%              % K 1
   \divide\count1 100            % K 1
   \count2=\count1               % M 3
   \multiply\count2 3
   \advance\count2 3
   \divide\count2 4              % 2 holds (3 * K +3) \ 4
   \count3=\count1
   \multiply\count3 8
   \advance\count3 13
   \divide\count3 -25
   \advance\count3\count2
   \advance\count3 15           % M 3
   \count2-\count2              % S 2
   \advance\count2 2
   \count4=\count0              % A 4
   \divide\count4 19
   \multiply\count4 -19
   \advance\count4\count0       % A 4
   \count1=\count4              % D 1 *
   \multiply\count1 19
   \advance\count1\count3
   \count5\count1
   \divide\count1 30
   \multiply\count1 -30
   \advance\count1\count5       % D 1 *
   \divide\count4 11            % R 4 *
   \advance\count4\count1
   \divide\count4 29            % R 4 *
   \advance\count1-\count4      % OG 1 **
   \advance\count1 21           % OG 1 **
   \count3\count0               % SZ 3 *
   \divide\count3 4
   \advance\count3\count0
   \advance\count3\count2
   \count4-\count3
   \divide\count3 7
   \multiply\count3 7
   \advance\count3\count4
   \advance\count3 7           % SZ 3 *
   \count4-\count3             % OE 4 **
   \advance\count4\count1
   \count3-\count4
   \divide\count4 7
   \multiply\count4 7
   \advance\count4\count3
   \advance\count4 7           % OE 4 **
   \advance\count4\count1
   \pgfcalendardatetojulian{\the\count0-03-\the\count4}\pgfutil@tempcnta
   \expandafter\xdef\csname pgfcalendar@Easter@\the\count0\endcsname
     {\the\pgfutil@tempcnta}%
 \endgroup}
\pgfqkeys{/pgf/calendar/Easter}{
  .default=0,
  .code={%
      \expandafter\ifx\csname pgfcalendar@Easter@\pgfcalendarifdateyear\endcsname\relax
        \pgfcalendar@Easter{\pgfcalendarifdateyear}%
      \fi
      \expandafter\pgfutil@tempcnta
        \csname pgfcalendar@Easter@\pgfcalendarifdateyear\endcsname
      \advance\pgfutil@tempcnta#1\relax
      \ifnum\pgfcalendarifdatejulian=\pgfutil@tempcnta
        \pgfcalendarmatchestrue
      \fi}}
\makeatother
\begin{document}
\begin{tikzpicture}
  \calendar[dates=2012-04-01 to 2012-05-last, week list]
    if (Easter=-3, % Maundy Thursday
        Easter=-2, % Good Friday
        Easter,    % Easter Sunday
        Easter=1,  % Easter Monday
        Easter=39, % Feast of the Ascension
        Easter=49, % Pentecost
        Easter=50) % Whit Monday
    [red];
\end{tikzpicture}
\end{document}

Output

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.