//------------------------------------------------------------------------------ // Compilation Unit Header //------------------------------------------------------------------------------ // // Copyright (c) 2006 Waysys, Inc. All Rights Reserved. // // Permission to use, copy, modify, and distribute this software // and its documentation for NON-COMMERCIAL purposes and without // fee is hereby granted provided that this copyright notice // appears in all copies. // // Waysys MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF // THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE, OR NON-INFRINGEMENT. Waysys SHALL NOT BE LIABLE FOR // ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR // DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. // // For further information, contact Waysys Inc. at wshaffer@waysysweb.com // or 800-622-5315 (USA). // //------------------------------------------------------------------------------ // Maintenance History //------------------------------------------------------------------------------ // // Person Date Change // ------ ----------- ---------------------------------------------------- // // Shaffer 17-Jun-2000 File create // Shaffer 23-Jun-2000 Added override to Object methods // Shaffer 24-Jun-2000 Added constructor to return today's date // Shaffer 07-Nov-2006 Adapted to Eclipse environment // Shaffer 07-Jan-2007 Added assert statements // //------------------------------------------------------------------------------ // Package Declaration //------------------------------------------------------------------------------ package com.waysysweb.util; //------------------------------------------------------------------------------ //Import Declarations //------------------------------------------------------------------------------ import java.io.Serializable; import java.util.Calendar; import java.util.Date; //------------------------------------------------------------------------------ //Public Class Declaration //------------------------------------------------------------------------------ /** * This class performs various manipulations on dates. The class addresses * the problem with date handling: it has two different types of requirements that * dictate two different types of implementations. Display activities such * as returning the particular day of the date or displaying a string * representation of the date favor an implementation in which month, day, and * year are separate instance fields. Calculations activities like subtracting * one date from another favor a representation in which the date is stored as * the number of days from an epoch. * * This class is structured according to the Bridge pattern in [GAMM95]. * This class wraps an abstract implementation class. * The abstract implementation class has two subclasses. * One subclass is optimized for computation. The * other class is optimized for display purposes. * * This class has two significant responsibilities. First, it validates any inputs, * thereby saving considerable validation code in the implementation classes. * The implementation class simply assume they are getting valid inputs. Second, * this class handles the switching between the display implementation class and * the calculation implementation class. * * WayDate is designed to handle manipulations of dates between the designated Epoc * of 1-Jan-1601 and 31-Dec-3999. It uses 1,...,31 to represent the days of the month. * It uses 1,...,12 to represent the months of the year. It uses 1601,...,3999 to * represent years (century designations are always included in WayDate operations * involving years). * * @author William A. Shaffer * @version 3.00 07-Jan-2007 */ public class WayDate implements Serializable, Comparable, Cloneable, DateCalculation, DateDisplay { /** * The serial verion unique identifier for this class */ private static final long serialVersionUID = 9700L; /** * MAXYEAR is the largest year handled by WayDate. */ public static final int MAXYEAR = 3999; /** * MINYEAR is the minimum year handled by WayDate. Since the Gregorian * calendar did not begin until 1582, there seems little point in using * this class for dates prior to 1582. 1601 is a convenient year for the * epoch. */ public static final int MINYEAR = 1601; /* * Constants for days of week. */ /** * Constant representing Sunday */ public static final int SUNDAY = 0; /** * Constant representing Monday */ public static final int MONDAY = 1; /** * Constant representing Tuesday */ public static final int TUESDAY = 2; /** * Constant representing Wednesday */ public static final int WEDNESDAY = 3; /** * Constant representing Thursday */ public static final int THURSDAY = 4; /** * Constant representing Friday */ public static final int FRIDAY = 5; /** * Constant representing Saturday */ public static final int SATURDAY = 6; // // Fields // /** * The field 'date' holds an instance of WayDateCalc or WayDateDisplay. * These subclasses will do the actual work. */ private WayDateImpl date = null; /** Names of days of the week */ private static final String[] namesOfDays = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; /** The maximum allowed date */ public static final WayDate MaxDate = new WayDate(12, 31, MAXYEAR); /** The minimum allowed date */ public static final WayDate MinDate = new WayDate(01, 01, MINYEAR); //------------------------------------------------------------------------------ // Constructors //------------------------------------------------------------------------------ /** * Create a WayDate object with the specified * month, day, and year. * *

Example:

*
     *    WayDate aDate = new WayDate(12, 22, 1998);
     * 
