## Code Capsule

### A C++ Date Class, Part 2

### Chuck Allison

> Chuck Allison is a software architect for the Family History Department of Jesus Christ of Latter Day Saints Church Headquarters in Salt Lake City. He has a B.S. and M.S. in mathematics, has been programming since 1975, and has been teaching and developing in C since 1984. His current interest object-oriented technology and education. He is a member of X3J16, the ANSI C++ Standards Committee. Chuck can be reached on the Internet at allison@decus.org, or at (801) 240-4510.

> #### Listing 1 Introduces a function to compare dates

>     // date4.h

>     class Date
>     {
>        int month;
>        int day;
>        int year;

>     public:
>        // Constructors
>        Date()
>          {month = day = year= 0;}
>        Date(int m, int d, int y)
>          {month = m; day = d; year = y;}

>        // Accessor Functions
>        int get_month() const
>          {return month;}
>        int get_day() const
>          {return day;}
>        int get_year() const
>          {return year;}

>        Date * interval(const Date&) const;
>        int compare(const Date&) const;
>     };

>     // End of File

> #### Listing 2 Implements the interval and compare member functions

>     // date4.cpp

>     #include "date4.h"

>     inline int isleap(int y)
>       {return y%4 == 0 && y%100 != 0 || y%400 == 0;}

