28

Exactly as the question sounds. I want to subtract say 20120115 from 20120203 and get 19 as the answer. What is the best way to implement this in a shell script?

owagh
  • 3,428
  • 2
  • 31
  • 53
  • Have you considered another scripting language that supports dates inherently? (python, ruby, perl, ... in no particular order), Also you could use a script onliner in you bash source. – schoetbi Jan 25 '12 at 19:21
  • @schoetbi I did think of that but was not terribly confident of that availability everywhere. I know that basic bash and date is available in all points of interest but not about the rest. – owagh Jan 25 '12 at 21:44
  • 1
    @SteveRobillard Indeed it is a duplicate. I went through about 15 different questions all of which had solutions in perl, python, ruby etc but just couldn't find this one... any idea how to flag this question as a duplicate or just delete it? – owagh Jan 25 '12 at 21:46
  • @owagh answers to how to delete your question can be found here: http://meta.stackexchange.com/questions/5221/what-can-cause-a-post-to-be-deleted-and-what-does-that-actually-mean – Steve Robillard Jan 25 '12 at 21:50

3 Answers3

46
let DIFF=(`date +%s -d 20120203`-`date +%s -d 20120115`)/86400
echo $DIFF
Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • How come date recognized that string is in the format YYYYMMDD? Please direct me to the documentation. – shadyabhi Jan 25 '12 at 19:26
  • 1
    man 1 date says "The --date=STRING is a mostly free format human readable date string" – Eugen Rieck Jan 25 '12 at 19:28
  • What if the string is 03022012? Note that I have changed the format to DDMMYYYY. – shadyabhi Jan 25 '12 at 19:36
  • @shadyabhi Just try it out and let us know the result! I verified the values in the OQ and get 19 as a result. – Eugen Rieck Jan 25 '12 at 19:40
  • 2
    @EugenRieck It is a good solution, however not portable as `-d` is not a part of POSIX date. So as long as OP is not working on distributions like Solaris (as he has tagged it linux and not unix) he should be good. :) – jaypal singh Jan 25 '12 at 19:43
  • As the OQ has the `linux` tag and all linux distros I know, that carry bash, also carry a compatible version of date, I didn't sweat about this sort of portability – Eugen Rieck Jan 25 '12 at 19:45
  • I get 0 for that. and the man page for let says `EXIT STATUS ksh88 ksh88 returns the following exit values: 0 The value of the last expression is non-zero. 1 The value of the last expression is zero. ksh ksh returns the following exit values: 0 The last expr evaluates to a non-zero value. >0 The last expr evaluates to 0 or an error occurred. ` what if I want to get 19 as it is the difference between these two dates? – Asanke May 22 '17 at 00:40
  • 1
    We are not talking about the exit code (which SHOULD be 0), but about the value of `$DIFF` – Eugen Rieck May 22 '17 at 07:50
  • One liner version: echo $[(`date +%s -d 20120203`-`date +%s -d 20120115` )/86400] –  Sep 08 '21 at 13:58
  • you can always check validity of your date string by: $ date +%D -d "3 Jul 2012" –  Sep 08 '21 at 15:44
6

Proposing this solution which uses bc:

current="$(date +%s.%N)" #current date, precise to nanoseconds
old="$(date +%s.%N -d `sh some_script_that_gives_a_date.sh`)" #convert output to ns too
diff=$(echo "$current-$old" |bc)

date +%s.%N -d $1 takes an arbitrary date and converts it to a given format (as in this case +%s.%N, a float of seconds). Be aware that

-d is not a part of POSIX date. [But] as long as [you're] not working on distributions like Solaris ([OP] has tagged it linux and not unix) [you] should be good. :)

(comment by jaypal singh on this answer)


To convert it back to human-readable, you can use:
date $2 -d @0$diff #Pad diff with leading zero

Where $2 again is a date format see for example here

Cadoiz
  • 1,446
  • 21
  • 31
0

A quick and utterly dirty fix that can be used in scripts (quick and also fast):

This function gendates generates a list of valid dates between two dates in YYYYmmdd format (oldest first).

function gendates { for dd in $(seq -w $1 $2) ; do date -d $dd +%Y%m%d 2>/dev/null ; done  ; }                                                                             

I use this function for several purposes related to log files, like generating a list of logfile names to check [1]. And it comes handy to count the difference in days too:

echo "$(gendates YYYYmmdd YYYYmmdd)" | wc -l

YYYYmmdd have to be dates, of course. It only works if $1 is an earlier date than $2 and it's slow for large date differences, but for a period of a few years and to be used in ad-hoc scripting it's quite handy.

And if you happen to have MySQl or similar installed there is a very quick option :

mysql -BNe "SELECT DATEDIFF($1,$2) AS DiffDate ;" | tr -d -

The last tr allows you to enter the dates in any order (MySQL would else render a '-'if the first date is earlier than the second)

[1] The reason for generating a list of dates is that with that I can generate the names of the logfiles that are in this format: YYYYmmdd.log.gz. I could do that using an asterisk or $(ls), but this is way slower than just providing a list with strings.

runlevel0
  • 2,715
  • 2
  • 24
  • 31
  • echo "$(getdate YYYYmmdd YYYYmmdd)" gives me seq: invalid floating point argument: ‘YYYYmmdd’ Try 'seq --help' for more information. – Cadoiz May 21 '19 at 03:47
  • 1
    try to cut and paste the code into a shell. I used this function at a daily base and just checked for good measure (and because I was curious what you may have done) – runlevel0 Jul 02 '19 at 11:43
  • `GNU bash, version 4.2.53(1)-release (k1om-mpss-linux-gnu)` gives `BusyBox v1.27.0 (2017-09-27 13:20:28 EDT) multi-call binary. Usage: seq [-w] [-s SEP] [FIRST [INC]] LAST 1` – Cadoiz Jul 02 '19 at 14:24
  • with `GNU bash, version 4.3.42(1)-release (x86_64-suse-linux-gnu)` I get the first result. – Cadoiz Jul 02 '19 at 14:30
  • 1
    Might be an issue specific to BusyBox. I tried breaking it on purpose like adding / removing spaces and it either did not work or gave the correct result. Not sure if seq is one of the 300 commands it substitutes. – runlevel0 Jul 03 '19 at 14:23