With dateutils's datediff
(not GNU sorry), (formerly ddiff
, dateutils.ddiff
on Debian):
$ dateutils.ddiff -f '%Y years, %m months, %d days, %H:%0M:%0S' \
'2012-01-23 15:23:01' '2017-06-01 09:24:00'
5 years, 4 months, 8 days, 18:00:59
(dates taken as UTC, add something like --from-zone=Europe/London
for the dates to be taken as local time in the corresponding time zone. --from-zone=localtime
may work for the default timezone in the system; --from-zone="${TZ#:}"
would work as long as $TZ
specifies the path of a IANA timezone file (like TZ=:Europe/London
, not TZ=GMT0BST,M3.5.0/1:00:00,M10.5.0/2:00:00
POSIX-style TZ specification)).
With ast-open date
, so still not with GNU tools sorry (if using ksh93
as your shell, that date
may be available as a builtin though) you can use date -E
to get the difference between two dates in a format that gives 2 numbers with units:
$ date -E 1970-01-01 2017-06-01
47Y04M
$ date -E 2017-01-01 2017-06-01
4M29d
$ date -E 12:12:01 23:01:43
10h49m
Note that anything above hour is ambiguous, as days have a varying number of hours in locales with DST (23, 24 or 25) and months and years have a varying number of days, so it may not make much sense to have more precision than those two units above.
For instance, there's exactly one year in between 2015-01-01 00:00:00 and 2016-01-01 00:00:00 or between 2016-01-01 00:00:00 and 2017-01-01 00:00:00, but that's not the same duration (365*24 hours in one case, 366*24 hours in the other)
There's exactly one day between 2017-03-24 12:00:00 and 2017-03-25 12:00:00 or between 2017-03-25 12:00:00 and 2017-03-26 12:00:00, but when those are local time in European countries (that switched to summer time on 2017-03-26), that one day is 24 hours in one case and 23 hours in the other.
In other words a duration that mentions years, months, weeks or days is only meaningful when associated to one of the boundaries it's meant to be applied to (and the timezone). So you cannot convert a number of seconds to such a duration without knowing what time (from or until) it's meant to be applied to (unless you want to use approximate definitions of day (24 hour), month (30*24 hours) or year (365*24 hours)).
To some extent, even a duration in seconds is ambiguous. For simplification, the seconds in the Unix epoch time are defined as the 86400th part of a given Earth day1. Those seconds are getting longer and longer as Earth spins down.
So something like (with GNU date
):
d1='2016-01-01 00:00:00' d2='2017-01-01 00:00:00'
eval "$(date -d "@$(($(date -d "$d2" +%s) - $(date -d "$d1" +%s)))" +'
delta="$((%-Y - 1970)) years, $((%-m - 1)) months, $((%-d - 1)) days, %T"')"
echo "$delta"
Or in fish
:
date -d@(expr (date -d $d2 +%s) - (date -d $d1 +%s)) +'%Y %-m %-d %T' | \
awk '{printf "%s years, %s months, %s days, %s\n", $1-1970, $2-1, $3-1, $4, $5}'
Would generally not give you something that is correct as the time delta is applied to 1970 instead of the actual start date 2016.
For instance, above, that gives me (in a Europe/London timezone):
1 years, 0 months, 1 days, 01:00:00
instead of
1 years, 0 months, 0 days, 00:00:00
(that may still be a good enough approximation for you).
1 Technically, while a day is 86400 Unix seconds long, network-connected systems typically synchronise their clock to atomic clocks using SI seconds. When an atomic clock says 12:00:00.00, those Unix systems also say 12:00:00.00. The exception is only upon the introduction of leap seconds, where either there's one Unix second that lasts 2 seconds or a few seconds that last a bit longer to smear that extra second upon a longer period.
So, it is possible to know the exact duration (in terms of atomic seconds) in between two Unix time stamps (issued by systems synchronised to atomic clock): get the difference of Unix epoch time between the two timestamps and add the number of leap seconds that have been added in between.
datediff
also has a -f %rS
to get the number of real seconds in between two dates:
$ dateutils.ddiff -f %S '1990-01-01 00:00:00' '2010-01-01 00:00:00'
631152000
$ dateutils.ddiff -f %rS '1990-01-01 00:00:00' '2010-01-01 00:00:00'
631152009
The difference is for the 9 leap seconds that have been added in between 1990 and 2010.
Now, that number of real seconds is not going to help you to calculate the number of days as days don't have a constant number of real seconds. There are exactly 20 years between those 2 dates and those 9 leaps seconds rather get in the way to get to that value. Also note that ddiff
's %rS
only works when not combined with other format specifiers. Also, future leap seconds are not known long in advance, but also information about recent leap seconds may not be available. For instance, my system doesn't know about the ones after 2012-07-01.
echo $(($(date +%s -d "3 days ago")-$(date +%s -d "4 weeks 2 hours 1 minutes 33 seconds ago"))) | sed 's/-//'