Subject: Re: bug#47476: relative date of -1 month shows the wrong month

From: Bob Proulx <[email-masked]>
Date: Sun, 4 Apr 2021 21:30:21 -0600

Lars Nooden wrote:
> On March 29, 2021, if a relative date of '-1 month' is passed to 'date',
> then the output shows March instead of February.

The date manual includes this section on relative months.

       The fuzz in units can cause problems with relative items.  For
    example, ‘2003-07-31 -1 month’ might evaluate to 2003-07-01, because
    2003-06-31 is an invalid date.  To determine the previous month more
    reliably, you can ask for the month before the 15th of the current
    month.  For example:

         $ date -R
         Thu, 31 Jul 2003 13:02:39 -0700
         $ date --date='-1 month' +'Last month was %B?'
         Last month was July?
         $ date --date="$(date +%Y-%m-15) -1 month" +'Last month was %B!'
         Last month was June!

This exactly covers the initial bug report.  Because March 29, 2021
minus 1 month results in the invalid date February 29, 2021 which not
being a leap year does not exist.  What _should_ be the result if the
date one month ago does not exist?  And the answer to that will mostly
depend upon what purpose the question was being asked.

When dealing with time in months it also depends upon what you are
needing done.  If it is the 7th of the month and you want to generate
a date that is also the 7th but one month later or earlier then if it
is March 7th and generate February 7th then that will be fewer days
difference than if it is were June 7th and deciding May 7th is the
month early.  Due to the nature of having a different number of days
in different months.  But if that was what I wanted then I would
determine what was the month prior and generate a new datestamp using
the current day of the month.

[[Aside:
Off the top of my head and hopefully without a trivial bug.  I welcome
corrections if I made a mistake in this.  But this is still not
completely general purpose.

    $ date "+%F %T"
    2021-04-04 20:50:19

    $ date "+%Y-$(date --date="$(date +%Y-%m-15) -1 month" +%m)-%d %H:%M:%S"
    2021-03-04 20:50:54

*HOWEVER* that still does not handle the case of the original poster's
report about what happens on March 29, 2021 minus one month?  It can't
be February 29th!  Isn't that the same as March 1st?
]]

Perhaps instead of the code using 30 day months it should use the
number of days in the current month?  Then on March 31, 2021 -1 month
since March has 31 days that would calculate February 28, 2021.  Is
that better or worse?

    $ date --date="2021-03-31 12:00 +0000 -31 days" "+%F %T"
    2021-02-28 05:00:00

Potentially worse!  What happens on March 1, 2021 then?

    $ date --date="2021-03-01 12:00 +0000 -31 days" "+%F %T"
    2021-01-29 05:00:00

In that case we skip over February entirely!

Chris Elvidge wrote:
> Pádraig Brady wrote:
> > The current FAQ (linked below) suggests the workaround of:
> > 
> >    date --date="$(date +%Y-%m-15) -1 month" +'Last month was %B.'
> > 
> > https://www.gnu.org/software/coreutils/faq/coreutils-faq.html#The-date-command-is-not-working-right_002e
> 
> It's noticeable that (on my system, CYGWIN-NT and/or Raspbian) 'date -d"now
> -1month"' gives a definitely wrong answer, but 'dateadd now -1mo' gives a
> somewhat more reasonable answer. dateadd is from the dateutils package,
> sometimes dadd and/or dateutils.dadd.
> 
> $ date +"%Y-%m-%d %H:%M:%S"
> 2021-03-30 10:37:00
> 
> $ date -d"now -1 month" +"%Y-%m-%d %H:%M:%S"
> 2021-03-02 09:37:17

So...  Here is the problem with "now".  Using "now" is problematic
*some* of the time.  Not all of the time.  Never when you are trying
it on the command line in the middle of the day.  But there are
windows of time around DST time changes when it is problematic.  If
you are getting the time now and it is the middle of the day say
around noon then that is far away from time changes.  But almost every
seasonal time change there is a bug report from someone who has an
automated process that ran right at the same time as time change
giving them a surprising result and they are filing a bug that it gave
them the wrong answer, because there was no 2am that day, or maybe
there were two 2ams that day, or something.

That's why it is better to test for days using noon as a reference.
And why when checking for months it is better to test for months away
from the change of month.  Really the 10th or the 20th would be as
good as the 15th but the 15th is in the middle of every month and why
it ended up getting into the FAQ recommendation.

> $ dateadd now -1mo -f"%Y-%m-%d %H:%M:%S"
> 2021-02-28 09:37:27

I don't know anything about dateadd and it is not part of GNU Coreutils.

Bob