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}

2 Answers 2

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.

5
  • 1
    Shouldn't this be made into a package? (Or incorporated into tikz itself?)
    – mbork
    Jun 11, 2012 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, 2012 at 7:43
  • @cjorssen Would my answer help? :) Nov 10, 2013 at 5:11
  • @Qrrbrbirlbel Sure :) I'll add it ASAP.
    – cjorssen
    Nov 10, 2013 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, 2016 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.