5

I have made a command that looks like this:

\newcommand*{\numdash}{\,--\,}

\ExplSyntaxOn
\NewDocumentCommand{\dateRange}{mmmmmm}
    {
        \str_case:nnF { #1 }
        {
        {#4}  { 
            \str_case:nnF { #2 }
            {
            {#5}  { 
                \str_case:nnF { #3 }
                {
                {#6}  { \DTMdisplaydate{#4}{#5}{#6}{-1} }%
                }{\DTMordinal{#3}{}{}\numdash{}\DTMdisplaydate{#4}{#5}{#6}{-1}}
            }
            }{\DTMordinal{#3}~\DTMmonthname{#2}%
            {}\numdash{}\DTMdisplaydate{#4}{#5}{#6}{-1}}
        }
        }{\DTMdisplaydate{#1}{#2}{#3}{-1}{}\numdash{}\DTMdisplaydate{#4}{#5}{#6}{-1}}
    }
\ExplSyntaxOff

It displays the duration from one date to the next in a succinct matter. See the image below.

enter image description here

However, I need to manipulate the end date by incrementing it by a few days. Something like \dateRange{2016}{12}{31}{2016}{12}{31 + 1} does not work. I found the following code

%https://tex.stackexchange.com/questions/318006/add-n-days-to-variable-date
\DTMsavedate{DeadLineDate}{2016-05-20}
\newcommand{\DeadLineDateExtend}{1}

\newcount\daycount
\newcommand{\dueDate}[1]{%
    \DTMsaveddateoffsettojulianday{DeadLineDate}{#1}\daycount
    \DTMsavejulianday{newDeadLineDate}{\number\daycount}
    \DTMusedate{newDeadLineDate}
 }

Which can add days to a DTMdate. My problem is making these two functions work together.

  1. Rewrite dateRange so that it inputs two DTMdates instead of the plain numbers.
  2. Exctract the year, month and date from newDeadLineDate and insert it back into dateRange

So what I need help with is to first have two dates, increment one of them and display the daterange using \dateRange function. Any help with 1. or 2. from the list above is greatly appreciated.

\documentclass{article}

\usepackage[english]{babel}
\usepackage[en-GB,calc]{datetime2}

\usepackage{xparse}

% https://tex.stackexchange.com/questions/390693/datetime-ranges-using-datetime2/390738
\newcommand*{\numdash}{\,--\,}

\ExplSyntaxOn
\NewDocumentCommand{\dateRange}{mmmmmm}
    {
        \str_case:nnF { #1 }
        {
        {#4}  { 
            \str_case:nnF { #2 }
            {
            {#5}  { 
                \str_case:nnF { #3 }
                {
                {#6}  { \DTMdisplaydate{#4}{#5}{#6}{-1} }%
                }{\DTMordinal{#3}{}{}\numdash{}\DTMdisplaydate{#4}{#5}{#6}{-1}}
            }
            }{\DTMordinal{#3}~\DTMmonthname{#2}%
            {}\numdash{}\DTMdisplaydate{#4}{#5}{#6}{-1}}
        }
        }{\DTMdisplaydate{#1}{#2}{#3}{-1}{}\numdash{}\DTMdisplaydate{#4}{#5}{#6}{-1}}
    }
\ExplSyntaxOff

%https://tex.stackexchange.com/questions/318006/add-n-days-to-variable-date
\DTMsavedate{DeadLineDate}{2016-05-20}
\newcommand{\DeadLineDateExtend}{1}

\newcount\daycount
\newcommand{\dueDate}[1]{%
    \DTMsaveddateoffsettojulianday{DeadLineDate}{#1}\daycount
    \DTMsavejulianday{newDeadLineDate}{\number\daycount}
    \DTMusedate{newDeadLineDate}
 }

\begin{document}

\dateRange{2016}{12}{31}{2016}{12}{31}

\dateRange{2016}{12}{30}{2016}{12}{31}

\dateRange{2016}{11}{31}{2016}{12}{31}

\dateRange{2015}{12}{31}{2016}{12}{31}

\end{document}
  • You did not say how you want the \DeadLineDateExtend to be handled in the desired function. Do you want to modify \dateRange to always add \DeadLineDateExtend to the end date? Do you want it to accept the number of additional days as an additional argument (maybe an optional one, defaulting to 0)? Do you want a new function to deal with the extension?.. In other words, what is the desired interface (API)? – frougon Aug 21 at 14:44
6

Extracting the year, month and day from a named datetime2 date

As you probably noticed in your code sample where the commands \DTMsavedate and \DTMsavejulianday are used, the datetime2 package can save dates in special stores identified by a 〈name〉. Quoting section Storing and Using Dates and Times of the datetime2 manual:

In the commands below, the 〈name〉 (no active characters) is a name that uniquely identifies the information.

When you have a date in such a store, as is the case after executing \DTMsavejulianday{〈name〉}{〈number〉}, you can extract the corresponding year, month and day using the commands \DTMfetchyear, \DTMfetchmonth and \DTMfetchday. Each of these commands takes a 〈name〉 argument and expands to the corresponding number. In other words, \DTMfetchyear{〈name〉} expands to the year, \DTMfetchmonth{〈name〉} to the month and \DTMfetchday{〈name〉} to the day. The rest is essentially plumbing work. :-)

Connecting the different parts

I propose a \dateRange command that behaves like yours, except that:

  • it accepts an optional argument (defaulting to 0) that specifies the number of days you want to add to the end date before displaying the resulting range;

  • it uses a no-break space between the day ordinal and the month name (~ is a normal space under \ExplSyntaxOn regime, but I believe you wanted a no-break space);

  • it uses integer comparisons, so that 01 is considered the same as 1, 02 the same as 2, etc. for all day, month and year arguments (if you don't want that, use \str_if_eq:nnTF as in the first revision of this answer, but I don't see any good reason to do so).

I reindented the function and used \int_compare:nNnTF instead of \str_case:nnF to make the code easier to read and address the third point above.

I also defined a code-level function \nebuch_display_date_range:nnnnnn to contain this reworked code, because it is much easier to reuse in different situations this way. This allows us to generate a variant \nebuch_display_date_range:nnnxxx that fully expands the three last arguments before passing them to the base function \nebuch_display_date_range:nnnnnn (full expansion as done by \edef). This only requires a simple line of code:

\cs_generate_variant:Nn \nebuch_display_date_range:nnnnnn { nnnxxx }

Using the variant, it's easy to pass the result of the temporary date calculation (which adds the offset) to the base function \nebuch_display_date_range:nnnnnn, because \DTMfetchday, \DTMfetchmonth and \DTMfetchyear are all expandable functions (see the documentation of datetime2).

The computation of the offset date is also implemented in a code-level function, namely \nebuch_compute_offset_date:nnnnn, to make it easily reusable by other code (and it is used in my implementation of \dateRange).

\documentclass{article}
\usepackage[english]{babel}
\usepackage[en-GB,calc]{datetime2}
\usepackage{xparse}

\newcommand*{\numdash}{\,--\,}

\ExplSyntaxOn

% Similar to your \dateRange, but we use integer comparisons here and the
% code-level API allows the creation of variants with \cs_generate_variant:Nn.
\cs_new_protected:Npn \nebuch_display_date_range:nnnnnn #1#2#3#4#5#6
  {
    \int_compare:nNnTF {#1} = {#4}
      {
        \int_compare:nNnTF {#2} = {#5}
          {
            \int_compare:nNnTF {#3} = {#6}
              { \DTMdisplaydate {#4} {#5} {#6} {-1} }
              {
                \DTMordinal {#3} {} {} \numdash
                \DTMdisplaydate {#4} {#5} {#6} {-1}
              }
          }
          {
            % I replaced ~ with \nobreakspace here because of \ExplSyntaxOn
            \DTMordinal {#3} \nobreakspace \DTMmonthname {#2} \numdash
            \DTMdisplaydate {#4} {#5} {#6} {-1}
          }
      }
      {
        \DTMdisplaydate {#1} {#2} {#3} {-1} \numdash
        \DTMdisplaydate {#4} {#5} {#6} {-1}
      }
  }

\cs_generate_variant:Nn \nebuch_display_date_range:nnnnnn { nnnxxx }

\newcount \nebuch_tmp_count

% #1, #2, #3: year, month, day
% #4: number of days (offset)
% #5: <name> (in the sense of datetime2) used to store the resulting date
\cs_new_protected:Npn \nebuch_compute_offset_date:nnnnn #1#2#3#4#5
  {
    \DTMsavedate { nebuch_compute_offset_date_tmp_date } { #1-#2-#3 }
    \DTMsaveddateoffsettojulianday
      { nebuch_compute_offset_date_tmp_date } {#4} { \nebuch_tmp_count }
    \DTMsavejulianday {#5} { \number \nebuch_tmp_count }
  }

\NewDocumentCommand { \dateRange } { O{0} m m m m m m }
  {
    \nebuch_compute_offset_date:nnnnn {#5} {#6} {#7} {#1}
                                      { nebuch_dateRange_tmp_date }
    \nebuch_display_date_range:nnnxxx {#2} {#3} {#4}
      { \DTMfetchyear { nebuch_dateRange_tmp_date } }
      { \DTMfetchmonth { nebuch_dateRange_tmp_date } }
      { \DTMfetchday { nebuch_dateRange_tmp_date } }
  }

\ExplSyntaxOff

\begin{document}

\dateRange{2016}{12}{31}{2016}{12}{31}\par
\dateRange{2016}{12}{30}{2016}{12}{31}\par
\dateRange{2016}{11}{31}{2016}{12}{31}\par
\dateRange{2015}{12}{31}{2016}{12}{31}

\bigskip
\dateRange[3]{2016}{12}{31}{2016}{12}{31}\par
\dateRange[3]{2016}{12}{31}{2016}{12}{28}\par
\dateRange[3]{2016}{12}{30}{2016}{12}{31}\par
\dateRange[3]{2016}{12}{30}{2016}{12}{28}\par
\dateRange[31]{2016}{11}{31}{2016}{12}{31}\par
\dateRange[31]{2016}{11}{31}{2016}{11}{30}\par
\dateRange[365]{2015}{12}{31}{2016}{12}{31}\par
\dateRange[365]{2015}{12}{31}{2016}{01}{01}

\bigskip
\dateRange{2016}{02}{7}{2016}{2}{07}

\end{document}

Screenshot

  • This looks perfect, thank you so much! This might require a second question, but is there a way to add a toggle to skip weekends? – N3buchadnezzar Aug 22 at 7:35
  • This is indeed a different question. If you are thinking about the end date (shouldn't be on weekend?), you can certainly examine which day of the week it is and add an additional offset according to the result. If the skipping is for the whole offset, as in “weekend days don't count,” it's a bit more difficult. – frougon Aug 22 at 7:39

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.