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?
-
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 Answers
let DIFF=(`date +%s -d 20120203`-`date +%s -d 20120115`)/86400
echo $DIFF

- 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
-
1man 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
-
1We 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
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

- 1,446
- 21
- 31
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.

- 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
-
1try 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
-
1Might 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