I run rsync as a cron job and display the last backup via conky (i.e. "Last backup 2017 05 12 14:22:20"). I would like to display my last backup as "2 days ago" or "4 hours ago", rather than just displaying a static datestamp.

Is there any way to display a date/time using this sort of user-friendly output format? I've looked at man date, but cannot find anything about outputting a date in this sort of format. I see how to query a relative time or date with the -d flag, but cannot see how to get the output of date to include words like "yesterday", "3 days ago" etc.


  • Time marches on, though. Won't that (static) report have to change from "2 days ago" today to "3 days ago" tomorrow? – Jeff Schaller May 13 '17 at 1:23
  • if you're using gnu date, info date has more in depth documentation than man date – the_velour_fog May 13 '17 at 3:06
  • I don't know if it's the same on other systems but on mine (Mac OS X) man date is not very informative. man strftime will explain the different formats available. – I0_ol May 13 '17 at 3:59
  • 1
    If that's user-friend to begin with. If something happened "yesterday", when was it? Before the last midnight? Local time or UTC? At least 24 h ago? etc. SE shows times like "asked 10 hours ago", and I sometimes hope it would just tell the exact time, too. (without having to go looking at the little floaty box) – ilkkachu May 13 '17 at 11:28

using bash you can do something like the code below:
Note you will need to use sed or something to reformat your date string to something date will accept, like:

"2017-05-13 15:44:20"
#!/usr/bin/env bash

last_run="2017-05-13 15:44:20"

function relative() {
    local last_unix="$(date --date="$1" +%s)"    # convert date to unix timestamp
    local now_unix="$(date +'%s')"

    local delta=$(( $now_unix - $last_unix ))

    if (( $delta < 60 )) 
        echo "last run "$delta" seconds ago"
    elif ((delta < 2700))  # 45 * 60
        echo "last run "$(( $delta / 60 ))" minutes ago";

relative "$last_run"
|improve this answer|||||

Here's an extension of the_velour_fog's answer, adapted to use second/minute/day/month/year.

#!/usr/bin/env bash


function rel_fmt_low_precision() {
    local SEC_PER_MINUTE=$((60))
    local   SEC_PER_HOUR=$((60*60))
    local    SEC_PER_DAY=$((60*60*24))
    local  SEC_PER_MONTH=$((60*60*24*30))
    local   SEC_PER_YEAR=$((60*60*24*365))

    local last_unix="$(date --date="$1" +%s)"    # convert date to unix timestamp
    local now_unix="$(date +'%s')"

    local delta_s=$(( now_unix - last_unix ))

    if (( delta_s <  SEC_PER_MINUTE * 2))
        echo "last run "$((delta_s))" seconds ago"
    elif (( delta_s <  SEC_PER_HOUR * 2))
        echo "last run "$((delta_s / SEC_PER_MINUTE))" minutes ago"
    elif (( delta_s <  SEC_PER_DAY * 2))
        echo "last run "$((delta_s / SEC_PER_HOUR))" hours ago"
    elif (( delta_s <  SEC_PER_MONTH * 2))
        echo "last run "$((delta_s / SEC_PER_DAY))" days ago"
    elif (( delta_s <  SEC_PER_YEAR * 2))
        echo "last run "$((delta_s / SEC_PER_MONTH))" months ago"
        echo "last run "$((delta_s / SEC_PER_YEAR))" years ago"

rel_fmt_low_precision "`date`"
rel_fmt_low_precision "2018-06-21 21:10:18"
rel_fmt_low_precision "2018-06-21 20:10:18"
rel_fmt_low_precision "2018-05-21 21:10:18"
rel_fmt_low_precision "2017-06-21 20:10:18"
rel_fmt_low_precision "2016-06-21 20:10:18"

Here's how it decides which unit to use: it uses the largest unit that gives a number of at least two.

Example: If something happened 72 hours ago, it will output in days. If something happened exactly an hour ago, it will use minutes.

|improve this answer|||||

Hopefully someone has a better solution, but here is a script I came up with:

cd /tmp
git init -q
git -c user.email=0 -c user.name=0 commit -q -m 0 --allow-empty --date="$1"
git show --format=%ar


$ relative.sh 2016-1-23
2 years, 5 months ago

$ relative.sh 2016-9-23
1 year, 9 months ago
|improve this answer|||||
  • 4
    I just threw up in my mouth a little. But bravo. – Petro Jun 25 '18 at 21:36

Using awk:

hdate () {
  awk -v date="$(date +%s -d "$1")" -v now="$(date +%s)" '
    BEGIN {  diff = now - date;
       if (diff > (24*60*60)) printf "%.0f days ago", diff/(24*60*60);
       else if (diff > (60*60)) printf "%.0f hours ago", diff/(60*60);
       else if (diff > 60) printf "%.0f minutes ago", diff/60;
       else printf "%s seconds ago", diff;


~$ hdate '2018-06-17 14:22:20'
5 days ago

The input date must be in a format date can read.

|improve this answer|||||

Well in the Perl language (from the Perl Cookbook) you'd go:

use Date::Calc qw(Delta_DHMS);
@now = ( 1981, 6, 16, 4, 35, 25 ); # 16 June 1981 4:35:25
@then =( 1973, 1, 18, 3, 45, 50 ); # 18 January 1973 3:45:50

@diff = Delta_DHMS( @then, @now );

print "This is now, then was $diff[0] days, $diff[1]:$diff[2]:$diff[3] ago!\n";

And get:

This is now, then was 3071 days, 0:49:35 ago!

Unfortunately the Date::Calc module isn't installed by default so you'd have to either get someone to install it or learn to use the Perl module manager CPAN yourself.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.