Subject: Re: bug#13071: Date and month ago

From: Bob Proulx <[email-masked]>
Date: Mon, 3 Dec 2012 16:35:41 -0700

tag 13071 + notabug moreinfo
close 13071
thanks

Tuc at T-B-O-H wrote:
> Hi,

Hi! :-)

> Running into a problem with date and month...

Since you aren't really submitting a bug report but just asking
questions I am going to go ahead here and mark the accounting of the
bug as closed.  If a bug report arrises out of this feel free to open
it again.

Also, in the future, if you have general discussion please bring that
up on the [email-masked] mailing list instead of the bug tracker.
We don't need to track discussion like a bug.  Thanks.

> function example {

As a random side note I always suggest the "example() {" syntax.

>   rptdate=$(date +%Y-%m-%d -d "$1 -6 months")

Just as a shortcut the GNU extension %F is the same as %Y-%m-%d.  Same
thing.  No difference.  But easier to remember and saves some typing.

>   echo "6 months ago from $1 is ${rptdate}"
> 
> example 2012-09-30
> example 2012-10-31
> example 2012-10-30
> example 2012-11-30
> example 2012-12-01
> example 2012-03-31

Ew!  You didn't specify the time and/or didn't use UTC.  So the
default will be midnight in the local time.  Depending upon the
timezone that is going to hit DST changes and trip over "illegal"
times.  (One of the few times where it isn't a "legal" time, as
opposed to valid because timezones is by act of congress!)

> 6 months ago from 2012-09-30 is 2012-03-30
> ****6 months ago from 2012-10-31 is 2012-05-01***
> 6 months ago from 2012-10-30 is 2012-04-30
> 6 months ago from 2012-11-30 is 2012-05-30
> 6 months ago from 2012-12-01 is 2012-06-01
> *****6 months ago from 2012-03-31 is 2011-10-01*

Right.

> It seems that its running into the issue that if the 6 months ago doesn't
> have the same amount of days, its falling into the next month. Is there a
> way to tell it to instead just use the last day of the month?

Yes.  See the documentation about relative dates.  It says:

  28.7 Relative items in date strings

     The unit of time displacement may be selected by the string `year'
  or `month' for moving by whole years or months.  These are fuzzy units,
  as years and months are not all of equal duration.  More precise units
  are `fortnight' which is worth 14 days, `week' worth 7 days, `day'
  worth 24 hours, `hour' worth 60 minutes, `minute' or `min' worth 60
  seconds, and `second' or `sec' worth one second.  An `s' suffix on
  these units is accepted and ignored.

     The unit of time may be preceded by a multiplier, given as an
  optionally signed number.  Unsigned numbers are taken as positively
  signed.  No number at all implies 1 for a multiplier.  Following a
  relative item by the string `ago' is equivalent to preceding the unit
  by a multiplier with value -1.

     The string `tomorrow' is worth one day in the future (equivalent to
  `day'), the string `yesterday' is worth one day in the past (equivalent
  to `day ago').

     The strings `now' or `today' are relative items corresponding to
  zero-valued time displacement, these strings come from the fact a
  zero-valued time displacement represents the current time when not
  otherwise changed by previous items.  They may be used to stress other
  items, like in `12:00 today'.  The string `this' also has the meaning
  of a zero-valued time displacement, but is preferred in date strings
  like `this thursday'.

     When a relative item causes the resulting date to cross a boundary
  where the clocks were adjusted, typically for daylight saving time, the
  resulting date and time are adjusted accordingly.

     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!

     Also, take care when manipulating dates around clock changes such as
  daylight saving leaps.  In a few cases these have added or subtracted
  as much as 24 hours from the clock, so it is often wise to adopt
  universal time by setting the `TZ' environment variable to `UTC0'
  before embarking on calendrical calculations.

And also see the FAQ entry which also discusses this topic in some detail:

  http://www.gnu.org/software/coreutils/faq/#The-date-command-is-not-working-right_002e

I am sure those docs answer your questions.  But just the same we
would appreciate a note from you saying that it did.  Or suggesting
improvements!  :-)

Bob