/**************************************************************************** ** COPYRIGHT (C): 1997 Cay S. Horstmann. All Rights Reserved. ** PROJECT: Practical OO Development with C++ and Java ** FILE: date.cpp ** PURPOSE: date class for chapter 3 ** VERSION 1.0 ** PROGRAMMERS: Cay Horstmann (CSH) ** RELEASE DATE: 3-15-97 (CSH) ** UPDATE HISTORY: ****************************************************************************/ #include "setup.h" #include #include EXPORT #include EXPORT class Date { public: enum Weekday { MON, TUE, WED, THU, FRI, SAT, SUN }; Date(); Date(int d, int m, int y); void advance(long n); int day() const; int month() const; int year() const; Weekday weekday() const; Date add_days(long n) const; long days_between(const Date& b) const; int compare(const Date& b) const; static int compare(const Date* a, const Date* b); static Date today(); private: bool is_valid() const; static bool is_leap(int year); int _day; int _month; int _year; static int days_per_month[12]; }; /*-------------------------------------------------------------*/ const int JULYEAR0 = -4713; // Julian day 0 = Jan. 1, -4713 int Date::days_per_month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /*-------------------------------------------------------------*/ static long dat2jul(int d, int m, int y) /* RECEIVES: d, m, y - the day, month and year RETURNS: The Julian day number that begins at noon of the given calendar date. REMARKS: Positive year signifies A.D., negative year B.C. Remember that the year after 1 B.C. was 1 A.D. A convenient reference point is that May 23, 1968 noon is Julian day 2440000. Julian day 0 is a Monday. This algorithm is from Press et al., Numerical Recipes in C, 2nd ed., Cambridge University Press 1992 */ { assert(y != 0); // there is no year 0 int jy = y; if (y < 0) jy++; int jm = m; if (m > 2) jm++; else { jy--; jm += 13; } long jul = (long) (floor(365.25 * jy) + floor(30.6001*jm) + d + 1720995.0); const long IGREG = 15 + 31L*(10+12L*1582); // Gregorian Calendar adopted Oct. 15, 1582 if (d + 31L * (m + 12L * y) >= IGREG) // change over to Gregorian calendar { int ja = (int)(0.01 * jy); jul += 2 - ja + (int)(0.25 * ja); } return jul; } /*.............................................................*/ static void jul2dat(long j, int& d, int& m, int& y) /* PURPOSE: Converts a Julian day to a calendar date RECEIVES: j - the Julian date d, m, y (OUT) - the date on which the specified Julian day started at noon. REMARKS: This algorithm is from Press et al., Numerical Recipes in C, 2nd ed., Cambridge University Press 1992 */ { long ja = j; const long JGREG = 2299161L; // the Julian date of the adoption of the Gregorian calendar if (j >= JGREG) // cross-over to Gregorian Calendar produces this correction { long jalpha = (long)(((float)(j - 1867216L) - 0.25) / 36524.25); ja += 1 + jalpha - (long)(0.25 * jalpha); } long jb = ja + 1524; long jc = (long)(6680.0 + ((float)(jb-2439870L) - 122.1)/365.25); long jd = (long)(365 * jc + (0.25 * jc)); int je = (int)((jb - jd)/30.6001); d = (int)(jb - jd - (long)(30.6001 * je)); m = je - 1; if (m > 12) m -= 12; y = (int)(jc - 4715); if (m > 2) --y; if (y <= 0) --y; } /*.............................................................*/ EXPORT istream& operator>>(istream& is, Date& date) /* REMARKS: This function expects dates in the U.S. format month/day/year */ { int d, m, y; char ch; is >> m; if (!is) return is; is >> ch; if (!is || ch != '/') { is.clear(is.rdstate() | ios::failbit); return is; } is >> d; if (!is) return is; is >> ch; if (!is || ch != '/') { is.clear(is.rdstate() | ios::failbit); return is; } is >> y; if (!is) return is; date = Date(d, m, y); return is; } /*.........................................................................*/ EXPORT ostream& operator<<(ostream& os, const Date& date) /* REMARKS: This function prints dates in the U.S. format month/day/year */ { char ch = os.fill('0'); os.width(2); os << date.month() << "/"; os.width(2); os << date.day() << "/"; os.width(4); os << date.year(); os.fill(ch); return os; } /*-------------------------------------------------------------*/ Date::Date() : _day(1), _month(1), _year(JULYEAR0) /*REMARKS: makes the date with Julian day number 0 */ {} /*.............................................................*/ Date::Date(int d, int m, int y) /* RECEIVES: d, m, y - the day, month and year */ : _day(d), _month(m), _year(y) { assert(is_valid()); } /*.............................................................*/ Date Date::add_days(long n) const /* RETURNS: The date n days away from this date */ { long j = dat2jul(_day, _month, _year); j += n; int d, m, y; jul2dat(j, d, m, y); return Date(d, m, y); } /*.............................................................*/ long Date::days_between(const Date& b) const /* RETURNS: The number of days between this date and b */ { long j = dat2jul(_day, _month, _year); long k = dat2jul(b._day, b._month, b._year); return j - k; } /*.............................................................*/ int Date::compare(const Date& b) const /* RETURNS: < 0 if this date before b 0 if this date equals b > 0 if this date after b */ { int d = _year - b._year; if (d != 0) return d; d = _month - b._month; if (d != 0) return d; return _day - b._day; } /*.............................................................*/ int Date::compare(const Date* a, const Date* b) { int d = a->_year - b->_year; if (d != 0) return d; d = a->_month - b->_month; if (d != 0) return d; return a->_day - b->_day; } /*.............................................................*/ void Date::advance(long n) { long j = dat2jul(_day, _month, _year); j += n; jul2dat(j, _day, _month, _year); } /*.............................................................*/ EXPORT inline int Date::day() const { return _day; } /*.............................................................*/ EXPORT inline int Date::month() const { return _month; } /*.............................................................*/ EXPORT inline int Date::year() const { return _year; } /*.............................................................*/ Date::Weekday Date::weekday() const { return (Weekday)(dat2jul(_day, _month, _year) % 7); } /*.............................................................*/ Date Date::today() { time_t _time; time(&_time); struct tm* t = localtime(&_time); int day = t -> tm_mday; int month = t -> tm_mon + 1; int year = t -> tm_year + 1900; return Date(day, month, year); } /*.............................................................*/ bool Date::is_leap(int year) { if (year % 4 != 0) return false; if (year < 1582) return true; if (year % 100 != 0) return true; if (year % 400 != 0) return false; return true; }; /*.............................................................*/ bool Date::is_valid() const { if (_day <= 0) return false; if (_month <= 0 || _month > 12) return false; if (_year == 0) return false; if (_month == 2 && _day == 29) return is_leap(_year); return _day <= days_per_month[_month - 1]; }