Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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?

share|improve this question
1  
Please add a minimal working example (MWE) that illustrates your problem. It will be much easier for us to reproduce your situation and find out what the issue is when we see compilable code, starting with \documentclass{...} and ending with \end{document}. –  Jubobs Oct 27 '13 at 13:40
    
MWE added. Thanks for any suggestions. –  healyp Oct 27 '13 at 18:17
    
Do you also need easter rules? Perhaps you can use parts of the answer in tex.stackexchange.com/questions/57218/… –  knut Oct 27 '13 at 18:53
    
@knut: as the comment at the top of my MWE suggests it is based on the link you mention. The rules there are very useful but they don't solve the problem of finding the kth Monday in the month. Nor does the PGF guide. –  healyp Oct 27 '13 at 20:22

1 Answer 1

up vote 2 down vote accepted

A simple and function

If you simply want an and function for PGF’s calendar you can use the following code. It checks both arguments if they are true.

Problematically is that \ifdate resets the status of \ifpgfcalendarmatches which would result in errors because those conditional are setup in a way that they built a logical Or. The conditional keys only set \pgfcalendarmatchestrue and never …false.

For this case I created another conditional that acts globally and is only used to switch the main conditional to true. (We could have also used other approaches here.)

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calendar}
\makeatletter
\def\pgfcalendar@matchesfalse{\global\let\ifpgfcalendar@matches\iffalse}
\def\pgfcalendar@matchestrue{\global\let\ifpgfcalendar@matches\iftrue}
\pgfcalendar@matchesfalse
\pgfqkeys{/pgf/calendar}{and/.code 2 args={%
    \begingroup
      \ifdate{#1}{\ifdate{#2}{\pgfcalendar@matchestrue}{}}{}%
    \endgroup
    \ifpgfcalendar@matches\pgfcalendarmatchestrue\pgfcalendar@matchesfalse\fi}}
\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 (and={between=2014-01-08 and 2014-01-14}{Monday}) [orange];
\end{tikzpicture}
\end{document}

Output

enter image description here

First Monday in June?

This code 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 corresponds with the Monday-to-Friday 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 monts. 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.


Granted, we could have used the internals to calculate the last day of a month, i.e. subtracting <year>-<month>-01 from <year>-<month>-last+1, but that would involve a lot of calculation that aren’t really needed.

Code

\documentclass[tikz,png,convert=false]{standalone}
\usetikzlibrary{calendar}
\makeatletter
\def\pgfcalendar@matchesfalse{\global\let\ifpgfcalendar@matches\iffalse}
\def\pgfcalendar@matchestrue{\global\let\ifpgfcalendar@matches\iftrue}
\pgfcalendar@matchesfalse
\pgfqkeys{/pgf/calendar}{
  Jan/.code={\ifnum\pgfcalendarifdatemonth=1\relax\pgfcalendarmatchestrue\fi},Feb/.code={\ifnum\pgfcalendarifdatemonth=2\relax\pgfcalendarmatchestrue\fi},Mar/.code={\ifnum\pgfcalendarifdatemonth=3\relax\pgfcalendarmatchestrue\fi},
  Apr/.code={\ifnum\pgfcalendarifdatemonth=4\relax\pgfcalendarmatchestrue\fi},May/.code={\ifnum\pgfcalendarifdatemonth=5\relax\pgfcalendarmatchestrue\fi},Jun/.code={\ifnum\pgfcalendarifdatemonth=6\relax\pgfcalendarmatchestrue\fi},
  Jul/.code={\ifnum\pgfcalendarifdatemonth=7\relax\pgfcalendarmatchestrue\fi},Aug/.code={\ifnum\pgfcalendarifdatemonth=8\relax\pgfcalendarmatchestrue\fi},Sep/.code={\ifnum\pgfcalendarifdatemonth=9\relax\pgfcalendarmatchestrue\fi},
  Oct/.code={\ifnum\pgfcalendarifdatemonth=10\relax\pgfcalendarmatchestrue\fi},Nov/.code={\ifnum\pgfcalendarifdatemonth=11\relax\pgfcalendarmatchestrue\fi},Dez/.code={\ifnum\pgfcalendarifdatemonth=12\relax\pgfcalendarmatchestrue\fi},
  leap year/.code={%
    \pgfutil@tempcnta=#1\relax
    \divide\pgfutil@tempcnta4
    \multiply\pgfutil@tempcnta4
    \ifnum\pgfutil@tempcnta=#1\relax
      \divide\pgfutil@tempcnta100
      \multiply\pgfutil@tempcnta100
      \ifnum\pgfutil@tempcnta=#1\relax
        \divide\pgfutil@tempcnta400
        \multiply\pgfutil@tempcnta400
        \ifnum\pgfutil@tempcnta=#1\relax
          \pgfcalendarmatchestrue
        \fi
      \else
        \pgfcalendarmatchestrue
      \fi
    \fi},
  leap year/.default=\pgfcalendarifdateyear,
  week of month/.code={%
    \pgfutil@tempcnta=#1\relax
    \multiply\pgfutil@tempcnta7\relax
    \ifnum\pgfcalendarifdateday>\pgfutil@tempcnta\else
      \advance\pgfutil@tempcnta-7\relax
      \ifnum\pgfcalendarifdateday>\pgfutil@tempcnta
        \pgfcalendarmatchestrue
      \fi
    \fi},
  week of month'/.code={%
    \pgfcalendar@getlastYMX\pgfcalendarifdateyear\pgfcalendarifdatemonth\pgfutil@tempcnta
    \advance\pgfutil@tempcnta1
    \pgfutil@tempcntb=#1\relax
    \multiply\pgfutil@tempcntb7\relax
    \advance\pgfutil@tempcnta-\pgfutil@tempcntb\relax
    \ifnum\pgfcalendarifdateday<\pgfutil@tempcnta\else
      \advance\pgfutil@tempcnta+7\relax
      \ifnum\pgfcalendarifdateday<\pgfutil@tempcnta
        \pgfcalendarmatchestrue
      \fi
    \fi},
  and/.code 2 args={
    \pgfcalendar@matchesfalse
    \begingroup
      \ifdate{#1}
        {\ifdate{#2}{\pgfcalendar@matchestrue}{\pgfcalendar@matchesfalse}}
        {\pgfcalendar@matchesfalse}%
    \endgroup
    \ifpgfcalendar@matches\pgfcalendarmatchestrue\fi},
  first/.code={\pgfutil@in@:{#1}\ifpgfutil@in@
    \pgfcalendar@ifdate@first{}#1\pgf@stop\else
    \pgfcalendar@ifdate@first{}1:#1\pgf@stop\fi},
  last/.code={\pgfutil@in@:{#1}\ifpgfutil@in@
    \pgfcalendar@ifdate@first'#1\pgf@stop\else
    \pgfcalendar@ifdate@first'1:#1\pgf@stop\fi}}
\def\pgfcalendar@ifdate@first#1#2:#3\pgf@stop{%
  \pgfqkeys{/pgf/calendar}{and={#3}{week of month#1={#2}}}}
\def\pgfcalendar@getlastYMX#1#2#3{% #1 = year, #2 = month, #3 := last day
  \begingroup
  \ifnum#2=2 % stupid February
    \ifdate{leap year=#1}{#3=29\relax}{#3=28\relax}%
  \else
    #3=\ifcase\pgfcalendarifdatemonth\or
      31\or\or31\or30\or31\or30\or31\or31\or30\or31\or30\or31\fi\relax
  \fi\expandafter
  \endgroup\expandafter#3\the#3\relax}
\makeatother
\begin{document}

\begin{tikzpicture}[thick]
  \calendar[dates=2014-01-01 to 2014-12-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 (and={Jun}{first=Monday}) [blue]
%  if (first={and={Jun}{Monday}}) [blue]% the same as above
  if (and={Oct}{last=Monday})  [blue];
\end{tikzpicture}
\end{document}
share|improve this answer
    
Very powerful. Thanks a lot. The earlier solution worked, too, but this brings a lot more flexibility. –  healyp Oct 28 '13 at 11:06

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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