>     static int dtab[2][13] =
>     {
>       {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
>       {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
>     };

>     Date * Date::interval(const Date& d2) const
>     {
>        static Date result;
>        int months, days, years, prev_month;

>        // Compute the interval - assume d1 precedes d2
>        years = d2.year - year;
>        months = d2.month - month;
>        days = d2.day - day;

>        // Do obvious corrections (days before months!)
>        //
>        // This is a loop in case the previous month is
>        // February, and days < -28.
>        prev_month = d2.month - 1;
>        while (days < 0)
>        {
>           // Borrow from the previous month
>           if (prev_month == 0)
>              prev_month = 12;
>           -months;
>           days += dtab[isleap(d2.year)][prev_month-];
>        }

>        if (months < 0)
>        {
>           // Borrow from the previous year
>           -years;
>           months += 12;
>        }

>        // Prepare output
>        result.month = months;
>        result.day = days;
>        result.year = years;
>        return &result;
>     }

>     int Date::compare(const Date& d2) const
>     {
>        int months, days, years, order;

>        years = year - d2.year;
>        months = month - d2.month;
>        days = day - d2.day;

>        // return <0, 0, or >0, like strcmp()
>        if (years == 0 && months == 0 && days == 0)
>           return 0;
>        else if (years == 0 && months == 0)
>           return days;
>        else if (years == 0)
>           return months;
>        else
>           return years;
>     }
>     // End of File

> #### Listing 3 Tests the compare member function

>     // tdate4.cpp

>     #include <stdio.h>
>     #include "date4.h"

>     void compare_dates(const Date& d1, const Date& d2)
>     {
>        int compval = d1.compare(d2);
>        char *compstr - (compval < 0) ? "precedes" :
>          ((compval > 0) ? "follows" : "equals"};

>        printf("%d/%d/%d %s %d/%d/%d\n",
>          d1.get_month(),d1.get_day(0),d1.get_year(),
>          compstr,
>          d2.get_month(),d2.get_day(),d2.get_year());
>     }
>     main()
>     {
>        Date d1(1,1,1970);
>        compare dates(d1,Date(10,1,1951));
>        compare_dates{d1,Date(1,1,1970));
>        compare_dates(d1,Date(12,31,1992));
>        return 0;
>     }

>     /* OUTPUT

>     1/1/1970 follows 10/1/1951
>     1/1/1970 equals 1/1/1970
>     1/1/1970 precedes 12/31/1992
>     */

>     // End of File

> #### Listing 4 Defines relational operators for the Date class

>     // date5.h

>     class Date
>     {
>        int month;
>        int day;
>        int year;

>     public:
>        // Constructors
>        Date()
>          {month = day = year = 0;}
>        Date(int m, int d, int y)
>          {month = m; day = d; year = y;}

>        // Accessor Functions
>        int get_month() const
>          {return month;}
>        int get_day() const
>          {return day;}
>        int get_year() const
>          {return year;}

>        Date * interval(const Date&) const;
>        int compare(const Date&) const;

>        // Relational operators
>        int operator<(const Date& d2) const
>          {return compare(d2) < 0;}
>        int operator<=(const Date& d2) const
>          {return compare(d2) <= 0;}
>        int operator>(const Date& d2) const
>          {return compare(d2) > 0;}
>        int operator>=(const Date& d2) const
>          {return compare(d2) >= 0;}
>        int operator!=(const Date& d2) const
>          {return compare(d2) != 0;}
>        int operator!=(const Date& d2) const
>          {return compare(d2) !=0;}
>     };

>     // End of File

> #### Listing 5 Uses the Date relational operators

>     // tdate5.cpp

>     #include <stdio.h>
>     #include <stdlib.h>
>     #include "date5.h"

>     void compare_dates(const Date& d1, const Date& d2)
>     {
>        char *compstr = (d1 < d2) ? "precedes" :
>          ((d1 > d2) ? "follows" : "equals");

>        printf("%d/%d/%d %s %d/%d/%d\n",
>          d1.get_month(),d1.get_day(),d1.get_year(),
>          compstr,
>          d2.get_month(),d2.get_day(),d2.get_year());
>     }

>     main()
>     {
>        Date d1(1,1,1970);
>        compare_dates(d1,Date(10,1,1951));
>        compare_dates(d1,Date(1,1,1970));
>        compare_dates(d1,Date(12,31,1992));
>        return 0;
>     }

>     /* OUTPUT

>     1/1/1970 follows 10/1/1951
>     1/1/1970 equals 1/1/1970
>     1/1/1970 precedes 12/31/1992
>     */

>     // End of File

> #### Listing 6 Adds binary and unary minus to the Date class

>     // date6.h

>     class Date
>     {
>        int month;
>        int day;
>        int year;

>     public:
>        // Constructors
>        Date()
>          {month = day = year = 0;}
>        Date(int m, int d, int y)
>          {month = m; day = d; year = y;}

>        // Accessor Functions
>        int get_month() const
>          {return month;}
>        int get_day() const
>          {return day;}
>        int get_year() const
>          {return year;}

>        Date operator-(const Date& d2) const;
>        Date& operator-()
>          {month = -month; day = -day; year = -year;
>           return *this;}

>        int compare(const Date&) const;

>        // Relational operators
>        int operator<(const Date& d2) const
>          {return compare(d2) < 0;}
>        int operator<=(const Date& d2) const
>          {return compare(d2) <= 0;}
>        int operator>(const Date& d2) const
>          {return compare(d2) > 0;}
>        int operator>=(const Date& d2) const
>          {return compare(d2) >= 0;}
>        int operator==(const Date& d2) const
>          {return compare(d2) == 0;}
>        int operator!=(const Date& d2) const
>          {return compare(d2) != 0;}
>     };

>     // End of File

> #### Listing 7 Implements the binary minus operator

>     // date6.cpp
>     #include <assert.h>
>     #include "date6.h"

>     inline int isleap(int y)
>       {return y%4 == 0 && y%100 != 0 || y%400 == 0;}

>     static int dtab[2][13] =
>     {
>       {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
>       {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
>     };

>     Date Date::operator-(const Date& d2) const
>     {
>        int months, days, years, prev_month, order;
>        const Date * first, * last;

>        // Must know which date is first
>        if (compare(d2) <= 0)
>        {
>           // this <= d2
>           order = -1;
>           first = this;
>           last = &d2;
>        }
>        else
>        {
>           order = 1;
>           first = &d2;
>           last = this;
>        }

>        // Compute the interval; first <= last
>        years = last->year - first->year;
>        months = last->month - first->month;
>        days = last->day - first->day;
>        assert(years >= 0 && months >= 0 && days >= 0);

>        // Do obvious corrections (days before months!)
>        // This is a loop in case the previous month is
>        // February, and days < -28.
>        prev_month = last->month - 1;
>        while (days < 0)
>        {
>           // Borrow from the previous month
>           if (prev_month == 0)
>              prev_month = 12;
>           --months;
>           days += dtab[isleap(last->year)][prey_month--];
>        }

>        if {months < 0)
>        {
>           // Borrow from the previous year
>           --years;
>           months += 12;
>        }

>        // Return a date object with the interval
>        if (order == -1)
>           return Date(-months,-days,-years);
>        else
>           return Date(months, days, years);
>     }

>     int Date::compare(const Date& d2) const
>     {
>         // same as in Listing 2
>     }
>     // End of File

> #### Listing 8 Subtracts two dates

>     // tdate6.cpp:

>     #include <stdio.h>
>     #include "date6.h"

>     main()
>     {
>        Date d1(1,1,1970), d2(12,8,1992);
>        Date result = d1 - d2;
>        printf("years: %d, months: %d, days: %d\n",
>           result.get_year(),
>           result.get_month(),
>           result.get_day());
>        result = d2 - d1;
>        printf("years: %d, months: %d, days: %d\n",
>           result.get_year(),
>           result.get_month(),
>           result.get_day());
>        int test = d1 - d2 == -(d2 - d1);
>        printf("d1 - d2 == -(d2 - d1)? %s\n",
>          test ? "yes" : "no");
>        return 0;
>     }

>     /* OUTPUT

>     years: -22, months: -11, days: -7
>     years: 22, months: 11, days: 7
>     d1 - d2 == -(d2 - d1)? yes
>     */

>     // End of File

> #### Listing 9 Adds stream I/O to the Date class

>     // date7.h

>     class ostream;

>     class Date
>     {
>        int month;
>        int day;
>        int year;

>     public:
>        // Constructors
>        Date()
>          {month = day = year = 0;}
>        Date(int m, int d, int y)
>          {month = m; day = d; year = y;}

>        // Accessor Functions
>        int get_month() const
>          {return month;}
>        int get_day() const
>          {return day;}
>        int get_year() const
>          {return year;}

>        Date operator-(const Date& d2) const;
>        Date& operator-()
>          {month= -month; day = -day; year = -year;
>           return *this;}

>        int compare(const Date&) const;

>        // Relational operators
>        int operator<(const Date& d2) const
>          {return compare(d2) < 0;}
>        int operator<=(const Date& d2) const
>          {return compare(d2) <= 0;)
>        int operator>(const Date& d2) const
>          {return compare(d2) > 0;}
>        int operator>=(const Date& d2) const
>          {return compare(d2) >= 0;}
>        int operator==(const Date& d2) const
>          {return compare(d2) == 0;}
>        int operator!=(const Date& d2) const
>          {return compare(d2) != 0)

>        // I/O operators
>        friend ostream& operator<<(ostream&, const Date&);
>        friend istream& operator>>(istream&, Date&);
>     };

>     // End of File

> #### Listing 10 Implements Date stream I/O functions

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

>     ostream& operator<<(ostream& os, const Date& d)
>     {
>        os << d.month << '/' << d.day << '/' << d.year;
>        return os;
>     }

>     istream& operator>>(istream& is, Date& d)
>     {
>        char slash;
>        is >> d.month >> slash >> d.day >> slash >> d.year;
>        return is;
>     }

>     // End of File

> #### Listing 11 Illustrates stream I/O of Date objects

>     // tdate7.cpp:

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

>     main()
>     {
>        Date d1, d2;
>        cout << "Enter a date: ";
>        cin >> d1;
>        cout << "Enter another date: ";
>        cin >> d2;
>        cout << "d1 - d2 = "<< d1 - d2 << endl;
>        cout << "d2 - d1 = "<< d2 - d1 << endl;
>        return 0;
>     }

>     /* OUTPUT

>     Enter a date: 10/1/1951
>     Enter another date: 5/1/1954
>     d1 - d2 = -7/0/-2
>     d2 - d1 = 7/0/2
>     */

>     // End of File

> #### Listing 12 Defines static members

>     // date8.h

>     // Forward declarations
>     class istream;
>     class ostream;

>     class Date
>     {
>        int month;
>        int day;
>        int year;

>        static int dtab[2][13];

>     public:
>        // Constructors
>        Date();         // Get today's date (see .cpp file)
>        Date(int m, int d, int y)
>          {month = m; day = d; year = y;}

>        // Accessor Functions
>        int get_month() const
>          {return month;}
>        int get_day() const
>          {return day;}
>        int get_year() const
>          {return year;}

>        Date operator-(const Date& d2) const;
>        Date& operator-()
>          {month = -month; day = -day; year = -year;
>           return *this;}

>        int compare(const Date&) const;

>        // Relational operators
>        int operator<(const Date& d2) const
>          {return compare{d2) < 0;}
>        int operator<=(const Date& d2) const
>          {return compare(d2) <= 0;}
>        int operator>(const Date& d2) const
>          {return compare(d2) > 0;}
>        int operator>=(const Date& d2) const
>          {return compare(d2) >= 0;}
>        int operator==(const Date& d2) const
>          {return compare(d2) == 0;}
>        int operator!=(const Date& d2) const
>          {return compare(d2) != 0;}

>        // Stream I/O operators
>        friend ostream& operator<<(ostream&, const Date&);
>        friend istream& operator>>(istream&, Date&);

>        static int isleap(int y)
>          {return y%4 == 0 && y%100 != 0 || y%400 == 0;}
>     };

>     // End of File

> #### Listing 13 Final implementation of the Date class

>     // date8.cpp

>     #include <iostream.h>
>     #include <time.h>
>     #include <assert.h>
>     #include "date8.h"

>     // Must initialize statics outside the class definition
>     int Date::dtab[2][13] =
>     {
>       {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
>       {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
>     };

>     Date Date::operator-(const Date& d2) const
>     {
>        int months, days, years, prev_month, order;
>        const Date * first, * last;

>        // Must know which date is first
>        if (compare(d2) <= 0)
>        {
>           // this <= d2
>           order= -1;
>           first = this;
>           last = &d2;
>        }
>        else
>        {
>           order = 1;
>           first = &d2;
>           last = this;
>        }

>        // Compute the interval; first <= last
>        years = last->year - first->year;
>        months = last->month - first->month;
>        days = last->day - first->day;
>        assert(years >= 0 && months >= 0 && days >= 0);

>        // Do obvious corrections (days before months!)
>        //
>        // This is a loop in case the previous month is
>        // February, and days < -28.
>        prev_month = last->month - 1;
>        while (days < 0)
>        {
>           // Borrow from the previous month
>           if (prev_month == 0)
>              prev_month = 12;
>           -months;
>           days += dtab[isleap(last->year)][prev_month-];
>        }

>        if (months < 0)
>        {
>           // Borrow from the previous year
>           -years;
>           months += 12;
>        }

>        // Return a date object with the interval
>        if (order == 1)
>           return Date(-months,-days,-years);
>        else
>           return Date(months,days,years);
>     }

>     int Date::compare(const Date& d2) const
>     {
>        int months, days, years, order;

>        years = year - d2.year;
>        months = month - d2.month;
>        days = day - d2.day;

>        // return <0, 0, or >0, like strcmp()
>        if (years == 0 && months == 0 && days == 0)
>           return 0;
>        else if (years == 0 && months == 0)
>           return days;
>        else if (years == 0)
>           return months;
>        else
>           return years;
>     }

>     ostream& operator<<(ostream& os, const Date& d)
>     {
>        os << d.month << '/' << d.day << '/' << d.year;
>        return os;
>     }

>     istream& operator>>(istream& is, Date& d)
>     {
>        char slash;
>        is >> d.month >> slash >> d.day >> slash >> d.year;
>        return is;
>     }

>     Date::Date()
>     (
>        // Get today's date
>        time_t tval = time(0);
>        struct tm *tmp= localtime(&tval);

>        month = tmp->tm_mon+1;
>        day = tmp->tm_mday;
>        year = tmp->tm_year + 1900;
>     }
>     // End of File

> #### Listing 14 Gets today's date

>     // tdate8.cpp:

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

>     main()
>     {
>        Date today, d2;
>        cout << "Today's date is "<< today << endl;
>        cout << "Enter another date: ";
>        cin >> d2;
>        cout << "today - d2 = "<< today - d2 << endl;
>        cout << "d2 - today = "<< d2 - today << endl;
>        return 0;
>     }

>     /* OUTPUT
>     Today's date is 12/12/1992
>     Enter another date: 1/1/1970
>     today - d2 = 11/11/22
>     d2 - today = -11/-11/-22
>     */
>     // End of File