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+
...
Seamus
  • 2,522
  • 1
  • 16
  • 31
  • Possibly related: [Why does the `-` (minus) interpretation of GNU date differs from the intuitive one, when a date is specified?](https://unix.stackexchange.com/questions/645758/why-does-the-minus-interpretation-of-gnu-date-differs-from-the-intuitive-o) – steeldriver May 03 '21 at 20:51
  • @steeldriver: Could be; I'll try to check this when I have more time. I do like the idea of the `--debug` option!! – Seamus May 03 '21 at 21:00
  • I look forward to seeing your findings ... I wonder if your solution is just an alternative way to stop the `+/- N` from being parsed as a timezone? – steeldriver May 03 '21 at 21:06

2 Answers2

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
Seamus
  • 2,522
  • 1
  • 16
  • 31
  • 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 May 05 '21 at 00:01
  • OK - I just wondered because you used it a 2nd time near the end of your answer, and I recalled that the 'space' between `+` and `6` is (at least sometimes) significant to the result. – Seamus May 05 '21 at 00:16
  • 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 May 05 '21 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.

Seamus
  • 2,522
  • 1
  • 16
  • 31