12

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!

agc
  • 7,223
Aijalon
  • 121
  • 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 01:23
  • if you're using gnu date, info date has more in depth documentation than man date – the_velour_fog May 13 '17 at 03: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 03: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

5 Answers5

9

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
Zombo
  • 1
  • 5
  • 44
  • 63
6

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.

muru
  • 72,889
Nick ODell
  • 2,608
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"
muru
  • 72,889
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.

pLumo
  • 22,565
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.

muru
  • 72,889
Nadreck
  • 402