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