Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

n days ago from a given date on command line

For example, given date is '2016-12-31', n is 2, expected output is '2016-12-29'.

I survey date command and get n days ago from current date is easy:
date -d "2 days ago" +%Y-%m-%d

like image 352
cheng Avatar asked Jan 18 '17 12:01

cheng


2 Answers

Just mention the date you want to extract two days from:

$ date -d "2016-12-31 2 days ago" +%Y-%m-%d
2016-12-29

Or a bit better grammatically-wise:

$ date -d "2016-12-31 -2 days" +%Y-%m-%d
2016-12-29
like image 143
fedorqui 'SO stop harming' Avatar answered Sep 22 '22 04:09

fedorqui 'SO stop harming'


If you do not have GNU date, perhaps you have BSD date instead. In that case, you can do this:

date -jf %s $(( $(date +%s) - 86400 * 2 ))

Note that this is not POSIX, nor is it GNU.

This tells BSD date not to change the clock (-j) and to take the input as UNIX time (seconds after 1970/01/01 00:00:00 UTC, -f %s). The time supplied is a mathematical substitution of the current time ($(date +%s), using the same format but as output) minus the number of seconds in a day (86400) times two.

I have, in the past, written code that looks for GNU and then fails over to BSD:

# Usage: _epoch2datefmt UNIX_TIME [+FORMAT]
if date -d @1484850180 +%s >/dev/null 2>&1
  then _epoch2datefmt() { date -d @"$@"; }    # GNU
  else _epoch2datefmt() { date -jf %s "$@"; } # BSD
fi

For this question, we also need to go the other direction. BSD date needs the format too:

# Usage: _date2epoch YYYY-MM-DD [INPUT_FORMAT]
if date -d 2017-01-19 +%s >/dev/null 2>&1
  then _yyyymmdd2epoch() { date -d "$1" +%s; }       # GNU
  else _yyyymmdd2epoch() { date -jf "$2" "$1" +%s; } # BSD
fi

Here's how to put those together to get two days before a given date with either GNU or BSD:

_epoch2datefmt $(( $(_date2epoch 2016-12-31 %Y-%m-%d) - 2 * 86400 )) +%Y-%m-%d

From the inside out, this converts 2016-12-31 to UNIX time, subtracts two days (there are 86400 seconds in a day), then passes the resulting UNIX time (the answer) to be converted back into YYYY-MM-DD format.


If you wanted this all in one function, it could be:

# Usage: _date_helper INPUT_FORMAT DATE [+OUTPUT_FORMAT]
if date -d@1484850180 +%s >/dev/null 2>&1
  then _date_helper() { local A=; [ "$1" = %s ] && A=@; shift; date -d "$A$@"; }
  else _date_helper() { date -jf "$@"; }
fi

(I had to be a little tricky to handle inputting the UNIX time (as indicated by the first argument being %s) since GNU's date -d requires a leading @. This saves that prefix in $A if necessary, purges the format (GNU is smart and doesn't otherwise need it), then passes the rest off to the GNU date command. The BSD version should be self-explanatory.)

Here's how to run it to get "two days before 2016-12-31" using this function:

_date_helper %s $(( $(_date_helper %Y-%m-%d 2016-12-31 +%s) - 86400 * 2)) +%Y-%m-%d
like image 25
Adam Katz Avatar answered Sep 22 '22 04:09

Adam Katz