* * @param month month of the date (month inset range(1, 12)) * @param day day of the date (day inset range(1, 31)) * @param year year of the date (year inset range(MINYEAR, MAXYEAR)) * */ public WayDate(int month, int day, int year) { assert checkValidDate(month, day, year): "Invalid date"; date = new WayDateDisplay(month, day, year); return; } /** * Return an instance of WayDate the day of the year of which * corresponds to the input to this constructor. This constructor and the * accessors getDayOfYear() and getYear() provide the capability to work * with day of year (or Julian date) calculations. * *

Example:

*
     *    WayDate date = new WayDate(dayYear, yr);
     * 
* @param dayYear day of the year for the date (dayYear inset range(1, daysInYear(year))) * @param year year of the date (year inset range(MINYEAR, MAXYEAR)) */ public WayDate(int dayYear, int year) { assert isValidYear(year) : "Illegal year: " + year; assert isValidDayOfYear(dayYear, year) : "Illegal day of year " + dayYear; date = new WayDateDisplay(dayYear, year); } //------------------------------------------------------------------------------ // Static Methods //------------------------------------------------------------------------------ /** * A factory method to create an instance of WayDate with today's date. * *

Example:

*
     *    WayDate date = WayDate.today();
     * 
* @return an instance of WayDate with the current date. */ public static WayDate today() { WayDateDisplay dt = (WayDateDisplay) WayDateImpl.today(); return new WayDate(dt.getMonth(), dt.getDay(), dt.getYear()); } /** * Return an instance of WayDate corresponding to a Date object. * * @param date an instance of the Java Date class * @return an instance of WayDate corresponding to to the input */ public static WayDate asWayDate(Date date) { WayDateDisplay dt = (WayDateDisplay) WayDateImpl.asWayDate(date); return new WayDate(dt.getMonth(), dt.getDay(), dt.getYear()); } /** * Check if a date is valid. * *

Example:

*
     *    if (WayDate.checkValidDate(12, 22, 1945))
     *    {
     *      ...
     *    }
     * 
* * @param month the month part of the date (mnth inset range(1, 12)) * @param day the day part of the date (dy inset range(1, daysInMonth(mnth, yr))) * @param year the year part of the date (yr inset range(MINYEAR, MAXYEAR)) * @return true if the date inputs are valid */ public static boolean checkValidDate(int month, int day, int year) { boolean result; if (!isValidYear(year)) { result = false; } else if (!isValidMonth(month)) { result = false; } else if (!isValidDay(month, day, year)) { result = false; } else result = true; return result; } /** * Return true if the year is valid. * *

Example:

*
     * if (isValidYear(year)
     *     {
     *     . . .
     *     }
     * 
* * @return year inset range(MINYEAR, MAXYEAR) * @param year the year of a date */ static public boolean isValidYear(int year) { boolean result; if ((year >= MINYEAR) && (year <= MAXYEAR)) result = true; else result = false; return result; } /** * Return true if month is inset range(1, 12) * * @return month inset range(1, 12) * @param month the month of a date */ static public boolean isValidMonth(int month) { boolean result; if ((month >= 1) && (month <= 12)) result = true; else result = false; return result; } /** * Return true if day inset range(1, dayOfMonth(month, year)) * * @return day inset range(1, dayOfMonth(month, year)) * @param month the month of the date * @param day the day of the date * @param year the year of the date */ public static boolean isValidDay(int month, int day, int year) { boolean result; if (!isValidYear(year) ) result = false; else if (!isValidMonth(month)) result = false; else if ((day >= 1) && (day <= daysInMonth(month, year))) result = true; else result = false; return result; } /** * Return true if the day of the year is valid. * * @param dayOfYear the day of the year * @param year the year * @return dayOfYear inset range(1, daysInYear(year)) */ public static boolean isValidDayOfYear(int dayOfYear, int year) { boolean result; if (!isValidYear(year)) result = false; else if ((dayOfYear >= 1) && (dayOfYear <= daysInYear(year))) result = true; else result = false; return result; } /** * Return true if the day of the week is valid. * * @param dayOfWeek the day of the week * @return dayOfWeek inset range(0, 6) */ public static boolean isValidDayOfWeek(int dayOfWeek) { boolean result; if ((dayOfWeek >= SUNDAY ) && (dayOfWeek <= SATURDAY) ) { result = true; } else result = false; return result; } /** * Return the number of days in a month. * *

Example:

*
     * int days = date.daysInMonth(mnth, yr);
     * 
* * @return the number of days in the month * @param mnth month (month inset range(1, 12)) * @param yr year (used to determine if this is a leap year) * (year inset range(MINYEAR, MAXYEAR)) */ public static int daysInMonth(int mnth, int yr) { int dy; assert isValidYear(yr) : "Invalid year: " + yr; assert isValidMonth(mnth) : "Invalid month: " + mnth; dy = WayDateImpl.daysInMonth(mnth, yr); return dy; } /** * Return true if year is a leap year * *

Example:

*
     * if (WayDate.isLeapYear(yr))
     *    {
     *    }
     * 
* @return result true if year is leap year * false if year is not leap year * @param yr a year (year inset range(MINYEAR, MAXYEAR)) */ public static boolean isLeapYear(int yr) { assert isValidYear(yr) : "Illegal year: " + yr; return WayDateImpl.isLeapYear(yr); } /** * Return the number of days in a year. * *

Example:

*
     *    int days = WayDate.daysInYear(yr)
     * 
* * @return days number of days in the year * @param year a year (year inset range(MINYEAR, MAXYEAR)) * */ public static int daysInYear(int year) { assert isValidYear(year) : "Illegal year: " + year; return WayDateImpl.daysInYear(year); } /** * Return a string with the abbreviated name of the month. * *

Example:

*
     *    String name = WayDate.monthString(12);
     * 
* @return name a string with the abbreviated name of the month * @param month the desired month (month inset range(1, 12)) */ public static String monthString(int month) { assert isValidMonth(month) : "Illegal month: " + month; return WayDateImpl.monthString(month); } //------------------------------------------------------------------------------ // Display interface //------------------------------------------------------------------------------ /** * Return the day part of the date. * *

