[Tex/LaTex] TikZ calendar and conditional tests

calendarconditionalstikz-pgf

I would like to specially mark national holidays in my calendar. Some of the rules are "First Monday in June" and "Last Monday in October".

I have been looking at the example on p. 280 of the PGF 2.10 manual but I'm not able ot modify it for my purposes. The useful parts of this example are:

if (between=\year-\month-\day+8 and \year-\month-\day+10)
  [red]
if (Sunday)
  [gray,nodes={draw=none}]

Here's an MWE, I believe.

% Based on Hakon Malmedal's 'Birthday calendar' from TeXamples
\documentclass[fontsize=20pt]{scrartcl}
\usepackage{tikz}
\usetikzlibrary{calendar,fit}

\begin{document}

\begin{tikzpicture}[thick]
  \calendar[dates=2014-01-01 to 2014-01-last,
            week list,
            month label above centered,
            month text=\textsc{\%mt \%y0}]
  if (equals=01-01,
      equals=03-17,
      equals=12-25,
      equals=12-26) [orange]
      % p. 280 of PGF 2.10 manual is useful
  if (between=2014-01-08 and 2014-01-14) [orange]
% following doesn't work:
%  if (Monday \AND between=2014-01-08 and 2014-01-14) [orange]
;
\end{tikzpicture}

\end{document}

The 7 days from 8th to 14th of Jan. get displayed in orange but I would like simply the Monday (2nd Monday of month) to be in orange, as intended by the commented line.

How can I build a conjunction of two conditions so that the holiday is printed in orange?

Best Answer

A simple and function

If you simply want an and function for PGF’s calendar you can use the following code. It checks the first argument and only if it is true it does check the other one. (If the first one's already false, why bother checking the second?)

This can be used as

if (and={between=2014-01-08 and 2014-01-14}{Monday}) [orange];

Maybe it is better to write

if (between=2014-01-08 and 2014-01-14, And=Monday, And=…) [orange];

This works, too. Obviously, you'll need {} if you use it like this:

if (Monday, And={between=2014-01-08 and 2014-01-14}) [orange];

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calendar}
\makeatletter
\pgfqkeys{/pgf/calendar}{
  and/.code 2 args=%
    \begingroup
      \let\pgf@cal@temp\pgfutil@empty
      \pgfcalendar@launch@ifdate{#1}{\pgfcalendar@launch@ifdate{#2}{\def\pgf@cal@temp{\let\ifpgfcalendarmatches\iftrue}}{}}{}%
    \expandafter\endgroup\pgf@cal@temp,
  And/.code=%
    \begingroup
      \let\pgf@cal@temp\pgfutil@empty
      \ifpgfcalendarmatches\expandafter\pgfutil@firstofone\else\expandafter\pgfutil@gobble\fi
      {\pgfcalendar@launch@ifdate{#1}{\def\pgf@cal@temp{\let\ifpgfcalendarmatches\iftrue}}{}}%
    \expandafter\endgroup\pgf@cal@temp}
\makeatother
\begin{document}
\begin{tikzpicture}
  \calendar[dates=2014-01-01 to 2014-01-last, week list,
            month label above centered, month text=\textsc{\%mt \%y0}]
  if (equals=01-01, equals=03-17, equals=12-25, equals=12-26) [orange]
  if (between=2014-01-08 and 2014-01-14, And=Monday)          [orange]
  if (and={between=2014-01-08 and 2014-01-14}{Monday})        [days={fill=gray}];
\end{tikzpicture}
\end{document}

Output

enter image description here

Second Sunday in May?

This library qrr.calendar introduces the conditionals

  • leap year

    This can also be used for other years:

      2000 was\ifdate{leap year=200}{}{ not} a leap year
    
  • week of month=<arg> tests whether we are in the <arg>th week of the month. This does not correspond with the Monday-to-Sunday (or the Sunday-to-Saturday) week. The first week goes from day 1 to day 7, the second week from day 8 to 14, and so on.

  • week of month'=<arg> is the same as week of month but from the last day on.

    week of month'=1 would (currently: October) test whether we are between day 25 and day 31 of the month. This uses the \pgfcalendar@getlastYMX macro to get the last day of a month which uses the leap year key when the month falls on February.

  • first=[<i>:]<conditonal> and last=[<i>:]<conditional> combines week of month and week of month' with <conditional>. You can check for the second Monday in a month with

     first=2:Monday
    

    If no <i>: is given, the first/last (i.e. 1:) is used.

    The <conditional> can actually be anything but naturally you want to specify a day of the week here.

  • between days=<first> and <last> checks wheter the day is between first and <last>.

  • not=<cond> negates the outcome of <cond>


Load it with

\usetikzlibrary{qrr.calendar}
% or
\usepgflibrary{qrr.calendar}

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calendar,qrr.calendar}\usepackage{libertine}
\begin{document}\sffamily
\begin{tikzpicture}
  \calendar[dates=2023-01-01 to 2023-12-31, month list, month label left,
    month yshift={!mod(\numexpr\pgfcalendarcurrentmonth-1\relax,3)*.25em+1.25em}]
    if (Sunday) [black!50]
    if (first=2:Sunday, And=May)
      [days={fill=red, text=black, rounded corners, text depth=0pt},
        day text=$\heartsuit$];
\end{tikzpicture}
\end{document}

Output

enter image description here