Bash Date Time Calculations How To

Although bash is a powerful scripting environment it is a bit deficient, compare to other scripting languages, when it comes to date/time calculations. There are no convenient, generally available, date functions such as datediff or overloaded operators for mathematically manipulating dates and time. The general approach to date time calculations in Bash, is to convert the two dates you want to perform some calculations on to Unix Time, which is the number of seconds from 1 Jan 1970, and the then one can do the maths on the integer results using standard Bash functionality.

Date Command Can Convert Human Readable Date to Unix Epoch Time

To convert a date from human readable format, to Unix Epoch time, one can use the 'date' command with the '%s' format specifier. For example to get the current date in Unix time  use the following

'date +%s'

(Note the + in front of the %s). To convert a specific date, rather than the current date/time to Unix Epoch time, you can use the date command with the "-d" option to set the date you want converted e.g.

'date -d '1 Jan 2010' +%s'.

This gives you a specific date converted to Unix Epoch time.

Use Bash or bc to Do the Maths 

Once you have the date in the correct format you can then do simple maths on the results. To do the maths you need to use the Bash maths expression e.g. to get the different between two dates:

$(( `date -d '2 Jan 2009' +%s`-`date -d '1 Jan 2009' +%s`)) 

Here we are using the bash mathematical expression syntax $((...)) to do the calculations. The maths expression only works with integers, or at least just drops the fractional part of any calculations, but since we are working with integers, and it makes no sense to divide two dates, this should be ok. When converting from seconds, to hours or days, this may be a problem and then we will ue the 'bc' programme.

Also be aware of integer overflow if your dates are widely different if so just use the 'bc' programme. Note, bc expects to read its input from a file or standard in so you need to use  the pipe (|) operator to redirect input. e.g.

"scale=10; `date -d '2 Jan 2009' +%s`-`date -d '1 Jan 2009' +%s` | bc " 

The scale option is needed to specify the number of decimals you want in the result. Armed with the above one can easily subtract two dates or add minutes, hours, days to a particular date. To convert the seconds back to days, hours or minutes simply divide the result as follows (assuming the result of the calculation is stored in the $result variable):

"$result/60 | bc" = minutes (60 seconds in a minute)
"$reuslt/3600 | bc" = hours (60 seconds in a minute and 60 minutes in an hour)
"$result/ 86400 | bc" = days (24 hours in a day)

Convert the Result Back To Human Readable Format

If you want to get the result back to a human readable date, or need to extract the month, day, year or time portion then you need to use awk. eg

awk 'BEGIN {print strftime("%Y-%m-%d",$result)}'

This will output the date as Year-Month-Day. One can also use awk to get the day, month or year part of the result.

Bash Date/Time Manipulation

So here is a brief script tying it all together.:

#!/bin/sh
#number of seconds in a minute
sinm=60
#number of seconds in an hour
sinh=3600
#number of seconds in a day
sind=86400

#Calculate the date diff in seconds
startDate=`date -d "11 Jun 2010" +%s`
currentDate=`date +%s`
datediff=$(($startDate-$currentDate))

minutes=$(($datediff/$sinm))
echo "number of minutes to kick off $minutes"
hours=$(($datediff/$sinh))
echo "number of hours to kick off $hours"
days=$(($datediff/$sind))
echo "number of days to kick off $days"

#calulate end date and show in human readable format
endDate=$(($startDate+($sind*60)))
strEndDate=`awk 'BEGIN {print strftime("%Y-%m-%d",$endDate)}'`
echo "End date is $strEndDate"