Example:

*
     * int day = aDate.getDay();
     * 
* @return the day part of the date */ public int getDay() { WayDateDisplay dt = date.asWayDateDisplay(); date = dt; return dt.getDay(); } /** * Return the month part of the date. * *

Example:

*
     * int month = aDate.getMonth();
     * 
* @return month month part of the date. */ public int getMonth() { WayDateDisplay dt = date.asWayDateDisplay(); date = dt; return dt.getMonth(); } /** * Return the year part of the date. * *

Example:

*
     * int year = aDate.getYear();
     * 
* @return year the year part of the date. */ public int getYear() { WayDateDisplay dt = date.asWayDateDisplay(); date = dt; return dt.getYear(); } /** * Return the day of the year for a particular date. * *

Example:

*
     *    dayOfYear = date.getDayOfYear();
     * 
* * @return dayOfYear the number of the day in the year * */ public int getDayOfYear() { WayDateDisplay dt = date.asWayDateDisplay(); date = dt; return dt.getDayOfYear(); } /** * Return true if the date is in a leap year. *

Example:

*
     * if (date.isLeapYear())
     *    {
     *    }
     * 
* * @return true if date is in a leap year */ public boolean isLeapYear() { WayDateDisplay dt = date.asWayDateDisplay(); date = dt; return dt.isLeapYear(); } //------------------------------------------------------------------------------ // Calculation interface //------------------------------------------------------------------------------ /** * Add a number of days to a date. * *

Example:

*
     * aDate.add(10);
     * 
