#7 Validating Date Formats

One of the most challenging validation tasks, but one that's crucial for shell scripts that work with dates, is to ensure that a specific date is actually possible. If we ignore leap years, this task is not too bad, because the calendar is well behaved and consistent each year. All we need in that case is a table with the days of each month against which to compare a specified date. To take leap years into account, you have to add some additional logic to the script. One set of rules for calculating a leap year is as follows:

  • Years not divisible by 4 are not leap years.

  • Years divisible by 4 and by 400 are leap years.

  • Years divisible by 4, not divisible by 400, and divisible by 100, are not leap years.

  • All other years divisible by 4 are leap years.

Notice how this script utilizes normdate (Script #3) to ensure a consistent date format before proceeding.

The Code

#!/bin/sh
# valid-date -- Validates a date, taking into account leap year rules.

exceedsDaysInMonth()
{
  # Given a month name, return 0 if the specified day value is
  # less than or equal to the max days in the month; 1 otherwise

  case $(echo $1|tr '[:upper:]' '[:lower:]') in
    jan* ) days=31    ;;  feb* ) days=28    ;;
    mar* ) days=31    ;;  apr* ) days=30    ;;
    may* ) days=31    ;;  jun* ) days=30    ;;
    jul* ) days=31    ;;  aug* ) days=31    ;;
    sep* ) days=30    ;;  oct* ) days=31    ;;
    nov* ) days=30    ;;  dec* ) days=31    ;;
    * ) echo "$0: Unknown month name $1" >&2; exit 1
   esac

   if [ $2 -lt 1 -o $2 -gt $days ] ; then
     return 1
   else
     return 0   # the day number is valid
   fi
}

isLeapYear()
{
  # This function returns 0 if a leap year; 1 otherwise.
  # The formula for checking whether a year is a leap year is:
  # 1. Years not divisible by 4 are not leap years.
  # 2. Years divisible by 4 and by 400 are leap years.
  # 3. Years divisible by 4, not divisible by 400, and divisible by 100,
  # are not leap years.
  # 4. All other years divisible by 4 are leap years.

  year=$1
  if [ "$((year % 4))" -ne 0 ] ; then
    return 1 # nope, not a leap year
  elif [ "$((year % 400))" -eq 0 ] ; then
    return 0 # yes, it's a leap year
  elif [ "$((year % 100))" -eq 0 ] ; then
    return 1
  else
    return 0
  fi
}

## Begin main script

if [ $# -ne 3 ] ; then
  echo "Usage: $0 month day year" >&2
  echo "Typical input formats are August 3 1962 and 8 3 2002" >&2
  exit 1
fi

# Normalize date and split back out returned values

newdate="$(normdate "$@")"

if [ $? -eq 1 ] ; then
  exit 1        # error condition already reported by normdate
fi

month="$(echo $newdate | cut -d  -f1)"
  day="$(echo $newdate | cut -d  -f2)"
 year="$(echo $newdate | cut -d  -f3)"

# Now that we have a normalized date, let's check to see if the
# day value is logical

if ! exceedsDaysInMonth $month "$2" ; then
  if [ "$month" = "Feb" -a "$2" -eq "29" ] ; then
    if ! isLeapYear $3 ; then
      echo "$0: $3 is not a leap year, so Feb doesn't have 29 days" >&2
      exit 1
    fi
  else
    echo "$0: bad day value: $month doesn't have $2 days" >&2
    exit 1
  fi
fi

echo "Valid date: $newdate"

exit 0

Running the Script

To run the script, simply specify a date on the command line, in "month day year" format. The month can be a three-letter abbreviation, a full word, or a numeric value; the year must be four digits.

The Results

$ valid-date august 3 1960
Valid date: Aug 3 1960
$ valid-date 9 31 2001
valid-date: bad day value: Sep doesn't have 31 days
$ valid-date feb 29 2004
Valid date: Feb 29 2004
$ valid-date feb 29 2006
valid-date: 2006 is not a leap year, so Feb doesn't have 29 days

Hacking the Script

A roughly similar approach to this script could validate time specifications, either using a 24-hour clock or with an ante meridiem/post meridiem (am/pm) suffix. Split the value at the colon, ensure that the minutes and seconds (if specified) are between 0 and 60, and then check that the first value is between 0 and 12 if allowing am/pm, or between 0 and 24 if you prefer a 24-hour clock. (Fortunately, while there are leap seconds and other tiny variations in time to help keep the calendar balanced, we can safely ignore them on a day-to-day basis.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.54.7