Handout 31

The Case of the Missing Methods*


You have just begen your promising programming career at a consulting company know as Big Buck$ Software. For your first task, you have been asked to fix some code left by another programmer. What your fellow programmer has left is a copy of the header files for a date class and some of the methods associated with the class. Your task is to complete the class as defined by the header file and make it operational. The available code includes: main.cpp date.h date.cpp. A listing of the code is shown below.


main.cpp

#include <iostream.h>
#include "date.h"

int main() {

	Date a(12,5,2001), b(12,5,2000), c;

	c = a;

	return 0;

}


date.h

#ifndef _DATE_H
#define _DATE_H

#include <iostream.h>

// a class for manipulating dates
//
// Date class represents a date in the Gregorian calendar
// works only for dates after October, 1752
//
// attempts to construct invalid dates, e.g., 15 month,
// or 38th day result in month == 1, day == 1.  years aren't checked
// for validity
//
// Date()                  --- construct default date (today)
// Date(long int days)     --- construct date given absolute # of days from
//                             1 A.D., e.g., 710,347 = November 12, 1945
//
// Date(int m,int d,int y) --- constructor requires three parameters:
//                             month, day, year, e.g.,
//                             Date d(4,8,1956); initializes d to represent
//                             the date April 8, 1956.  Full year is required
//
// void SetDate(int m,int d,int y) --- set date according to parameters:
//                             month, day, year, e.g.,
//                             d.SetDate(1,1,2000); sets date d to January
//                             1 in the year 2000
//    
// int Month()             --- return, respectively, month, day, and year
// int Day()                   corresponding to date with 1 = january,
// int Year()                  2 = february, ... 12 = december
//                                
//
// char * DayName()        --- return string corresponding to day of week
//                             either "Monday", "Tuesday", ... "Sunday"
// char * MonthName()      --- return string corresponding to month
//                             either "January", "February",..."December"
//
// int DaysIn()            --- return number of days in month
//                                                         
//    
// long int Absolute() --- returns absolute # of date assuming
//                         that Jan 1, 1 AD is day 1.  Has property
//                         that Absolute() % 7 = k, where k = 0 is sunday
//                         k = 1 is monday, ... k = 6 is saturday
//
// char * ToString()  -- returns string version of date, e.g.,
//                    -- d.SetDate(11,23,1963); then d.ToString()
//                       returns string "November 23 1963"
// char * DateAscii() -- returns ascii version of date
//                       (for backward compatibility)
//
// *************************************************
//         arithmetic operators for dates
// *************************************************
//
// dates support some addition and subtraction operations
//
// Date d(1,1,1960);        // 1960 is a leap year
// d++;                     // d represents January 2, 1960
// d--;                     // d is back to January 1, 1960
// d += 31;                 // d is February 1, 1960
// d -= 32;                 // d is December 31, 1959
// Date d2 = d + 1;         // d2 is January 1, 1960
// Date d3 = 365 + d2;      // d3 is December 31, 1961
// Date d4 = d - 1;         // d4 is December 30, 1959
//
// *************************************************
class Date
{
  public:
              // constructors
    Date();                       // construct date with default value
    Date(long int days);          // construct date from absolute #
    Date(int m,int d,int y);      // construct date with specified value
                                  // set date via CheckDate method

              // accessor functions

    int Month()         const;     // return month corresponding to date
    int Day()           const;     // return day corresponding to date
    int Year()          const;     // return year corresponding to date
    int DaysIn()        const;     // return # of days in month
    char * DayName()    const;     // "monday", "tuesday", ... or "sunday"
    char * MonthName()  const;     // "january","february",... or "december"
    long int Absolute() const;     // number of days since 1 A.D. for date
    char * ToString()   const;     // returns string for date in ascii
    char * DateAscii()  const;     // returns string for date in ascii

             // mutator functions

    void SetDate(int m,int d,int y);  // set to specified date
    Date operator ++(int);       // add one day, postfix operator
    Date operator --(int);       // subtract one day, postfix operator
    Date operator ++();          // add one day, preix operator
    Date operator --();          // subtract one day, prefix operator
    Date& operator +=(long dx);  // add dx, e.g., jan 1 + 31 = feb 1
    Date& operator -=(long dx);  // subtract dx, e.g., jan 1 - 1 = dec 31

    bool Equal(const Date & rhs) const;
    bool Less(const Date & rhs) const;
    
  private:
    
    int myDay;                    // day of week, 0-6
    int myMonth;                  // month, 0-11
    int myYear;                   // year in four digits, e.g., 1899

    void CheckDate(int m, int d, int y); // make sure that date is valid
};

// other operators that are not part of the class

Date operator + (const Date & d, long dx);       // add dx to date d
Date operator + (int dx, const Date & d);        // add dx to date d
Date operator - (const Date & d, long dx);       // subtract dx from date d
long operator - (const Date & lhs, const Date & rhs);  // subtract dates

