# How to parse multiple arguments in TeX macros

I have dates formatted {yyyy-mm-dd}. I would like display the time elapsed between the dates in the form Y years, M months. Given the format of the dates, it seems easier to use arithmetic than to manipulate them as dates.

I am trying to call the macro with two date arguments, parsing each one of them. This is the part that is failing, I believe due to the interaction between the two date arguments and their separators. Is there a way to do this within the macro definition, or is it necessary to define another macro to act on each date argument separately?

\documentclass{article}

\newcount\elapsedyears
\newcount\elapsedmonths

\def\elapsedyearsmonths[#1-#2-#3]#4-#5-#6\relax{%
\ifnum#2>#5
\elapsedyears=\numexpr#1-#4
\elapsedmonths=\numexpr#2-#5+1
\else
\elapsedyears=\numexpr#1-#4-1
\elapsedmonths=\numexpr#2-#5+12
\fi
\the\elapsedyears{}years, \the\elapsedmonths{} months
}

\begin{document}

\def\myenddate{2013-10-22}
\def\mystartdate{2010-09-15}

\elapsedmonthsyears{2014-11-06}{2012-02-03}

\elapsedmonthsyears\myenddate\mystartdate

\end{document}


I see now that I will have to create some more conditional statements to pluralise, or not, "year(s)" and "month(s)" also.

Cheers

• You should take a look at the datetime package... ;). – cfr Nov 26 '14 at 1:05

As written, the posted code produces

! Undefined control sequence.
l.22 \elapsedmonthsyears


so changing the last two calls to use \elapsedyearsmonths

produces

! Use of \elapsedyearsmonths doesn't match its definition.
l.22 \elapsedyearsmonths{


as the call on line 22 is followed by a {

(the end of line in the error message shows TeX stopped at that point)

as as written the macro has a [ delimiter before #1 so must be followed by [.

Changing the calls to

\elapsedyearsmonths[2014-11-06]2012-02-03\relax

\elapsedyearsmonths[\myenddate]\mystartdate\relax


produces

! Paragraph ended before \elapsedyearsmonths was complete.
\par
l.25


which is good, it means that the first call did something, as the error is line 25.

The second call failed as TeX ran past the blank line, while looking for a - that is not there.

Adding a macro to expand the arguments:

\def\xelapsedyearsmonths#1#2{%
\edef\tmp{\noexpand\elapsedyearsmonths[#1]#2\relax}%
\tmp}


and changing the calls to

\xelapsedyearsmonths{2014-11-06}{2012-02-03}

\xelapsedyearsmonths\myenddate\mystartdate


It runs without error

\documentclass{article}

\newcount\elapsedyears
\newcount\elapsedmonths

\def\elapsedyearsmonths[#1-#2-#3]#4-#5-#6\relax{%
\ifnum#2>#5
\elapsedyears=\numexpr#1-#4
\elapsedmonths=\numexpr#2-#5+1
\else
\elapsedyears=\numexpr#1-#4-1
\elapsedmonths=\numexpr#2-#5+12
\fi
\the\elapsedyears{}years, \the\elapsedmonths{} months
}

\def\xelapsedyearsmonths#1#2{%
\edef\tmp{\noexpand\elapsedyearsmonths[#1]#2\relax}%
\tmp}

\begin{document}

\def\myenddate{2013-10-22}
\def\mystartdate{2010-09-15}

\xelapsedyearsmonths{2014-11-06}{2012-02-03}

\xelapsedyearsmonths\myenddate\mystartdate

\end{document}

• This working great, except calling \xelapsedyearsmonths results in a leading space that is messing up my formatting. Why? – Jonathan Nov 29 '14 at 10:49
• only space should be after months you can put an example in the question and ping me here if you have a reproducible test – David Carlisle Nov 29 '14 at 11:57
• My mistake. I left out the % at the end of the first line of the \elapsedyearsmonths definition when copying the code. – Jonathan Nov 30 '14 at 16:28

Here's an implementation with expl3:

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

% the user level command; with :oo both arguments are expanded once
% before being processed
\NewDocumentCommand{\elapsedmonthsyears}{mm}
{
\jonathan_elapsed_months:oo{#1}{#2}
}

% some variables for later usage
\seq_new:N \l_jonathan_end_date_seq
\seq_new:N \l_jonathan_start_date_seq
\int_new:N \l_jonathan_months_int

\cs_new_protected:Npn \jonathan_elapsed_months:nn #1 #2
{
% split the first argument into components {yearend}{monthend}[dayend}
\seq_set_split:Nnn \l_jonathan_end_date_seq { - } { #1 }
% split the second argument into components {yearstart}{monthstart}[daystart}
\seq_set_split:Nnn \l_jonathan_start_date_seq { - } { #2 }
% compute d=(yearend*12+monthend)-(yearstart*12+monthstart)
% which gives the difference in months
\int_set:Nn \l_jonathan_months_int
{
(
\seq_item:Nn \l_jonathan_end_date_seq { 1 } * 12 +
\seq_item:Nn \l_jonathan_end_date_seq { 2 }
)
-
(
\seq_item:Nn \l_jonathan_start_date_seq { 1 } * 12 +
\seq_item:Nn \l_jonathan_start_date_seq { 2 }
)
}
% print the number of years, d/12 (truncated)
\int_to_arabic:n { \int_div_truncate:nn { \l_jonathan_months_int } { 12 } }
\nobreakspace
year
% if the number of years is =1 don't add an "s"
\int_compare:nF { \int_div_truncate:nn { \l_jonathan_months_int } { 12 } = 1}{s}
% print a comma with a space
,~
% print the number of months, d mod 12
\int_to_arabic:n { \int_mod:nn { \l_jonathan_months_int } { 12 } }
\nobreakspace
month
% if the number of months is =1 don't add an "s"
\int_compare:nF { \int_mod:nn { \l_jonathan_months_int } { 12 } = 1 }{s}
}

% generate the :oo variant
\cs_generate_variant:Nn \jonathan_elapsed_months:nn { oo }
\ExplSyntaxOff

\begin{document}

\def\myenddate{2013-10-22}
\def\mystartdate{2010-09-15}

\elapsedmonthsyears{2014-11-06}{2012-02-03}

\elapsedmonthsyears\myenddate\mystartdate

\end{document}


Not good, but not too bad:

%begin time definitions
\newcount\eyear
\newcount\emonth
\newcount\eday
\newcount\ehour
\newcount\eminutes
% \time
\newcount\hour
\newcount\hours
\newcount\minutes
\hour=\time \divide\hour by 60
\minutes=\time
\hours=\hour \multiply\hours by 60
\def\fromdate[#1-#2-#3 #4:#5]{%
\eyear=\year%
\emonth=\month%
\eday=\day%
\ehour=\hour%
\eminutes=\minutes%
\def\elapsed{%
\vskip1\baselineskip From Date #1-#2-#3 #4:#5\vskip1\baselineskip%
Difference (Years:\the\eyear)-(Months:\the\emonth)-(Days:\the\eday)%
(Hours:\the\ehour):(Minutes:\the\eminutes)
}%
Current Date \the\year-\the\month-\the\day\ \ifnum\hour<10%
0\fi\the\hour:\ifnum\minutes<10 0\fi\the\minutes%
\elapsed%
\vskip2\baselineskip
}%
\fromdate[2013-10-3 1:10]
\fromdate[2012-1-30 23:10]
\fromdate[2010-12-10 3:10]
%end  \time defintions
\bye