(* Class: Holiday Package: com.waysysweb.util Author: W. Shaffer Date: 11-Nov-2006 Description: This structure handles holiday calculations. See http://en.wikipedia.org/wiki/Federal_holidays#List_of_Holidays See also http://www.opm.gov/fedhol/2006.asp See also http://aa.usno.navy.mil/faq/docs/easter.html Maintenance: Date Author Description ----------- ------ ----------------------------------------------------------- 11-Nov-2006 Shaffer File created *) structure HOLIDAY = struct (*============ Exceptions ==============================================*) (*============ Imports =============================================*) (* Date imports *) type WayDate = WAYDATE.WayDate; type Month = WAYDATE.Month; type Day = WAYDATE.Day; type Year = WAYDATE.Year; type DayOfYear = WAYDATE.DayOfYear; type DayOfWeek = WAYDATE.DayOfWeek; fun add(date1 : WayDate, value : int) : WayDate = WAYDATE.add(date1, value); fun numDayOfWeek(dayOfWeek : DayOfWeek) : int = WAYDATE.numDayOfWeek(dayOfWeek); fun dayOfWeek(date1 : WayDate) : DayOfWeek = WAYDATE.dayOfWeek(date1); fun daysInMonth(mnth : Month, yr : Year) : Day = WAYDATE.daysInMonth(mnth, yr); (* Date set *) type DateSet = DATERANGE.DateSet; fun max(set : DateSet, pred : WayDate -> bool) : WayDate = DATERANGE.max(set, pred); fun range(fromDate : WayDate, thruDate : WayDate) : DateSet = DATERANGE.range(fromDate, thruDate); (*============ Types =============================================*) (* Day Positions *) datatype DayPosition = First | Second | Third | Fourth | Last; (*============ Constants ==============================================*) (*============ Properties ==============================================*) (*============ Attributes =============================================*) (*============ Invariant =============================================*) (*============ Initialization ==========================================*) fun create(mnth : Month, dy : Day, yr : Year) : WayDate = WAYDATE.create(mnth, dy, yr); (*============ Operations =============================================*) (* ------------ Date positioning functions ----------------------------- *) (* Day of week on or before *) fun dayOfWeekOnOrBefore(date : WayDate, dyOfWk : DayOfWeek) : WayDate = let val datePriorWeek = add(date, ~6); val dateRange = range(datePriorWeek, date); fun pred(adate : WayDate) : bool = dayOfWeek(adate) = dyOfWk in max(dateRange, pred) end; (* Day of week on or after *) fun dayOfWeekAfter(date1 : WayDate, dyOfWk : DayOfWeek) : WayDate = dayOfWeekOnOrBefore(add(date1, 7), dyOfWk); (* Day of week after *) fun dayOfWeekBefore(date1 : WayDate, dyOfWk : DayOfWeek) : WayDate = dayOfWeekOnOrBefore(add(date1, ~1), dyOfWk); (* Nth Day from Date *) fun nthDayFromDate( First, dyOfWk : DayOfWeek, date1 : WayDate) : WayDate = add(dayOfWeekBefore(date1, dyOfWk), 7) | nthDayFromDate( Second, dyOfWk : DayOfWeek, date1 : WayDate) : WayDate = add(dayOfWeekBefore(date1, dyOfWk), 14) | nthDayFromDate( Third, dyOfWk : DayOfWeek, date1 : WayDate) : WayDate = add(dayOfWeekBefore(date1, dyOfWk), 21) | nthDayFromDate( Fourth, dyOfWk : DayOfWeek, date1 : WayDate) : WayDate = add(dayOfWeekBefore(date1, dyOfWk), 28) | nthDayFromDate( Last, dyOfWk : DayOfWeek, date1 : WayDate) : WayDate = add(dayOfWeekAfter (date1, dyOfWk), ~7); (* Date from position *) fun dateFromPosition( mnth : Month, Last, dow : DayOfWeek, yr : Year) = nthDayFromDate(Last, dow, create(mnth, daysInMonth(mnth, yr), yr)) | dateFromPosition( mnth : Month, dy : DayPosition, dow : DayOfWeek, yr : Year) = nthDayFromDate(dy, dow, create(mnth, 1, yr)); (* Observed holiday *) fun calcObservedHoliday(date : WayDate) : WayDate = case dayOfWeek(date) of WAYDATE.Saturday => WAYDATE.decrement(date) | WAYDATE.Sunday => WAYDATE.increment(date) | _ => date; (* ------------ Supporting routines for Easter calculation ------------- *) (* century - determine the century from a year *) fun century(yr : Year) : int = (yr div 100) + 1; fun shiftedEpact(yr : Year) : int = let val cent = century(yr) in (14 + (11 * (yr mod 19)) - ((3 * cent) div 4) + ((5 + 8 * cent) div 25)) mod 30 end; fun adjustedEpact(yr : Year) : int = let val shifted = shiftedEpact(yr) in if shifted = 0 orelse ( (shifted = 1) andalso (10 < (yr mod 19))) then shifted + 1 else shifted end; fun paschalMoon(yr : Year) : WayDate = let val paschal = adjustedEpact(yr) in add(create(4, 19, yr), ~ (paschal)) end; (* ------------ US Federal Holidays ------------------------------------ *) (* New Year's Day *) fun NewYearsDay(yr : Year) : WayDate = create(1, 1, yr); (* Birthday of Martin Luther King, Jr. *) fun MartinLutherKingsBirthday(yr : Year) : WayDate = dateFromPosition(1, Third, WAYDATE.Monday, yr); (* Washington's Birthday *) fun WashingtonsBirthday(yr : Year) : WayDate = dateFromPosition(2, Third, WAYDATE.Monday, yr); (* Memorial Day *) fun MemorialDay(yr : Year) : WayDate = dateFromPosition(5, Last, WAYDATE.Monday, yr); (* Independence Day *) fun IndependenceDay(yr : Year) : WayDate = create(7, 4, yr); (* Labor Day *) fun LaborDay(yr : Year) : WayDate = dateFromPosition(9,First, WAYDATE.Monday, yr); (* Columbus Day *) fun ColumbusDay(yr : Year) : WayDate = dateFromPosition(10, Second, WAYDATE.Monday, yr); (* Veteran's Day *) fun VeteransDay(yr : Year) : WayDate = create(11, 11, yr); (* Thanksgiving *) fun ThanksgivingDay(yr : Year) : WayDate = dateFromPosition(11, Fourth, WAYDATE.Thursday, yr); (* Christmas *) fun Christmas(yr : Year) : WayDate = create(12, 25, yr); (* ------------ Easter ------------------------------------------------- *) (* Easter *) fun Easter(yr : Year) : WayDate = dayOfWeekAfter(paschalMoon(yr), WAYDATE.Sunday); end (* HOLIDAY *)