0

Formatting a time interval in seconds as minutes:seconds using MacOS ootb /bin/date works as expected using the "+%M:%S" format:

# convert 200 seconds to 03:20
date -j -f "%s" 200  "+%M:%S"
# OUTPUT: 03:20

# convert 3595 seconds to 59:55
date -j -f "%s" 3595  "+%M:%S"
# OUTPUT: 59:55

However when the parsed value is more than one hour (3600+ seconds) the format string "+%H:%M:%S" (and the equivalent "+%T") appears to have an off-by-one error:

date -j -f "%s" 3600  "+%H:%M:%S"
# ACTUAL OUTPUT: 02:00:00
# EXPECTED OUTPUT: 01:00:00

The manpage for date mentions that Parsing is done using strptime(3) which in turn points to strftime which says:

%s is replaced by the number of seconds since the Epoch, UTC (see mktime(3)).

According to the above I would expect 3600 to be parsed and formatted as 01:00:00 and NOT as 02:00:00.

Is there something wrong with the arguments I am passing or is this an implementation bug?

ccpizza
  • 28,968
  • 18
  • 162
  • 169
  • 2
    What is the output of `date -j -f "%s" 3595 "+%H:%M:%S %Z %z"`? What timezone are you in? What is your `locale`? Is there or was there daylight saving time in your region? – KamilCuk Apr 13 '21 at 15:54
  • @KamilCuk: the output is `01:59:55 CET +0100` which matches what Gordon explained in the accepted answer. The locale settings are `LC_ALL="en_US.UTF-8"` and `LANG="en_US.UTF-8"`. – ccpizza Apr 13 '21 at 19:39

1 Answers1

1

The basic problem is that date doesn't deal with time intervals, it deals with absolute time+dates. When you use date -j -f "%s" 200, it doesn't interpret the "200" as meaning an interval of 200 seconds, it means 200 seconds after midnight, January 1 1970, UTC. So if I run that on my Mac, I get this:

$ date -j -f "%s" 200
Wed Dec 31 16:03:20 PST 1969

...because I'm in the US Pacific time zone. I'm currently on Pacific Daylight time, but at 200 seconds after midnight, January 1 1970, UTC, this area would've been on Pacific Standard time, so it uses that to display the time & date.

You're seeing essentially the same thing, but since your area is one hour ahead of UTC (or was on January 1, 1970), you get an hour added instead of 8 subtracted. You don't notice this unless you display the hours, but it'll happen even if the time "interval" is less than 3600:

$ date -j -f "%s" 200 "+%H:%M:%S"
16:03:20

(In your time zone, you'll presumably see "01:03:20".) You can get even weirder results in a time zone with a non-hour offset:

$ TZ=Canada/Newfoundland date -j -f "%s" 200  "+%H:%M:%S"
20:33:20

You can mostly work around this by telling date to output in UTC, either with -u or TZ=UTC:

$ date -ju -f "%s" 3600  "+%H:%M:%S"
01:00:00
$ TZ=UTC date -j -f "%s" 3600  "+%H:%M:%S"
01:00:00

But at least in my opinion, this is still just a hack; the fundamental problem is that you're mixing up intervals with absolute times, and I'm not convinced this won't cause other problems as well.

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • Thanks for such a detailed and enlightening explanation! Indeed, I am using `date` to do more than it's meant for, and wasn't aware of the time zone adjustment. Much appreciated. – ccpizza Apr 13 '21 at 19:36
  • Btw, for the sake of completeness: the GNU `date` appears to always parse using the UTC time zone; the equivalent GNU date command `date -u -d@3600` returns `Thu Jan 1 01:00:00 UTC 1970` (can be reproduced on mac via `brew install coreutils`), so `gdate -u -d@3600 +"%H:%M:%S"` will output `01:00:00`. – ccpizza Apr 13 '21 at 19:51
  • 1
    "to __always__ parse using the UTC time zone", sure that's what `date -u` means. If you do not want UTC, do not use `-u`. – KamilCuk Apr 13 '21 at 19:52
  • What `date` is that `/bin/date` on a macOS? Obviously not part of the [GNU Coreutils](https://www.gnu.org/software/coreutils/), so where can I find its docs? (Addendum, I think `man /usr/share/man/man1/date.1` works.) – Jens Oct 04 '22 at 07:29
  • @Jens The best source is the built-in man pages in macOS (i.e. `man date`). Unfortunately, I think you need to install the developer command-line tools to get them; run the command and see what it says. – Gordon Davisson Oct 04 '22 at 07:45