Listing 2 datediff
#!/bin/ksh
# Listing 2: datediff
# prints the difference between two dates (output is in seconds)
# usage: datediff date1 time1 date2 time2
# date format: YYYY/mm/dd
# time format: hh:mm:ss
. /etc/setenv_path
TRUE=0
FALSE=1
# julian_date() returns the number days from 1 January 4713 BC.
# The algorithm is good from years 1801 to 2099.
# See: http://aa.usno.navy.mil/faq/docs/JD_Formula.html
# arguments: $1 = day, $2 = month, $3 = year in format YYYY
readonly JD_LOWER_LIMIT=1801
readonly JD_UPPER_LIMIT=2099
julian_date()
{
echo $(($1-32075+1461*($3+4800+($2-14)/12)/4+367* \
($2-2-($2-14)/12*12)/12-3*(($3+4900+($2-14)/12)/100)/4))
}
is_leap_year()
{
# a year is a leap year if it is even divisible by 4
# but not evenly divisible by 100
# unless it is evenly divisible by 400
[[ -n $1 ]] && year=$1 || ( echo "missing year" && exit 1 )
if (($year % 4 == 0)) && (($year % 100 != 0)) || (($year % 400 == 0))
then
return $TRUE
else
return $FALSE
fi
}
year_days()
{
[[ -n $1 ]] && year=$1 || ( echo "missing year" && exit 1 )
if is_leap_year $year
then
echo 366
else
echo 365
fi
}
month_days()
{
[[ -n $1 ]] && year=$1 || ( echo "missing year" && exit 1 )
[[ -n $2 ]] && month=$2 || ( echo "missing month" && exit 1 )
case $month in
1) echo 31 ;;
2)
if is_leap_year $year
then
echo 29
else
echo 28
fi
;;
3) echo 31 ;;
4) echo 30 ;;
5) echo 31 ;;
6) echo 30 ;;
7) echo 31 ;;
8) echo 31 ;;
9) echo 30 ;;
10) echo 31 ;;
11) echo 30 ;;
12) echo 31 ;;
esac
}
ordinal_day()
{
[[ -n $1 ]] && year=$1 || ( echo "missing year" && exit 1 )
[[ -n $2 ]] && month=$2 || ( echo "missing month" && exit 1 )
[[ -n $3 ]] && day=$2 || ( echo "missing day" && exit 1 )
sum=$day
while (( $month > 1 ))
do
month=$(($month - 1))
days_in_month=$(month_days $year $month)
sum=$(($sum + $days_in_month))
done
echo $sum
}
month_to_int()
{
[[ -n $1 ]] && month=$1 || ( echo "missing month" && exit 1 )
case $month in
1 | 01 | jan* | JAN* | Jan*) echo 1 ;;
2 | 02 | feb* | FEB* | Feb*) echo 2 ;;
3 | 03 | mar* | MAR* | Mar*) echo 3 ;;
4 | 04 | apr* | APR* | Apr*) echo 4 ;;
5 | 05 | may* | MAY* | May*) echo 5 ;;
6 | 06 | jun* | JUN* | Jun*) echo 6 ;;
7 | 07 | jul* | JUL* | Jul*) echo 7 ;;
8 | 08 | aug* | AUG* | Aug*) echo 8 ;;
9 | 09 | sep* | SEP* | Sep*) echo 9 ;;
10 | oct* | OCT* | Oct*) echo 10 ;;
11 | nov* | NOV* | Nov*) echo 11 ;;
12 | dec* | DEC* | Dec*) echo 12 ;;
*) echo 0 ;;
esac
}
# Verify that 4 parameters were passed to the program.
[[ -n $1 ]] && date1=$1 || ( echo "missing date1" && exit 1 )
[[ -n $2 ]] && time1=$2 || ( echo "missing time1" && exit 1 )
[[ -n $3 ]] && date2=$3 || ( echo "missing date2" && exit 1 )
[[ -n $4 ]] && time2=$4 || ( echo "missing time2" && exit 1 )
# Parse the date/time strings.
readonly DELIMITER_TIME=":"
readonly DELIMITER_DATE="/"
readonly DELIMITER_ORIGINAL=$IFS
IFS=$DELIMITER_DATE
echo "$date1" | read year1 month1 day1
echo "$date2" | read year2 month2 day2
IFS=$DELIMITER_TIME
echo "$time1" | read hr1 min1 sec1
echo "$time2" | read hr2 min2 sec2
IFS=$DELIMITER_ORIGINAL
# Convert the month to an integer.
month1=$(month_to_int $month1)
month2=$(month_to_int $month2)
if (( $JD_LOWER_LIMIT <= year1 )) && (( year1 <= $JD_UPPER_LIMIT )) \
&& (( $JD_LOWER_LIMIT <= year2 )) && (( year2 <= $JD_UPPER_LIMIT ))
then
jd1=$(julian_date $day1 $month1 $year1)
jd2=$(julian_date $day2 $month2 $year2)
seconds1=$(($jd1*24*3600 + $hr1*3600 + $min1*60 + $sec1))
seconds2=$(($jd2*24*3600 + $hr2*3600 + $min2*60 + $sec2))
echo $(( $seconds2 - $seconds1 ))
else
# Calculate the "ordinal second" for each date/time.
ordinal_day1=$(ordinal_day $year1 $month1 $day1)
ordinal_sec1=$(($ordinal_day1*24*3600 + $hr1*3600 + $min1*60 + $sec1))
ordinal_day2=$(ordinal_day $year2 $month2 $day2)
ordinal_sec2=$(($ordinal_day2*24*3600 + $hr2*3600 + $min2*60 + $sec2))
# Determine whether the difference should be positive or negative,
# and set the small-year and big-year variables appropriately.
if (( $year1 > $year2 ))
then
big_year=$year1
ordinal_day_by=$ordinal_day1
ordinal_sec_by=$ordinal_sec1
small_year=$year2
ordinal_day_sy=$ordinal_day2
ordinal_sec_sy=$ordinal_sec2
sign="-"
elif (( $year2 > $year1 ))
then
big_year=$year2
ordinal_day_by=$ordinal_day2
ordinal_sec_by=$ordinal_sec2
small_year=$year1
ordinal_day_sy=$ordinal_day1
ordinal_sec_sy=$ordinal_sec1
sign=""
else
# If the years are the same, just do the subtraction.
diff_secs=$(($ordinal_sec2 - $ordinal_sec1))
echo $diff_secs
exit 0
fi
# Calculate the seconds remaining in the small year.
year_days_sy=$(year_days $small_year)
yearsecs_sy=$(($year_days_sy * 24 * 3600))
remaining_secs_sy=$(($yearsecs_sy - $ordinal_sec_sy))
# Calculate seconds in the years between the big year and the small year.
sum_days=0
next_year=$(($small_year + 1))
while (( $next_year < $big_year ))
do
year_days_ny=$(year_days $next_year)
sum_days=$(($sum_days + $year_days_ny))
next_year=$(($next_year + 1))
done
sum_secs_ny=$(($sum_days * 24 * 3600))
# Add: the seconds left in the small year + the seconds in the years
# between the small and the bigs years + the seconds elapsed in the
# big year.
diff_secs=$(($remaining_secs_sy + $sum_secs_ny + $ordinal_sec_by))
# Print the result with the appropriate sign.
echo "$sign$diff_secs"
fi |