LDP/LDP/guide/docbook/abs-guide/days-between.sh

155 lines
3.8 KiB
Bash

#!/bin/bash
# days-between.sh: Number of days between two dates.
# Usage: ./days-between.sh [M]M/[D]D/YYYY [M]M/[D]D/YYYY
ARGS=2 # Two command line parameters expected.
E_PARAM_ERR=65 # Param error.
REFYR=1600 # Reference year.
CENTURY=100
DIY=365
ADJ_DIY=367 # Adjusted for leap year + fraction.
MIY=12
DIM=31
LEAPCYCLE=4
MAXRETVAL=256 # Largest permissable
# positive return value from a function.
diff= # Declare global variable for date difference.
value= # Declare global variable for absolute value.
day= # Declare globals for day, month, year.
month=
year=
Param_Error () # Command line parameters wrong.
{
echo "Usage: `basename $0` [M]M/[D]D/YYYY [M]M/[D]D/YYYY"
echo " (date must be after 1/3/1600)"
exit $E_PARAM_ERR
}
Parse_Date () # Parse date from command line params.
{
month=${1%%/**}
dm=${1%/**} # Day and month.
day=${dm#*/}
let "year = `basename $1`" # Not a filename, but works just the same.
}
check_date () # Checks for invalid date(s) passed.
{
[ "$day" -gt "$DIM" ] || [ "$month" -gt "$MIY" ] || [ "$year" -lt "$REFYR" ] && Param_Error
# Exit script on bad value(s).
# Uses "or-list / and-list".
#
# Exercise: Implement more rigorous date checking.
}
strip_leading_zero () # Better to strip possible leading zero(s)
{ # from day and/or month
val=${1#0} # since otherwise Bash will interpret them
return $val # as octal values (POSIX.2, sect 2.9.2.1).
}
day_index () # Gauss' Formula:
{ # Days from Jan. 3, 1600 to date passed as param.
day=$1
month=$2
year=$3
let "month = $month - 2"
if [ "$month" -le 0 ]
then
let "month += 12"
let "year -= 1"
fi
let "year -= $REFYR"
let "indexyr = $year / $CENTURY"
let "Days = $DIY*$year + $year/$LEAPCYCLE - $indexyr + $indexyr/$LEAPCYCLE + $ADJ_DIY*$month/$MIY + $day - $DIM"
# For an in-depth explanation of this algorithm, see
# http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm
if [ "$Days" -gt "$MAXRETVAL" ] # If greater than 256,
then # then change to negative value
let "dindex = 0 - $Days" # which can be returned from function.
else let "dindex = $Days"
fi
return $dindex
}
calculate_difference () # Difference between to day indices.
{
let "diff = $1 - $2" # Global variable.
}
abs () # Absolute value
{ # Uses global "value" variable.
if [ "$1" -lt 0 ] # If negative
then # then
let "value = 0 - $1" # change sign,
else # else
let "value = $1" # leave it alone.
fi
}
if [ $# -ne "$ARGS" ] # Require two command line params.
then
Param_Error
fi
Parse_Date $1
check_date $day $month $year # See if valid date.
strip_leading_zero $day # Remove any leading zeroes
day=$? # on day and/or month.
strip_leading_zero $month
month=$?
day_index $day $month $year
date1=$?
abs $date1 # Make sure it's positive
date1=$value # by getting absolute value.
Parse_Date $2
check_date $day $month $year
strip_leading_zero $day
day=$?
strip_leading_zero $month
month=$?
day_index $day $month $year
date2=$?
abs $date2 # Make sure it's positive.
date2=$value
calculate_difference $date1 $date2
abs $diff # Make sure it's positive.
diff=$value
echo $diff
exit 0
# Compare this script with the implementation of Gauss' Formula in C at
# http://buschencrew.hypermart.net/software/datedif