# Extract number from string and compare problem

I try to extract the year, month and day from a string and write different text depending on the given date. I thought I could use xstring and etoolbox, but I can'st get it to work. Here's the code snippet:

\usepackage{xstring}
\usepackage{etoolbox}

\begin{document}
\newcommand{\Datum}{04.12.2011}

\newcommand{\Jahr}{\StrBehind[2]{\Datum}{.}}
\newcommand{\Monat}{\StrBetween[1,2]{\Datum}{.}{.}}
\newcommand{\Tag}{\StrBefore[1]{\Datum}{.}}

Jahr: \Jahr

\ifnumcomp{\Jahr}{=}{2011}{Jahr eq 2011}{Jahr neq 2011}


Which gives the following error:

pdflatex> ! Missing number, treated as zero.
pdflatex>                    \let
pdflatex> l.11 {305}
pdflatex>
pdflatex> ! Missing = inserted for \ifnum.


But \Jahr seems to correctly contain 2011.

-
Welcome to TeX.SE. When asking questions about problems one is encountering with (La)TeX code, it is helpful to provide a complete Minimum Working Example (MWE) rather than code snippets. To help debug problems, it's generally also helpful to provide information about the TeX distribution one is using. –  Mico Jan 29 '12 at 20:17

You are passing the complete xstring-test to ifnumcomp. That fails. You can save the the result of the xstring-test by the optional argument of \StrBehind and pass this to \ifnumcomp:

\documentclass{article}
\usepackage{xstring}
\usepackage{etoolbox}

\begin{document}
\newcommand{\Datum}{04.12.2011}

\StrBehind[2]{\Datum}{.}[\Jahr]
\StrBetween[1,2]{\Datum}{.}{.}[\Monat]
\StrBefore[1]{\Datum}{.}[\Tag]

Jahr: \Jahr

\ifnumcomp{\Jahr}{=}{2011}{Jahr eq 2011}{Jahr neq 2011}
\end{document}


Maybe you want to combine the output and the test you can use:

\documentclass{article}
\usepackage{xstring}
\usepackage{etoolbox}
\makeatletter
\newcommand\Datum[1]{\@datum{#1}\@executetest{#1}}
\def\@datum#1{#1}
\def\@executetest#1{%
\StrBehind[2]{#1}{.}[\Jahr]
\StrBetween[1,2]{#1}{.}{.}[\Monat]
\StrBefore[1]{#1}{.}[\Tag]
}
\begin{document}
\Datum{04.12.2011}

Jahr: \Jahr

\ifnumcomp{\Jahr}{=}{2011}{Jahr eq 2011}{Jahr neq 2011}
\end{document}

-
Wow, thanks, that was quick! –  Tom Jan 29 '12 at 21:43

Modern parsing packages take care of some things that probably should be taken care of, but sometimes (or just as an illustrative example) it's simpler to use the TeX primitives directly:

\documentclass{article}

\def\datum{04.12.2011}

\def\Jahr{\expandafter\xJahr\datum\relax}
\def\Monat{\expandafter\xMonat\datum\relax}
\def\Tag{\expandafter\xTag\datum\relax}

\def\xJahr#1.#2.#3\relax{#3}
\def\xMonat#1.#2.#3\relax{#2}
\def\xTag#1.#2.#3\relax{#1}

\begin{document}

Jahr: \Jahr

\ifnum\Jahr=2011
Jahr eq 2011
\else
Jahr neq 2011
\fi

\end{document}

-
+1 for not shooting with cannon balls on sparrows and a good example of using TeX directly for parsing. For the sake of the "illustrativeness" of the example you might want to consider adding a bit of explanation to aid the beginner (e.g., how TeX matches argument patterns and the roles of \expandafter and \relax). –  Daniel Jan 30 '12 at 8:42
\expandafter makes \datum expand before \xJahr, so \Jahr is equivalent to \xJahr 04.12.2011\relax then \xJah takes three arguments delimited by . or \relax and returns one of them. The \relax token is just used as a token to mark the end of the input here, it's never actually executed, and using an undefined command or a . or \documentclass would have worked just as well, so long as \xJah is defined to consume everything up to the token inserted by \Jahr. –  David Carlisle Jan 30 '12 at 10:27
David, I would suggest to edit this information into your answer (and then delete the comment, as will I do with mine). –  Daniel Jan 30 '12 at 17:25

A different implementation with xparse:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\datetest}{ O{\Datum} m m m }
{
\seq_set_split:Nnx \l_datetest_date_seq { . } { #1 }
\int_compare:nTF { \datetest_get:n { #2 } #3 #4 }
}
\cs_new:Npn \datetest_get:n #1
{
\prg_case_str:nnn { #1 }
{
{ Jahr }  { \seq_item:Nn \l_datetest_date_seq { 2 } }
{ Monat } { \seq_item:Nn \l_datetest_date_seq { 1 } }
{ Tag }   { \seq_item:Nn \l_datetest_date_seq { 0 } }
}
{ 0 }
}
\seq_new:N \l_datetest_date_seq
\cs_generate_variant:Nn \seq_set_split:Nnn { Nnx }
\ExplSyntaxOff

\newcommand{\Datum}{04.8.2011}

\begin{document}

\datetest{Jahr}{=}{2011}{Jahr eq 2011}{Jahr neq 2011}

\datetest{Monat}{<=}{9}{$\le$}{$>$}

\newcommand{\mydate}{01.02.2003}

\datetest[\mydate]{Monat}{>}{3}{After March}{Before April}

\datetest[01.05.2010]{Monat}{>}{3}{After March}{Before April}
\end{document}


\datetest acts by default on \Datum, but a macro or an explicit date can be passed as optional argument. The item to test is the first argument, the type of test (<, <=, =, >=, or >) as second argument; then the number to test and what to do if the test is either true or false.

Note that the second argument is a string, not a command. One can extend the macros to define \Jahr, \Monat or \Tag, if so desired.

-
+1. Rather than \tl_set:Nx and \tl_replace_all:Nnn, I'd use \seq_set_split:Nnx \l_foo_seq {.} {#1}, then use \seq_item:Nn where you used \clist_item:Nn. It is cleaner because you are not converting a tl into a clist by hand. By the way, \clist_set:Nn \l_foo_clist { a~, } results in \l_foo_clist containing a only, so a clist is not simply a token list with some commas. –  Bruno Le Floch Jan 30 '12 at 7:25
@BrunoLeFloch Thanks, \seq_set_split is just what I was looking for. –  egreg Jan 30 '12 at 9:55