8

Assuming date is ISO-formatted (YYYY-MM-DD), how can I parse out its individual parts into variables?

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\keys_define:nn { note-verbale } {
  date .code:n = { },           % ???
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { note-verbale } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
\PrintDate 
\end{document}
4
  • I think there's a duplicate out there.
    – Manuel
    Feb 10, 2015 at 17:54
  • 1
    @Manuel You would think, but a search for expl3 iso date yields nil. Feb 10, 2015 at 17:55
  • @Manuel I suppose it's possible such a duplicate wouldn't mention expl3. Feb 10, 2015 at 18:12
  • It's bugging me, I think it was intended to be parsed exactly like this, with expl3 involved. And I think the asker was A.Ellet, but may be my memory is wrong, I can't find it.
    – Manuel
    Feb 10, 2015 at 18:17

2 Answers 2

9

Improving on your code:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\cs_new_protected:Npn \mytest_date_parse:n #1
 {
  \__mytest_date_parse:www #1 \q_stop
 }
\cs_new_protected:Npn \__mytest_date_parse:www #1-#2-#3 \q_stop
 {
  \int_set:Nn \mytest_date_year_int  {#1}
  \int_set:Nn \mytest_date_month_int {#2}
  \int_set:Nn \mytest_date_day_int   {#3}
 }

\keys_define:nn { my-test } {
  date .code:n = { \mytest_date_parse:n { #1 } },
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { my-test } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
\PrintDate 
\end{document}

It's better to split off the .code:n bit with the actual implementation. There's no need to define the parser macro with six arguments, as three are predetermined.

Don't forget that functions performing assignments should be protected.

Here's a check that the date is in ISO format: YYYY-MM-DD (but it allows month and year to be one digit); l3regex is the easiest tool. In case the date is invalid, 0/0/0 is printed, but it is easy to add a suitable error message.

\documentclass{article}
\usepackage{xparse,l3regex}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\cs_new_protected:Npn \mytest_date_parse:n #1
 {
  \regex_match:nnTF { \A \d{4}-\d{1,2}-\d{1,2} \Z } { #1 }
   {
    \__mytest_date_parse:www #1 \q_stop
   }
   {
    \__mytest_date_parse:www 0000-00-00 \q_stop
   }
 }
\cs_new:Npn \__mytest_date_parse:www #1-#2-#3 \q_stop
 {
  \int_set:Nn \mytest_date_year_int  {#1}
  \int_set:Nn \mytest_date_month_int {#2}
  \int_set:Nn \mytest_date_day_int   {#3}
 }

\keys_define:nn { my-test } {
  date .code:n = { \mytest_date_parse:n {#1} },
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { my-test } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
\PrintDate 

\Information{date=201-1-1}
\PrintDate

\Information{date=2015-1}
\PrintDate

\Information{date=2015/1/1}
\PrintDate

\end{document}

The regex means: the entire string (\A and \Z represent start and end) should consist of four digits (\d{4}), a hyphen, one to two digits \d{1,2}, a hyphen and again one to two digits. Any other format will return false.


A further version that accepts also date=20150101, that is a string of eight digits and splits the parsing into the two cases.

\documentclass{article}
\usepackage{xparse,l3regex}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\cs_new_protected:Npn \mytest_date_validate_parse:n #1
 {
  \regex_match:nnTF { \A \d{4}-\d{1,2}-\d{1,2} \Z } { #1 }
   {
    \__mytest_date_parse:www #1 \q_stop
   }
   {
    \regex_match:nnTF { \A \d{8} \Z } { #1 }
     {
      \__mytest_date_parse:nnnnnnnn #1
     }
     {
      \__mytest_date_parse:nnnnnnnn 00000000
     }
   }
 }

\cs_new:Npn \__mytest_date_parse:www #1-#2-#3 \q_stop
 {
  \int_set:Nn \mytest_date_year_int  {#1}
  \int_set:Nn \mytest_date_month_int {#2}
  \int_set:Nn \mytest_date_day_int   {#3}
 }

\cs_new:Nn \__mytest_date_parse:nnnnnnnn
 {
  \int_set:Nn \mytest_date_year_int  {#1#2#3#4}
  \int_set:Nn \mytest_date_month_int {#5#6}
  \int_set:Nn \mytest_date_day_int   {#7#8}
 }

\keys_define:nn { my-test } {
  date .code:n = { \mytest_date_validate_parse:n {#1} },
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { my-test } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
Good: \PrintDate 

\Information{date=201-1-1}%bad
Bad: \PrintDate

\Information{date=2015-1}%bad
Bad: \PrintDate

\Information{date=2015/1/1}%bad
Bad: \PrintDate

\Information{date=20150101}%good
Good: \PrintDate

\Information{date=201511}%bad
Bad: \PrintDate

\Information{date=201501012}%bad
Bad: \PrintDate

\end{document}

enter image description here

8
  • 1
    I think I'd want a defensive part in case the - tokens have gone AWOL!
    – Joseph Wright
    Feb 10, 2015 at 18:04
  • 1
    Didn't know about protected stuff – is there a section in texdoc interface3 that goes over this? Feb 10, 2015 at 18:04
  • 1
    @SeanAllred In the style guide: mirror.ctan.org/macros/latex/contrib/l3kernel/l3styleguide.pdf
    – Joseph Wright
    Feb 10, 2015 at 18:10
  • 1
    @JosephWright I added a validation with a regular expression, which in this case seems the best tool.
    – egreg
    Feb 10, 2015 at 18:13
  • 2
    @SeanAllred We are planning to try to give a new overview for LaTeX3 docs: I'll make sure the style guide is included.
    – Joseph Wright
    Feb 10, 2015 at 18:37
5

You can use a parser function and introduce a quark in the date .code:n. Note that the quark \q_stop is used to delimit the length of the last argument.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\int_new:N \mytest_date_day_int
\int_new:N \mytest_date_month_int
\int_new:N \mytest_date_year_int

\cs_new:Npn \mytest_date_parse:wNNN #1-#2-#3 \q_stop #4 #5 #6
 {
  \int_set:Nn #4 {#1}
  \int_set:Nn #5 {#2}
  \int_set:Nn #6 {#3}
 }

\keys_define:nn { my-test } {
  date .code:n = {
  \mytest_date_parse:wNNN
    #1 \q_stop
    \mytest_date_day_int
    \mytest_date_month_int
    \mytest_date_year_int
  },
}

\NewDocumentCommand \Information { m } {
  \keys_set:nn { my-test } { #1 }
}

\NewDocumentCommand \PrintDate { } {
  Date:\nobreakspace

  \int_use:N \mytest_date_day_int
  \slash
  \int_use:N \mytest_date_month_int
  \slash
  \int_use:N \mytest_date_year_int
}

\ExplSyntaxOff

\Information{
  date = 2015-02-10,
}

\begin{document}
\PrintDate 
\end{document}
7
  • 1
    I think I'd want a defensive part in case the - tokens have gone AWOL!
    – Joseph Wright
    Feb 10, 2015 at 18:04
  • 1
    @JosephWright As if the user said date = 20150210? Feb 10, 2015 at 18:05
  • 1
    which is of course a valid ISO format date Feb 10, 2015 at 18:07
  • 1
    @SeanAllred Or anything else, but yes: you've not tested for - tokens!
    – Joseph Wright
    Feb 10, 2015 at 18:08
  • 1
    @DavidCarlisle Huh – turns out you're absolutely right. I had always thought ISO was unambiguous for some of these bigger things, but I suppose the indecision runs through and through. Feb 10, 2015 at 18:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .