0

I want to use date to calculate some future "alarm times" for the realtime clock on my Raspberry Pi. These "alarm times" will be used to "wake" the RPi.

The scheme I decided to use is as follows:

  1. Immediately after booting, I get the current "baseline" time in my chosen format:

    $ BASETIME=$(date "+%D %X")
    $ echo $BASETIME
    05/03/21 14:03:43
    
  2. Before shutdown, I need to calculate at least one "wake-up alarm time" from the "baseline" time; e.g. 6 hours from BASETIME. As I understand it, the -d or --date= option is used for this purpose, but I am not getting the correct result:

    $ date '+%D %X' -d "${BASETIME} + 6 hours"
    05/03/21 04:03:43
    

    The result is a time in the past!? - which is obviously incorrect (at least not what I wanted :) A thorough read of man date sheds no light; it suggests:

    The date string format is more complex than is easily documented here...

    and

    Full documentation [...] available locally via: info '(coreutils) date invocation'

    But I found nothing useful here either.

    An alternative that comes close is this:

    $ date "+%D %X" -d '+ 6 hours'
    05/03/21 10:10:11
    

    I can get the correct alarm time in this fashion, but this is relative to the current time, instead of the BASETIME.

My questions are:

  • Why does date '+%D %X' -d "${BASETIME} + 6 hours" decrement time rather than increment time?

  • What is the correct way to increment from a fixed BASETIME?

NOTE: FWIW, my OS, kernel, and date versions:

$ date --version
date (GNU coreutils) 8.30
...

$ hostnamectl
...
Operating System: Raspbian GNU/Linux 10 (buster)
          Kernel: Linux 5.10.17-v7+
...
2

2 Answers 2

3

The core issue is that the date string created in BASETIME=$(date "+%D %X") doesn't include a Timezone. Using BASETIME=$(date "+%D %X %z") will solve your issue..

With date '+%D %X' -d "${basetime} + 6 hours", the + 6 (or +6 or +06, etc.) is interpreted as the timezone value.

You can see it with GNU date --debug option (bold mine):

$ date '+%D %X %z' -d "${basetime} + 6 hours" --debug
date: warning: value 5 has less than 4 digits. Assuming MM/DD/YY[YY]
date: parsed date part: (Y-M-D) 0021-05-03
date: **parsed time part: 14:03:43 UTC+06**
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (+06)
date: warning: adjusting year value 21 to 2021
date: using specified time as starting value: '14:03:43'
date: starting date/time: '(Y-M-D) 2021-05-03 14:03:43 TZ=+06'
date: '(Y-M-D) 2021-05-03 14:03:43 TZ=+06' = 1620029023 epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1620032623 epoch-seconds
date: timezone: system default
date: final: 1620032623.000000000 (epoch-seconds)
date: final: (Y-M-D) 2021-05-03 09:03:43 (UTC)
date: final: (Y-M-D) 2021-05-03 05:03:43 (UTC-04)
05/03/21 05:03:43 AM -0400

Additionally, the hour word is being interpreted as "add 1 hour".

All problems get solved if you use a TimeZone value for your time.
I recommend using %F %T %z as the format:

$ basetime=$(date "+%F %T %z"); echo "$basetime"
2021-05-03 18:31:43 -0400

$ date '+%F %T %z' -d "${basetime} + 6 hours"
2021-05-04 00:31:43 -0400

Which is indeed in the future. It still could be executed with the `--debug`` option.

A workaround to avoid the timezone (which I don't recommend) is to place the relative item (+6 hours) before the static time value:

$ date -d "+6 hours 2021-05-03 18:31:43"
Tue 04 May 2021 12:31:43 AM EDT
2
  • 1
    Hello - I'm reviewing your answer - just a couple of things now: (1) In the 2nd line of your answer, you used + 6 and +6. As i always used the former in my Question, I'm not sure I take your meaning here correctly - could you explain? (2) the --debug option! Brilliant - and if ever a utility needed a debug option, this is it :) More to follow soon.
    – Seamus
    Commented May 5, 2021 at 0:01
  • Based on my tests, I think your answer is the correct one. But I respectfully submit that your explanation could be improved. I've suggested a minor edit. After poring over this for a while now, it's my personal opinion that date needs a bit more work in parsing the strings for the -d option. As man date says, The date string format is more complex than is easily documented. Agreed. I found it curious that date actually knows the local TZ, but chooses to throw out an obvious conclusion: without a specified TZ in the date string, use the local TZ - in favor of + 6 is the TZ
    – Seamus
    Commented May 5, 2021 at 10:01
0

I have not found this documented, but the trick seems to be in the formatting order used in the assignment of a date string to BASETIME. In other words, reversing the order of %D and %X causes the calculation performed under the -d/--date= option to yield the correct (hoped for) result:

$ BASETIME=$(date "+%X %D")
$ echo $BASETIME
15:00:28 05/03/21

$ date '+%D %X' -d "${BASETIME} + 6 hours"
05/03/21 21:00:28 
# note that the output format need not be the same as the `BASETIME` format

$ date '+%D %X' -d "${BASETIME} + 24 hours"
05/04/21 15:00:28 
# note that the date advances correctly 

$ date '+%D %X' -d "${BASETIME} + 15 minutes"
05/03/21 15:15:28

$ date '+%D %X' -d "${BASETIME} + 6 months"
11/03/21 15:00:28

$ date '+%D %X' -d "${BASETIME} + 26 weeks"
11/01/21 15:00:28

Note:

I found this answer after spending time formulating the question. I'm posting it here in hope that it may help others.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .