16

How is this possible and how do I deal with it? I'm making backup script that is dependent on Unix date and have discovered an interesting bug:

[root@web000c zfs_test]# date +%y-%m-%d --date='2 months ago'
14-04-01
[root@web000c zfs_test]# date +%y-%m-%d --date='3 months ago'
14-02-28
[root@web000c zfs_test]# date
Sun Jun  1 00:08:50 CEST 2014
Jacob
  • 9,114
  • 4
  • 44
  • 56
Shirker
  • 579
  • 1
  • 5
  • 18

3 Answers3

44

You're seeing this behavior because of summer time (daylight saving time).

Because you are currently in summer time, where your clock is one hour ahead, when you ask for three months ago at just after midnight on the first of June, the time ends up being one hour "earlier" because it was not summer time three months ago.

The GNU date documentation suggests to work around this by using 12:00 noon and the 15th of the month as starting points, when asking for relative days or months, respectively. For example:

date +%y-%m-%d --date="$(date +%Y-%m-15) -3 month"
Michael Hampton
  • 237,123
  • 42
  • 477
  • 940
  • thank you. Yes, exactly "3 months ago" coming by 01 am: `[root@web000c zfs_test]# date +%y-%m-%d --date='3 months ago'` `14-03-01` `[root@web000c zfs_test]# date` `Sun Jun 1 01:00:15 CEST 2014` – Shirker Jun 12 '14 at 03:01
  • Doh! I think I need to go grep through some of my scripts as I suspect I've overlooked this suggestion on `date` usage. – Caleb Jun 13 '14 at 08:16
14

If absolute timing is your primary concern, it's probably best to work off of UTC as it exists for that purpose. Michael's answer is very useful for when you have to work inside of the problem, but it's usually a good idea to avoid it entirely where you can.

When your system isn't set to UTC by default, the simplest way to pass the timezone in is by prefixing your command with the TZ environment variable. This limits the zone switch to a single command and keeps the variable from leaking into your subsequent commands.

$ NOW=$(date '+%s')
$ date -d @$NOW
Wed Jun 11 23:44:35 EDT 2014
$ TZ=UTC date -d @$NOW
Thu Jun 12 03:44:35 UTC 2014

What you shouldn't do is export the TZ variable as this can make things very confusing to troubleshoot, as the following demonstrates.

$ export TZ=UTC
$ date -d @$NOW
Thu Jun 12 03:44:35 UTC 2014
$ TZ=EDT date -d @$NOW
Thu Jun 12 03:44:35 EDT 2014
Andrew B
  • 31,858
  • 12
  • 90
  • 128
-3

On this specific year that your computer thinks it being operated in, and on the specific date you chose for a test of "1 month ago, 2 months ago, and 3 months ago, yes - it is likely a February 29 detection. Not always an error, but ..

Now, today is NOT 2014-06-01. Try again. Set the computer date to 2013-06-01. Try again.
Set the computer date to 2014-09-01. Try again.

Jacob
  • 9,114
  • 4
  • 44
  • 56
  • 6
    If you are providing dates in American `MDY` format, plase use `/`es for separating. Even better, as we are an international community here, use proper ISO dates such as `2014-09-01`. – glglgl Jun 12 '14 at 13:38