ostream &operator<<( ostream &output, const Date &d );
bool operator == (const Date & lhs, const Date & rhs);
bool operator != (const Date & lhs, const Date & rhs);
bool operator <  (const Date & lhs, const Date & rhs);
bool operator >  (const Date & lhs, const Date & rhs);
bool operator <= (const Date & lhs, const Date & rhs);
bool operator >= (const Date & lhs, const Date & rhs);

// helper methods

static bool IsLeap(int year);
static int DaysInMonth(int month,int year);
 
#endif


date.cpp

#include "date.h"
#include <time.h>
#include <iostream.h>
#include <string.h>

char * dayNames [] = {"Sunday",  "Monday", "Tuesday","Wednesday",
                      "Thursday","Friday", "Saturday"};

char * monthNames [] = {"January","February","March","April",
						"May","June","July","August",
						"September","October","November","December"};

char returnString[50];

///////////////////////   constructors   //////////////////////////////

Date::Date()
// postcondition: date initialized to default date (today)
{
    static struct tm timeHolder;
    static struct tm *date = &timeHolder;
    time_t tloc;
    
    time(&tloc);

    date = localtime(&tloc);
    
    myMonth = date->tm_mon+1;
    myDay   = date->tm_mday;
    myYear  = date->tm_year+1900;             // struct tm based on 1900
}

Date::Date(long int days)
// postcondition: date initialized corresponding to absolute days
//                after 1 A.D.
{
        // this code is taken from "Calendrical Calculations, II"
        // Reingold and Dershowitz and Clamen
        // Software Practice and Experience, V. 23(4) 383-404 (april 1993)

    long int prior = days - 1;               // prior days
    long int years400 = prior / 146097;      // # of 400 year cycles
    long int days400 = prior % 146097;       // days NOT in years400

    long int years100 = days400 / 36524;     // # 100 yr. cycles not checked
    long int days100 =  days400 % 36524;     // days NOT already included

    long int years4 = days100 / 1461;        // # 4 yr cycles not checked
    long int days4 = days100 % 1461;         // days NOT already included

    long int year1 = days4 / 365;            // # years not already checked
    long int day1  = days4 % 365;            // days NOT already included


        // use "magic formula" from SP&E article
    long int finalDay;
    long int finalYear = 400*years400 + 100*years100 + 4*years4 + year1;
    
    if (years100 == 4 || year1 == 4){      // December 31 of leap year
        finalDay = 366;
    }
    else{
        finalDay = day1 + 1;
        finalYear += 1;
    }

        // now have year and day #, find month and day in month
    myMonth = 1;
    while (myMonth <= 12 && 0 < finalDay){
        finalDay -= DaysInMonth(myMonth,finalYear);
        myMonth += 1;
    }
    myMonth -= 1;                              // went one to far
    finalDay += DaysInMonth(myMonth,finalYear);

    myDay = finalDay;
    myYear = finalYear;
}

Date::Date(int m, int d, int y)
// postcondition: date properly initialized for date m/d/y (american style)
// exception:  if m isn't between 1 and 12, converted to 1
//             if d out of range for month, converted to 1     
{
    CheckDate(m,d,y);    
}

///////////////////////   accessor methods   //////////////////////////////

long int Date::Absolute() const
// postcondition: returns absolute # days corresponding to Date
//                assuming January 1, 1 B.C is day 1
{
    int m = 1;               // start in January;
    int daysBefore = 0;      // count # days before month

    while (m < myMonth){       // tally # of days in preceding months
        daysBefore += DaysInMonth(m,myYear);
        m += 1;
    }

    long int dayYears = 365 * (myYear - 1);    // days before this year

        // add 1 extra day for each leap year
    
    int leapYears = (myYear - 1) / 4;     // initial # of leap years

    leapYears -= (myYear - 1) / 100;      // subtract years divisible by 100
    leapYears += (myYear - 1) / 400;      // add back years divisibly by 400

    return myDay + daysBefore + dayYears + leapYears;
}


///////////////////////   mutator methods   //////////////////////////////


///////////////////////   other methods   //////////////////////////////


ostream &operator<<( ostream &output, const Date &d ) {
	output << d.DayName() << " " << d.MonthName() << " " 
		<< d.Day() << ", " << d.Year(); 
	return output; // enables cout << x << y;
}


///////////////////////   helper methods   //////////////////////////////

static bool IsLeap(int year)
// postcondition: returns 1 if year is a leap year, else returns 0     
{
    if (year % 400 == 0){
        return true;
    }
    else if (year % 100 == 0){
        return false;
    }
    else if (year % 4 == 0){
        return true;
    }

    return false;
}

static int DaysInMonth(int month,int year)
// postcondition: returns # of days in month in year     
{
    int days = 30;

        // 30 days hath september, april, june, and november
        // other months have 31 as below (except February)
    
    if (month == 1 || month == 3 || month == 5 || month == 7 ||
        month == 8 || month == 10 || month == 12){
        days = 31;
    }
    else if (month == 2){          // treat February differently
        days = 28;
        if (IsLeap(year) == 1){        // add 1 for leap years
            days += 1;
        }
    }
    return days;
}


* based on an example from Computing Fundamentals With C++ : Object-Oriented Programming & Design by Rick Mercer...