# Runaway argument? with \newcommand[2]

I have a date (in the format YYYY.MM.DD) and a time (format HH:MM).

From these, I want to compute a kind of TimeStamp/Total of minutes.

\documentclass{article}

\makeatletter

% Timestamp
\newcommand{\timeStamp}[2]{%{#1-Date (YYYY.MM.DD)}{#2-Time (HH:MM)}
\expandafter\timeStamp@t#1 #2\@nil%
}%
\def\timeStamp@t#1.#2.#3 #4:#5\@nil{%
\the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%

\makeatother

\begin{document}
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\timeStamp{\dOne}{\tOne}\\%
\end{document}


The compiler says: "Runaway argument?"

This is probably due to a problem with the extension of the second argument, because this call (\timeStamp{\dOne}{03:00}) is Ok. What am I doing wrong?

\expandafter only expands one token after the token that follows (unless there are arguments). In your case this is just #1. One way to get your result, is to collect #1 #2 expanded in a macro before insertion:

\documentclass{article}

\makeatletter

% Timestamp
\newcommand{\timeStamp}[2]{%{#1-Date (YYYY.MM.DD)}{#2-Time (HH:MM)}
\edef\mytmp{#1 #2}\expandafter\timeStamp@t\mytmp\@nil%
}%
\def\timeStamp@t#1.#2.#3 #4:#5\@nil{%
\the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%

\makeatother

\begin{document}
\tracingmacros=2\tracingcommands=2
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\timeStamp{\dOne}{\tOne}
\end{document}


This approach should generalise well to more than two arguments.

• Thx. Clear and concise. – Joseph Marie Jun 18 '18 at 11:13
• But not fully expandable. – egreg Jun 18 '18 at 11:23
• Indeed @egreg. I faced this problem in my "real" code, so I switched to a more complicated solution. – Joseph Marie Jun 19 '18 at 7:38

One can do some juggling with \expandafter, but a direct approach may be better:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\cs_new:Nn \joseph_time_stamp:nn
{
\__joseph_time_stamp:w #1.#2\q_stop
}
\cs_generate_variant:Nn \joseph_time_stamp:nn { ff }
% a devious trick for the colon
\use:x
{
\cs_new:Npn
\exp_not:N \__joseph_time_stamp:w
##1.##2.##3.##4\token_to_str:N :##5
\exp_not:N \q_stop
}
{
\int_eval:n
{
#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
}
}
\NewExpandableDocumentCommand{\timeStamp}{mm}
{
\joseph_time_stamp:ff { #1 } { #2 }
}
\ExplSyntaxOff

\begin{document}

\def\tOne{03:00}
\def\dOne{2017.08.01}

\timeStamp{\dOne}{\tOne}

\timeStamp{2017.1.1}{0:0} % should print 0

\end{document}


If the time specification didn't include the colon, it would be easier; the problem is that : is special in the scope of \ExplSyntaxOn, so the direct

\cs_new:Npn \__joseph_time_stamp:w #1.#2.#3.#4:#5\q_stop
{
\int_eval:n
{
#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
}
}


would not work and an indirect method is needed in order to “stringify” the colon.

An expandable solution with \expandafter and argument juggling (OS for “old style”):

\makeatletter
\newcommand{\timeStampOS}[2]{%
\expandafter\timeStampOS@a\expandafter{#2}{#1}%
}
\newcommand{\timeStampOS@a}[2]{%
\expandafter\timeStampOS@b\expandafter{#2}{#1}%
}
\newcommand{\timeStampOS@b}[2]{\timeStampOS@c #1 #2\@nil}
\def\timeStampOS@c #1.#2.#3 #4:#5\@nil{%
\the\numexpr
#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365
\relax
}
\makeatother

• Thanks a lot for all and excuse me for all. I have added the link for the traslation. +1. – Sebastiano Jun 18 '18 at 11:38

expand #1 and #2

% Timestamp
\newcommand\timeStamp[2]{%
\expandafter\timeStamp@t#2 #1\@nil}%
\def\timeStamp@t#1:#2 #3\@nil{\expandafter\timeStamp@@t#3 #1:#2\@nil}%
\def\timeStamp@@t#1.#2.#3 #4:#5\@nil{%
\the\numexpr#5+#4*60+(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax%
}%

• Thx. But now, I am completely confused about '\expandafter'... – Joseph Marie Jun 18 '18 at 11:07
• @JosephMarieit expands the first token after the macro after \expandafter, there are two tokens here. – daleif Jun 18 '18 at 11:11

It is clearer to:

• devote macros to sub-computations, possibly reusable elsewhere,

• indicate in the macro name the expansion type it does on its arguments.

Like this: (O meaning "expands once the argument"; often one would prefer "F" for f-type expansion which expands repeatedly the first token, as this allows nesting. Else one would need e.g. T for "expands twice the argument", because with \def\foo{\the\numexpr...}, one needs two expansions for fully expanding).

\documentclass{article}

\newcommand{\TimeStampOO}[2]
{\the\numexpr\expandafter\DateToMinutes\expandafter{#1}+
\expandafter\TimeToMinutes\expandafter{#2}\relax}

\makeatletter
\newcommand{\DateToMinutes}[1]{\the\numexpr\Date@ToMinutes#1\relax}
\def\Date@ToMinutes#1.#2.#3\relax
{(#3-1)*60*24+(#2-1)*60*24*31+(#1-2017)*60*24*31*365\relax}
\newcommand{\TimeToMinutes}[1]{\the\numexpr\Time@ToMinutes#1\relax}
\def\Time@ToMinutes#1:#2\relax
{#2+#1*60\relax}
\makeatother

\begin{document}
\def\tOne{03:00}%
\def\dOne{2017.08.01}%
\TimeStampOO{\dOne}{\tOne}%
\end{document}


This is expandable.

• the auxiliary macros could use bare \numexpr, but with \the\numexpr (which adds some overhead in the global computation) they can be used top-level. – user4686 Jun 18 '18 at 13:09