11

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.

Thanks!

4
  • 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, 2017 at 1:23
  • if you're using gnu date, info date has more in depth documentation than man date May 13, 2017 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, 2017 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, 2017 at 11:28

5 Answers 5

8

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

#!/bin/sh
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

Result:

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

$ relative.sh 2016-9-23
1 year, 9 months ago
1
  • 7
    I just threw up in my mouth a little. But bravo.
    – Petro
    Jun 25, 2018 at 21:36
6
+25

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

#!/usr/bin/env bash

last_run="2018-06-21T21:10:18-06:00"


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))
    then
        echo "last run "$((delta_s))" seconds ago"
        return 
    elif (( delta_s <  SEC_PER_HOUR * 2))
    then
        echo "last run "$((delta_s / SEC_PER_MINUTE))" minutes ago"
        return 
    elif (( delta_s <  SEC_PER_DAY * 2))
    then
        echo "last run "$((delta_s / SEC_PER_HOUR))" hours ago"
        return 
    elif (( delta_s <  SEC_PER_MONTH * 2))
    then
        echo "last run "$((delta_s / SEC_PER_DAY))" days ago"
        return 
    elif (( delta_s <  SEC_PER_YEAR * 2))
    then
        echo "last run "$((delta_s / SEC_PER_MONTH))" months ago"
        return 
    else
        echo "last run "$((delta_s / SEC_PER_YEAR))" years ago"
        return 
    fi
}

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.

3

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 )) 
    then
        echo "last run "$delta" seconds ago"
        return 
    elif ((delta < 2700))  # 45 * 60
    then
        echo "last run "$(( $delta / 60 ))" minutes ago";
    fi
}

relative "$last_run"
1

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;
    }'
}

Example:

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

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

0

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.

You must log in to answer this question.

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