* @param value number of days to add to the date * @exception WayDateException thrown if result is out of bounds */ public void add(int value) throws WayDateException { WayDateCalc dt = date.asWayDateCalc(); dt.add(value); date = dt; return; } /** * Increases the date by one day. * *

Example:

*
     * aDate.increment();
     * 
*/ public void increment() { assert !equals(MaxDate): "Date is maximum date: " + toString(); WayDateCalc dt = date.asWayDateCalc(); dt.increment(); date = dt; return; } /** * Decrement date. * *

Example:

*
     * aDate.decrement();
     * 
*/ public void decrement() { assert !equals(MinDate) : "Date is maximum date: " + toString(); WayDateCalc dt = date.asWayDateCalc(); dt.decrement(); date = dt; } /** * Subtract two dates and return the number of * days between the two. * *

Example:

*
     *    diff = aDate.difference(anotherDate);
     * 
* @return diff an integer representing the number of days between the dates. * @param aDate a date */ public int difference(WayDate aDate) { WayDateCalc dt = date.asWayDateCalc(); date = dt; return dt.difference(aDate); } /** * Return the day of the week as an integer. * *

Example:

*
     *   dayOfWk = date.getDayOfWeek();
     * 
* @return dayOfWk 0 for Sunday, 1 for Monday, etc. * (dayOfWk inset range(0, 6)) * */ public int getDayOfWeek() { WayDateCalc dt = date.asWayDateCalc(); date = dt; return dt.getDayOfWeek(); } /** * Return the day of the week as a string. * * @return a string with the day of the weekn */ public String getDayOfWeekString() { int index = getDayOfWeek(); // assert index inset range(0,6) return namesOfDays[index]; } /** * Return the date as an instance of the Jave Date class. * * @return an instance of Date class corresponding to this date */ public Date asDate() { int month = getMonth() - 1; int day = getDay(); int year = getYear(); Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.MONTH , month); calendar.set(Calendar.DAY_OF_MONTH, day ); calendar.set(Calendar.YEAR , year ); Date date = calendar.getTime(); return date; } //------------------------------------------------------------------------------ // Comparable Interface //------------------------------------------------------------------------------ /** * Compare a date with the current date. * If the date is larger, return -1. * If the date is smaller, return +1. * If the date is equal, return 0. * *

Example:

*
     * if (date1.compareTo(date2) == 0)
     * {
     *     ...
     * }
     * 
* @return result -1 if less than, 0 if equal, +1 if greater than * @param aDate an instance of WayDate * @exception ClassCastException if aDate is not an instance of WayDate */ public int compareTo(Object aDate) throws ClassCastException { WayDate dt = (WayDate) aDate; WayDateCalc dt2 = (dt.getDate()).asWayDateCalc(); WayDateCalc dt1 = date.asWayDateCalc(); return dt1.compareTo(dt2); } //------------------------------------------------------------------------------ // Object Methods //------------------------------------------------------------------------------ /** * Return a copy of date. The copy always has an instance WayDateDisplay * as its implementation object. This override is necessary because * the clone() method in Object would return a copy pointing to the * original date implementation object. * * @return an instance of WayDate * */ public Object clone() { WayDateDisplay dt = date.asWayDateDisplay(); return new WayDate(dt.getMonth(), dt.getDay(), dt.getYear()); } /** * Return a string representation of the date. * *

Example:

*
     * String date = aDate.toString();
     * 
* @return date a string representing the date. The format is DD-MMM-YYYY * */ public String toString() { date = date.asWayDateDisplay(); return ((WayDateDisplay)date).toString(); } /** * Return true if another date is equal to this date. Two dates are * considered equal if they both have the same month, day, and year. * This override is necessary because two separate WayDate objects * can be equal, but won't be identical. * * @return true if dates have same month, day, and year */ public boolean equals(Object obj) { boolean result; if (obj instanceof WayDate) { result = (compareTo(obj) == 0); } else result = false; return result; } /** * Return a hash value. This method in fact returns the absolute date * corresponding to the date. This override is necessary so that two * separate date objects with the same month, day, and year will return * the same hash code even though they are different objects. * * @return integer has code */ public int hashCode() { WayDateCalc dt = date.asWayDateCalc(); date = dt; return dt.getAbsoluteDate(); } //------------------------------------------------------------------------------ // Internal Methods //------------------------------------------------------------------------------ /** * Return the instance of WayDateImpl for this date * * @return an instance of the WayDate implementation */ protected WayDateImpl getDate() { return date; } } //------------------------------------------------------------------------------ //Private classes //------------------------------------------------------------------------------ /** * This class is an abstract class that defines the interface to the WayDate * implementation classes and permits a type declaration common to both * subclasses. */ abstract class WayDateImpl implements Serializable { /** an array of days in months for non-leap year */ private static final int daysNonLeapYear[] = {31,28,31,30,31,30,31,31,30,31,30,31}; /** an array of month abbreviations */ private static final String mnthAbbrev[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /** * Return the number of days in a month. This routine assumes month * and year are valid. It does not check, because checking is done in * WayDate class. * *

Example:

*
     * int days = date.daysInMonth(mnth, yr);
     * 
* * @return the number of days in the month * @param mnth month (month inset range(1, 12)) * @param yr year (used to determine if this is a leap year) * (yr inset range(MINYEAR, MAXYEAR)) */ public static int daysInMonth(int mnth, int yr) { int dy; if (isLeapYear(yr) && (mnth == 2)) dy = 29; else dy = daysNonLeapYear[mnth-1]; return dy; } /** * Return true if year is a leap year. Routine assumes a valid year, but * does not check for it. * *

Example:

*
     * if (WayDateImpl.isLeapYear(yr))
     *    {
     *    }
     * 
* @return result true if year is leap year * false if year is not leap year * @param yr a year (yr inset range(MINYEAR, MAXYEAR)) * */ public static boolean isLeapYear(int yr) { return ((yr % 400 == 0) || ((yr % 4 == 0) && (yr % 100 != 0)) ); } /** * Return the number of days in a year. Method assumes a valid year, * but does not check for one. * *

Example:

*
     * int days = WayDateImpl.daysInYear(yr)
     * 
* * @return days number of days in the year * @param year a year (yr inset range(MINYEAR, MAXYEAR)) * */ public static int daysInYear(int year) { int result; if (isLeapYear(year)) result = 366; else result = 365; return result; } /** * Return the day of the year for a particular date. Validity of the * date is assumed, but not checked. * *

Example:

*
     *    dayInYear = WayDateImpl.dayOfYear(2, 29, 2001);
     * 
* * Reference: * Dershowitz, Nachum, and Edward M. Reingold, Calendrical Calculations, * (Cambridge, UK: Cambridge University Press, 1997) p. 36 * * @return dayOfYear the number of the day in the year * (dayOfYear inset range(1,...,daysInYear(yr))) * @param mnth month part of the date (mnth inset range(1,...,12))) * @param dy day part of the date * (dy inset range(1,...,daysInMonth(mnth, dy, yr))) * @param yr year part of the date (yr inset range(MINYEAR, MAXYEAR))) * @see Proof of Code */ protected static int dayOfYear(int mnth, int dy, int yr) { int dayInYear; dayInYear = (367*mnth - 362)/12 + dy; if ( isLeapYear(yr) && (mnth > 2)) dayInYear -= 1; else if (!isLeapYear(yr) && (mnth > 2)) dayInYear -= 2; return (dayInYear); } /** * Return a string with the abbreviated name of the month. * *

Example:

*
     *    String name = WayDate.monthString(12);
     * 
* @return name a string with the abbreviated name of the month * @param mnth the desired month (mnth inset range(1, 12}) * */ public static String monthString(int mnth) { assert WayDate.isValidMonth(mnth) : "Illegal month: " + mnth; return mnthAbbrev[mnth-1]; } /** * Return an instance of WayDateDisplay with current date * (as obtained from the computer) * * @return an instance of WayDateDisplay */ public static WayDateImpl today() { // get the current date and time Date date = new Date(); return asWayDate(date); } /** * Return an instance of WayDate corresponding to the instance of date * provided as an input. * * @param date an instance of the Java Date class * @return the corresponding instance of WayDate */ public static WayDateImpl asWayDate(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int year = calendar.get(Calendar.YEAR); //full four digit year int month = calendar.get(Calendar.MONTH) + 1; //add one since first month is 0 int day = calendar.get(Calendar.DAY_OF_MONTH); //in set {1,...,31} return new WayDateDisplay(month, day, year); } //------------------------------------------------------------------------------ // Conversion methods //------------------------------------------------------------------------------ /** * Return an instance of the date as a WayDateCalc object. * * @return an instance of WayDateCalc equal to the current object */ public abstract WayDateCalc asWayDateCalc(); /** * Return an instance of the date as a WayDateDisplay object * * @return an instance of WayDateDisplay equal to the current object */ public abstract WayDateDisplay asWayDateDisplay(); } //------------------------------------------------------------------------------ // Calculation Date Class //------------------------------------------------------------------------------ /** * This class performs calculations on dates. Its field consists of a * single absolute date field that facilitates calculations. Absolute date * calculations cannot generate integer overflow, because an absolute date * is always positive and less than the maximum size of an int. * */ class WayDateCalc extends WayDateImpl implements DateCalculation, Comparable { /** * The serial verion unique identifier for this class */ private static final long serialVersionUID = 9701L; /** * The maximum absolute date for 31-DEC-3999 */ private static final int MAX_ABSOLUTE = 876216; //absolute date for 31-DEC-3999 /** * The absolute date */ private int abDate; /** * Given a date, return the number of days since 12/31/1600. * Absolute dates, or the number of days from the beginning range * of legal dates, are used to perform computations upon dates. * Thus a date calculation can be performed by converting the * input dates to absolute dates, performing the calculations, * and converting the result back to a gregorian date. * *

Example:

*
     *    WayDateCalc date = new WayDateCalc(month, day, year);
     * 
* * @param month the month part of the date (month inset range(1, 12)) * @param day the day part of the date (day inset range(1, daysInMonth(month, year)) * @param year the year part of the date (year inset range(MINYEAR, MAXYEAR)) */ public WayDateCalc(int month, int day, int year) { abDate = dayOfYear(month, day, year) + daysInPriorYears(year); return; } /** * Return the number of days in prior years. *

* The algorithm works by counting the number of days in prior years * since the beginning of 1601. Multiplying the number of years by * 365 gives an initial estimate, but underestimates the number of * days because it does not count leap days. Therefore, we add * a day for every fourth year. This over-compensates, because * some years divisible by 100 are not leap years. We therefore * subtract the years divisible by 100. But this is too much * of a correction, since years divisible by 400 are leap years. * So we finally add in the number of years divisible by 400. *

*

* Reference: * Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations: * The Millennium Edition * (Cambridge, UK: Cambridge University Press, 2001) * p. 51 *

*

Example:

*
     *    days = WayDate.daysInPriorYears(yr);
     * 
* * @return days number of days in years prior to yr. * @param yr a year (yr inset range(MINYEAR,...,MAXYEAR)) * */ protected static int daysInPriorYears(int yr) { assert yr >= 1601:"illegal year: " + yr; int y = yr - 1601; int days; days = 365 * y // days in prior years + y/4 // plus julian leap days in prior years - y/100 // minus prior century years + y/400; // plus years divisible by 400 return days; } /** * Return the year implied by an absolute date. The algorithm is from: * * Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations: * The Millennium Edition * (Cambridge, UK: Cambridge University Press, 2001) * p. 52 * * @return year year associated with an absolute date * (year inset range(MINYEAR, MAXYEAR)) * @param abDate an absolute date (abDate inset range(1, MAX_ABSOLUTE}) * */ public static int yearFromAbsolute(int abDate) { int year; int d0 = abDate - 1; int n400 = d0/146097; //Number of days in a 400 year cycle: // 400*365 + 100 - 3 int d1 = d0 % 146097; int n100 = d1/36524; //Number of days in 100 year cycle: // 365*100 + 25 - 1 int d2 = d1 % 36524; int n4 = d2/1461; //Number of days in 4 year cycle: // 365*4 + 1 int d3 = d2 % 1461; int n1 = d3/365; //Number of days in 1 year: // 365 // // If n100 = 4 orelse n1 = 4, then the date is 31-Dec of the leap // year so we add one less year to get the absolute year // if ((n100 == 4) || (n1 == 4)) year = 400*n400 + 100*n100 + 4*n4 + n1 + 1600; else year = 400*n400 + 100*n100 + 4*n4 + n1 + 1601; return year; } // // Conversion Methods // /** * Return an instance of WayDateDisplay. * * @return the current object */ public WayDateDisplay asWayDateDisplay() { int year = yearFromAbsolute(abDate); int dayOfYear = abDate - daysInPriorYears(year); return new WayDateDisplay(dayOfYear, year); } /** * Return an instance of WayDateCalc with an equivalent value. * Since this is a WayDateCalc object, return this. * * @return an instance of WayDateCalc */ public WayDateCalc asWayDateCalc() { return this; } // // Date Caclulation Interface // /** * Add a number of days to a date. * *

Example:

*
     * aDate.add(10);
     * 
* @param value number of days to add to the date */ public void add(int value) throws WayDateException { int result = abDate + value; if (result < 1) { throw new WayDateException(WayDateException.ILLEGAL_ABSOLUTE, result); } if (result > MAX_ABSOLUTE) { throw new WayDateException(WayDateException.ILLEGAL_ABSOLUTE, result); } abDate = result; return; } /** * Increases the date by one day. * *

Example:

*
     * aDate.increment();
     * 
*/ public void increment() { assert (abDate < MAX_ABSOLUTE) : "absolute date is maximum"; abDate++; return; } /** * Decrement date. * *

Example:

*
     * aDate.decrement();
     * 
*/ public void decrement() { assert (abDate > 1) : "Absolute date is 1"; abDate--; return; } /** * Subtract two dates and return the number of * days between the two. * *

Example:

*
     *    diff = aDate.difference(anotherDate);
     * 
* @return the number of days between the dates. * @param anotherDate a date */ public int difference(WayDate anotherDate) { WayDateCalc date2 = anotherDate.getDate().asWayDateCalc(); return abDate - date2.getAbsoluteDate(); } /** * Return the day of the week as an integer. This method depends on * the fact that 1-Jan-1601 is a Monday. * *

Example:

*
     *   dayOfWk = date.getDayOfWeek();
     * 
* @return dayOfWk 0 for Sunday, 1 for Monday, etc. * (dayOfWk inset range(0, 6)) * */ public int getDayOfWeek() { return abDate % 7; } // // Comparable Interface // /** * Compare a date with the current date. * If the current date is less than supplied date, return -1. * If the current date is greater than supplied date, return +1. * If the current date is equal to supplied date, return 0. * *

Example:

*
     * if (date1.compareTo(date2) == 0)
     * {
     *     ...
     * }
     * 
* @return result -1 if less than, 0 if equal, +1 if greater than * @param aDate a date */ public int compareTo(Object aDate) { int abDate2 = ((WayDateCalc)aDate).getAbsoluteDate(); int result; if (abDate2 > abDate) {result = -1;} else if (abDate2 < abDate) {result = 1;} else {result = 0;} return result; } // // Internal methods // /** * Return the absolute date value * * @return absolute date */ public int getAbsoluteDate() { return abDate; } } //------------------------------------------------------------------------------ // Display Date Class //------------------------------------------------------------------------------ /** * This class handles displaying of dates. Its fields separately record * the month, day, and year of the date. * */ class WayDateDisplay extends WayDateImpl implements DateDisplay { /** * The serial verion unique identifier for this class */ private static final long serialVersionUID = 9701L; // Fields /** * The day portion of the date */ protected final int day; /** * The month portion of the date */ protected final int month; /** * The year portion of the date */ protected final int year; /** * Create a WayDate object with the specified * month, day, and year. * *

Example:

*
     * WayDate aDate = new WayDate(12, 22, 1998);
     * 
*

Specification

* * @param _month month of the date (_month inset range(1, 12)) * @param _day day of the date (_day inset range(1, daysInMonth(_month))) * @param _year year of the date (_year inset range(MINYEAR, MAXYEAR)) * */ public WayDateDisplay(int _month, int _day, int _year) { month = _month; day = _day; year = _year; } /** * Return an instance of WayDateDisplay the day of the year of which * corresponds to the input to this constructor. This constructor and the * accessors getDayOfYear() and getYear() provide the capability to work * with day of year (or Julian date) calculations. * *

Example:

*
     *    WayDate date = new WayDate(dayOfYear, yr);
     * 
* @param dayYear day of the year for the date (dayOfYear inset range(1,daysInYear(year)) * @param yr year of the date (yr inset range(MINYEAR, MAXYEAR)) */ public WayDateDisplay(int dayYear, int yr) { int mnth = 1; int dy = 1; year = yr; int diff = dayYear - 1; // dayOfYear(1, 1, year) = 1 /* * Precondition: isValidYear(yr) andalso isValidDayOfYear(dayYear) * Loop Invariant: diff = dayYear - dayOfYear(mnth, dy, yr) andalso * mnth inset range(1, 12) andalso * dy inset range(1, daysInMonth(mnth,yr)) * * Bound Function: * fun diff(dayYear : int, mnth : Month, dy : Day, year : Year) : int= * dayYear - dayOfYear(mnth, dy, year); * * Termination: diff = 0 * Postcondition: dayOfYear = dayOfYear(mnth, dy, yr) andalso * isValidDate(mnth, dy, yr) */ /* * Theorem 1: Changes in dayOfYear, month > 1 * * Precondition: month inset range(2, 12) * Postcondition: daysInMonth(month, year) = dayOfYear(month, day, year) - * dayOfYear(month-1, day, year) * Proof: * From definiton of dayOfYear() */ /* * Theorem 2: Changes in dayOfYear, month = 1 * * Precondition: month = 1 andalso day = 31 * Postcondition: daysInMonth(month, year) = dayOfYear(month, day, year) * * Proof: * From definition of dayOfYear() */ /* * Theorem 3: Change in day of month * * Precondition: diff < daysInMonth(month, year) * Postcondition: diff = dayOfYear(month, diff + 1, year) - * dayOfYear(month, 1, year) * * Proof: * From definition of dayOfYear() */ /* * Proof Obligation 1: the invariant holds at the beginning * * Proof: * From: dayOfYear(1, 1, yr) = 1 * 1. diff = dayYear - dayOfYear(1, 1, yr) * 2. 1 inset range(1, 12) * 3. 1 inset range(1, daysInMonth(mnth,yr)) * Infer: invariant holds */ while (diff > 0) { if (diff >= daysInMonth(mnth, year)) { diff -= daysInMonth(mnth, year); mnth++; /* * Proof Obligation 2: the invariant still holds * * Proof: * From: diff = diff' - daysInMonth(mnth, year) * 1. diff = diff' - * dayOfYear(mnth, year) + dayOfYear(mnth-1, year) {Theorem 1 and 2} * 2. diff' = dayYear - dayOfYear(mnth-1, 1, year) * 3. diff = dayYear - dayOfYear(mnth , 1, year) * 4. dy = 1 * 5. mnth inset range(1, 12) {Precondition} * Infer: diff = dayYear - dayOfYear(mnth, 1, yr) andalso * mnth inset range(1, 12) andalso * dy inset range(1, daysInMonth(mnth,yr)) */ } else { dy = diff + 1; diff = 0; /* * Proof Obligation 2: the invariant still holds * * Proof: * From: dy = diff + 1 andalso diff = 0 * 1. mnth inset range(1,12) {invariant prior iteration} * 2. dy inset range(1, daysInMonth(mnth,yr)) {1st guard command} * 3. diff' = dayYear - dayOfYear(mnth, 1, yr) * 4. diff' = dayYear - dayOfYear(mnth, diff' + 1, yr) + * diff' {Theorem 3} * 5. 0 = dayYear - dayOfYear(mnth, dy, yr) * 6. diff = 0 * 7. diff = dayYear - dayOfYear(mnth, dy, yr) * Infer: diff = dayYear - dayOfYear(mnth, dy, yr) andalso * mnth inset range(1, 12) andalso * dy inset range(1, daysInMonth(mnth,yr)) */ } } day = dy; month = mnth; /* * Proof Obligation 5: Each iteration reduces bound function * * Proof: * Under first guard, diff is reduced by dayInMonth which is * always positive. * * Under second guard, diff is set to 0 terminating iteration */ /* * Proof Obligation 4: Bound function is positive if loop has not terminated. * * Proof: * diff must be positive else loop terminates */ /* * Proof Obligation 3: Postcondition is true * * From: 0 = dayYear - dayOfYear(mnth, dy, yr) andalso * mnth inset range(1, 12) andalso * dy inset range(1, daysInMonth(mnth,yr)) * 1. dayYear = dayOfYear(mnth, dy, yr) andalso * mnth inset range(1, 12) andalso * dy inset range(1, daysInMonth(mnth, yr)) * Infer: postcondition holds */ return; } /** * Return an instance of WayDateDisplay. Since this object is * a WayDateDisplay, just return the current object. * * @return the current object */ public WayDateDisplay asWayDateDisplay() { return this; } /** * Return an instance of WayDateCalc with an equivalent value. * * @return an instance of WayDateCalc */ public WayDateCalc asWayDateCalc() { return new WayDateCalc(month, day, year); } // // Access Routines // /** * Return the day part of the date. * *

Example:

*
     * int day = aDate.getDay();
     * 
* @return day the day part of the date */ public int getDay() { return day; } /** * Return the month part of the date. * *

Example:

*
     * int month = aDate.getMonth();
     * 
* @return month month part of the date. */ public int getMonth() { return month; } /** * Return the year part of the date. * *

Example:

*
     * int year = aDate.getYear();
     * 
* @return year the year part of the date. */ public int getYear() { return year; } /** * Return true if the date is in a leap year. *

Example:

*
     * if (date.isLeapYear())
     *    {
     *    }
     * 
* * @return true if date is in a leap year */ public boolean isLeapYear() { return isLeapYear(year); } /** * Return the day of the year for a date. * *

Example:

*
     * int dayYear = date.getDayOfYear();
     * 
* @return the day of the year for the date. */ public int getDayOfYear() { return dayOfYear(month, day, year); } /** * Return a string representation of the date. * *

Example:

*
     * String date = aDate.toString();
     * 
* @return date a string representing the date. The format is DD-MMM-YYYY * */ public String toString() { String dayString = String.valueOf(day); if (dayString.length() == 1) dayString = "0" + dayString; String dateString = dayString + "-" + monthString(month) + "-" + String.valueOf(year); return dateString; } }