Gnu date command and arithmetic.

Bob Proulx bob at
Tue Jan 11 22:03:02 MST 2022

Brian Sturgill wrote:
> Besides having syntax that makes AppleScript look like a well
> thought out language, the gnu date command doesn't give a correct
> answer in edge cases...
> date --date="$(date +%Y-12-31) -1 month"
> Thu Dec  1 12:00:00 AM MST 2022

Relative sates with GNU date is tricky.  I think if one wants to use
it then it is required reading of the FAQ for it.  Which will just
make your point that date manipulations using GNU date are tricky.

Use the --debug option with that to see what it is really doing.

    $ date --debug --date="$(date +%Y-12-31) -1 month"
    date: parsed date part: (Y-M-D) 2022-12-31
    date: parsed relative part: -1 month(s)
    date: input timezone: system default
    date: warning: using midnight as starting time: 00:00:00
    date: starting date/time: '(Y-M-D) 2022-12-31 00:00:00'
    date: warning: when adding relative months/years, it is recommended to specify the 15th of the months
    date: after date adjustment (+0 years, -1 months, +0 days),
    date:     new date/time = '(Y-M-D) 2022-12-01 00:00:00'
    date: warning: month/year adjustment resulted in shifted dates:
    date:      adjusted Y M D: 2022 11 31
    date:    normalized Y M D: 2022 12 01
    date: '(Y-M-D) 2022-12-01 00:00:00' = 1669878000 epoch-seconds
    date: timezone: system default
    date: final: 1669878000.000000000 (epoch-seconds)
    date: final: (Y-M-D) 2022-12-01 07:00:00 (UTC)
    date: final: (Y-M-D) 2022-12-01 00:00:00 (UTC-07)
    Thu Dec  1 12:00:00 AM MST 2022

So it used midnight as the reference time.  That's often problematic.
I always suggest using noon instead.  The hint also recommends doing
month calculations in the middle of the month for the same reason.

Here it started at midnight on the 31st, subtracted a month, and
arrived at November 31st.  The "2022 11 31" in the debug above.
That's an invalid date.  So it adjusted to December 1st, which is
arguably the best adjustment for November 31st.

The date manual talks about this case specifically.

       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!

The FAQ for date covers this and many more gotchas in much more detail.

Which I know reinforces your point that using bare shell for things
like date manipulation is tricky stuff.


More information about the NCLUG mailing list