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.

6
  • 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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .