# How to pass days to tikz calendar using macros

I'd like to pass to the calendar some day names using a personal command, so user can change the days easily and create a personal calendar.

I defined the command \mydays with one argument, which is passed to the list \mydayslist.

How to use this list within the calendar?

MWE

\documentclass{report}
\usepackage[margin=1cm,landscape]{geometry}
\usepackage{tikz}
\usetikzlibrary{calendar}

\newcommand*{\mydays}[1]{%
\def\mydayslist{#1}%
}
\mydays{Monday,Friday}

\begin{document}

I want to pass the days \mydayslist\ to the calendar.

\noindent\centering
\begin{tikzpicture}
[every day/.style={anchor=mid},every node/.style={inner sep=2pt,rectangle,thick}]
\calendar (cal) [dates=2014-02-01 to 2014-07-last, month list, month label left,%
month text=\textcolor{black}{\%mt}, month yshift=1.7em, black!20]
%
if (Tuesday) [black]
% if (\mydayslist) [black] %% <-- insert \mydayslist
;
\end{tikzpicture}

\end{document}

• You want to loop over the contents of the list? – user31729 Feb 12 '16 at 17:25
• @ChristianHupfer, no. The calendar allows to use if (Monday,Friday) [black]. So I only want to pass the days to it. – Sigur Feb 12 '16 at 17:27

You may use the if keys to define a style to test one day (myif in my example). Then you may use an .expand once handler (or .expanded handler) to expand your macro and a .list handler to apply your list.

\documentclass{report}
\usepackage[margin=1cm,landscape]{geometry}
\usepackage{tikz}
\usetikzlibrary{calendar}

\newcommand*{\mydays}[1]{%
\def\mydayslist{#1}%
}
\mydays{Monday,Friday}

\begin{document}

I want to pass the days \mydayslist\ to the calendar.

\noindent\centering
\begin{tikzpicture}
[every day/.style={anchor=mid},every node/.style={inner sep=2pt,rectangle,thick}]

\calendar (cal) [dates=2014-02-01 to 2014-07-last,month list,month label left,
month text=\textcolor{black}{\%mt},month yshift=1.7em, black!20,
myif/.style={if=(#1)[black]},
myif/.list/.expand once={\mydayslist},
];
\end{tikzpicture}

\end{document}


First Edit

An example with and condition from TikZ calendar and conditional tests.

\documentclass{report}
\usepackage[margin=1cm,landscape]{geometry}
\usepackage{tikz}
\usetikzlibrary{calendar}
% code from https://tex.stackexchange.com/a/141027/14500
\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
%------
\newcommand*{\mydays}[1]{%
\def\mydayslist{#1}%
}
\mydays{Monday,Friday}

\begin{document}

I want to pass the days \mydayslist\ to the calendar.

\noindent\centering
\begin{tikzpicture}
[every day/.style={anchor=mid},every node/.style={inner sep=2pt,rectangle,thick}]

\calendar (cal) [
dates=2014-02-01 to 2014-07-last,
month list,month label left,
month text=\textcolor{black}{\%mt},
month yshift=1.7em, black!20,
myif2/.style={if={(and={between=2014-04-01 and 2014-05-32}{#1})[red]}},
myif2/.list/.expanded={\mydayslist},
];
\end{tikzpicture}

\end{document}


Second Edit

Here is solution mixing Guho's answer and previous solution. I defined a new key day to test a list of days:

\pgfkeys{
/pgf/calendar/@day/.style={#1},
/pgf/calendar/day/.style={@day/.list/.expanded=#1},
}


With these keys, you may always use the specification syntax (p. 524 of pgfmanual).

Example with and condition from TikZ calendar and conditional tests.

\documentclass{report}
\usepackage[margin=1cm,landscape]{geometry}
\usepackage{tikz}
\usetikzlibrary{calendar}
% code from https://tex.stackexchange.com/a/141027/14500
\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
%------
\pgfkeys{
/pgf/calendar/@day/.style={#1},
/pgf/calendar/day/.style={@day/.list/.expanded=#1},
}
\newcommand*{\mydays}[1]{%
\def\mydayslist{#1}%
}
\mydays{Monday,Friday}

\begin{document}

I want to pass the days \mydayslist\ to the calendar.

\noindent\centering
\begin{tikzpicture}
[every day/.style={anchor=mid},every node/.style={inner sep=2pt,rectangle,thick}]

\calendar (cal) [
dates=2014-02-01 to 2014-07-last,
month list,month label left,
month text=\textcolor{black}{\%mt},
month yshift=1.7em, black!20,
]
if (day=Sunday) [black,font=\itshape]
if (day=\mydayslist) [green]
if (and={between=2014-04-01 and 2014-05-32}{day=\mydayslist}) [red]
;
\end{tikzpicture}

\end{document}


Third Edit

My third solution defines the macro \calkeys (to manage key-value pairs as \pgfkeys or \tikzset but with /pgf/calendar/ prefix). With this macro, you may define any calendar selector as a named style:

\def\calkeys#1{\pgfkeys{/pgf/calendar/.cd,#1}}
\calkeys{
my day list/.style={Friday,Saturday},
april/.style={between=2014-04-01 and 2014-04-30},
my day list in april/.style={and={april}{my day list}},
}


Example:

\documentclass[margin=3mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calendar}
% code from https://tex.stackexchange.com/a/141027/14500
\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
% -----
\def\calkeys#1{\pgfkeys{/pgf/calendar/.cd,#1}}
\calkeys{
my day list/.style={Friday,Saturday},
april 2014/.style={between=2014-04-01 and 2014-04-30},
my day list in april 2014/.style={and={april 2014}{my day list}},
}

\begin{document}
\begin{tikzpicture}
[every day/.style={anchor=mid},every node/.style={inner sep=2pt,rectangle,thick}]
\calendar (cal) [dates=2014-02-01 to 2014-07-last,month list,month label left,
month text=\textcolor{black}{\%mt},month yshift=1.7em, black!20]
if (my day list) [red]
if (my day list in april 2014) [green]
;
\end{tikzpicture}
\end{document}

• Nice! But I have to try it with the and function from this post. – Sigur Feb 16 '16 at 1:32
• @Sigur Where is the problem? – Paul Gaborit Feb 16 '16 at 1:34
• @Sigur Done by edit... – Paul Gaborit Feb 16 '16 at 1:48
• @Sigur You may use any name ! Example: between 2014-04-01 and 2014-05-32 in red. Limitation: no =, {, } or ,. – Paul Gaborit Feb 16 '16 at 1:53
• @Sigur My second edit provides a better and simpler solution. – Paul Gaborit Feb 17 '16 at 9:06

Here's a version with a wrapper command for the tikz code, that does pre expand the argument (here \mydayslist) -- the usual problem with key value interfaces is the missing expansion of key values, which is the case here!

\documentclass{report}
\usepackage[margin=1cm,landscape]{geometry}
\usepackage{tikz}

\usetikzlibrary{calendar}

\def\mydayslist{}
\newcommand*{\mydays}[1]{%
\def\mydayslist{#1}%
}

\mydays{Monday,Tuesday,Friday}

\newcommand{\sigurslittlehelper}[1]{%
if (#1) [black]
;
}

\newcommand{\tikzdo}[1]{% Expand the list first, then use the tikz code
\expandafter\sigurslittlehelper\expandafter{#1}%
}

\begin{document}

I want to pass the days \mydayslist\ to the calendar.

\noindent\centering
\begin{tikzpicture}
[every day/.style={anchor=mid},every node/.style={inner sep=2pt,rectangle,thick}]
\calendar (cal) [dates=2014-02-01 to 2014-07-last, month list, month label left,%
month text=\textcolor{black}{\%mt}, month yshift=1.7em, black!20]
\tikzdo{\mydayslist}
\end{tikzpicture}

\end{document}


• +1. It worked. Thanks. I really have to learn about \expandafter. – Sigur Feb 12 '16 at 17:41
• @Sigur: Well, most of us should ;-) I am not claiming that I have understood every aspect of \expandafter however – user31729 Feb 12 '16 at 17:51
• Is it possible to keep the if ( ) [ ] code inside tikzpicture instead of outside in other command? My code is bigger so I'd like to keep it there for better reading it. – Sigur Feb 12 '16 at 17:52
• @Sigur: That's what I have tried so far but I failed. I can understand your issue, however – user31729 Feb 12 '16 at 17:55
• \expandafter inside ( ) does not work here :-(. – Sigur Feb 12 '16 at 17:57

This seems to work. It defines a key to expand the control sequence prior to passing it to the internal \ifdate. Derived from this answer.

\documentclass{report}
\usepackage[margin=1cm,landscape]{geometry}
\usepackage{tikz}
\usetikzlibrary{calendar}

\newcommand*{\mydays}[1]{%
\def\mydayslist{#1}%
}
\mydays{Monday,Friday}
\begin{document}

I want to pass the days \mydayslist\ to the calendar.

\noindent\centering
\begin{tikzpicture}
[every day/.style={anchor=mid},every node/.style={inner sep=2pt,rectangle,thick}]
\calendar (cal) [dates=2014-02-01 to 2014-07-last, month list, month label left,%
month text=\textcolor{black}{\%mt}, month yshift=1.7em, black!20]
%
if (Tuesday) [black]
if (fromcs={\mydayslist}) [black] %% <-- insert \mydayslist
;
\end{tikzpicture}

\end{document}


• I got errors Package pgfkeys Error: I do not know the key '/pgf/calendar/Monday,Friday' and I am going to ignore it. Perhaps you misspelled it. ; from your code. – Sigur Feb 16 '16 at 0:19
• @Sigur: That's what I get for trying to optimize. I removed the \expandafters to see if \ifdate would perform the expansion itself. For some reason I thought it worked on my test file, so I edited the answer. Not sure where I went wrong. The post is fixed now. Sorry and thanks for the catch! – Guho Feb 16 '16 at 0:24
• It seems to work. Let me test a little bit more. Thanks. – Sigur Feb 16 '16 at 0:24