7

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}
  • I think there's a duplicate out there. – Manuel Feb 10 '15 at 17:54
  • 1
    @Manuel You would think, but a search for expl3 iso date yields nil. – Sean Allred Feb 10 '15 at 17:55
  • @Manuel I suppose it's possible such a duplicate wouldn't mention expl3. – Sean Allred Feb 10 '15 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 '15 at 18:17
8

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

  • I think I'd want a defensive part in case the - tokens have gone AWOL! – Joseph Wright Feb 10 '15 at 18:04
  • Didn't know about protected stuff – is there a section in texdoc interface3 that goes over this? – Sean Allred Feb 10 '15 at 18:04
  • @SeanAllred In the style guide: mirror.ctan.org/macros/latex/contrib/l3kernel/l3styleguide.pdf – Joseph Wright Feb 10 '15 at 18:10
  • @egreg Didn't you answer something quite similar with an \seq_ approach? With David giving a plainTeX solution? – Manuel Feb 10 '15 at 18:12
  • 1
    @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 '15 at 18:37
4

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}
  • I think I'd want a defensive part in case the - tokens have gone AWOL! – Joseph Wright Feb 10 '15 at 18:04
  • @JosephWright As if the user said date = 20150210? – Sean Allred Feb 10 '15 at 18:05
  • which is of course a valid ISO format date – David Carlisle Feb 10 '15 at 18:07
  • @SeanAllred Or anything else, but yes: you've not tested for - tokens! – Joseph Wright Feb 10 '15 at 18:08
  • @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. – Sean Allred Feb 10 '15 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.