#include #include #include #include bool true_julian = true; using namespace std; // see the definition of main() to understand everything that this does class date { protected: int day, month, year, dow; static string mname[], dname[]; static int count; static int ljy, ljm, ljd, fgy, fgm, fgd; static int goffset; static const int fourhundreds = 400 * 365 + 100 - 4 + 1, firsthundred = 100 * 365 + 25, otherhundreds = 100 * 365 + 24, otherfours = 4 * 365 + 1, otherones = 365, di4jy = 366 + 365 * 3; public: date(); date(int y, int m, int d); date(const date & source); date(int daynumber); ~date(); void set(int y, int m, int dy); void set(int daynumber); void print(ostream & out) const; bool in_julian_period() const; bool is_last_julian() const; bool is_first_gregorian() const; void become_last_julian(); void become_first_gregorian(); int day_number() const; void increment(); void decrement(); date & operator++(); date operator++(int); date & operator--(); date operator--(int); int operator-(const date & other) const; date operator+(int days) const; bool operator<(const date & other) const; date & operator+=(int days); date & operator-=(int days); static const int January = 1, February = 2, March = 3, April = 4, May = 5, June = 6, July = 7, August = 8, September = 9, October = 10, November = 11, December = 12; static const int Sunday = 0, Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6; static string month_name(int m); static string day_name(int dow); static string stndrdth(int n); static bool julian_is_leap_year(int y); static bool gregorian_is_leap_year(int y); static bool is_leap_year(int y); static int julian_days_in(int y, int m); static int gregorian_days_in(int y, int m); static int days_in(int y, int m); static int julian_day_number(int y, int m, int d); static int gregorian_day_number(int y, int m, int d); static int day_number(int y, int m, int d); static int dn_to_dow(int daynumber); static bool in_julian_period(int y, int m, int d); static void julian_decode_day_number(int daynumber, int & y, int & m, int & d); static void gregorian_decode_day_number(int daynumber, int & y, int & m, int & d); static void decode_day_number(int daynumber, int & y, int & m, int & d); static void adjust_offset(); static void set_gregorian(); static void set_julian(); static void set_crossover_date(int jy, int jm, int jd, int gy, int gm, int gd); static void get_crossover_date(int & jy, int & jm, int & jd, int & gy, int & gm, int & gd); static void reset_crossover_date(); static int how_many(); }; int date::ljy = 1752, date::ljm = 9, date::ljd = 2, date::fgy = 1752, date::fgm = 9, date::fgd = 14; int date::goffset = 2; int date::count = 0; string date::mname[] = { "", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "Decemeber" }; string date::dname[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; string date::month_name(int m) // static { if (m < 1 || m > 12) return "BAD MONTH"; else return mname[m]; } string date::day_name(int dow) // static { if (dow < 0 || dow > 6) return "BAD DAY"; else return dname[dow]; } string date::stndrdth(int n) // static { if (n == 1 || n == 21 || n == 31) return "st"; if (n == 2 || n == 22) return "nd"; if (n == 3 || n == 23) return "rd"; return "th"; } void date::adjust_offset() // static { goffset = 0; int fgjdn = julian_day_number(fgy, fgm, fgd); int fggdn = gregorian_day_number(fgy, fgm, fgd); int ljjdn = julian_day_number(ljy, ljm, ljd); int ljgdn = gregorian_day_number(ljy, ljm, ljd); goffset = ljjdn - fggdn + 1; } void date::set_gregorian() // static { ljy = 1752; ljm = 9; ljd = 2; fgy = 1752; fgm = 9; fgd = 14; goffset = 0; } void date::set_julian() // static { ljy = 0x7FFFFFFE; ljm = 12; ljd = 31; fgy = 0x7FFFFFFF; fgm = 1; fgd = 1; goffset = 0; } void date::set_crossover_date(int jy, int jm, int jd, int gy, int gm, int gd) // static { ljy = jy; ljm = jm; ljd = jd; fgy = gy; fgm = gm; fgd = gd; adjust_offset(); } void date::get_crossover_date(int & jy, int & jm, int & jd, int & gy, int & gm, int & gd) // static { jy = ljy; jm = ljm; jd = ljd; gy = fgy; gm = fgm; gd = fgd; } void date::reset_crossover_date() // static { ljy = 1752; ljm = 9; ljd = 2; fgy = 1752; fgm = 9; fgd = 14; adjust_offset(); } bool date::in_julian_period(int y, int m, int d) // static { if (y < ljy) return true; if (y > ljy) return false; if (m < ljm) return true; if (m > ljm) return false; return d <= ljd; } bool date::is_last_julian() const { return year == ljy && month == ljm && day == ljd; } bool date::is_first_gregorian() const { return year == fgy && month == fgm && day == fgd; } void date::become_last_julian() { year = ljy; month = ljm; day = ljd; } void date::become_first_gregorian() { year = fgy; month = fgm; day = fgd; } bool date::julian_is_leap_year(int y) // static { return y % 4 == 0; } bool date::gregorian_is_leap_year(int y) // static { if (y % 400 == 0) return true; else if (y % 100 == 0) return false; else if (y % 4 == 0) return true; else return false; } bool date::is_leap_year(int y) // static { if (in_julian_period(y, 1, 1)) return julian_is_leap_year(y); else return gregorian_is_leap_year(y); } int date::julian_days_in(int y, int m) // static { if (m == 4 || m == 6 || m == 9 || m == 11) return 30; else if (m != 2) return 31; else if (julian_is_leap_year(y)) return 29; else return 28; } int date::gregorian_days_in(int y, int m) // static { if (m == 4 || m == 6 || m == 9 || m == 11) return 30; else if (m != 2) return 31; else if (gregorian_is_leap_year(y)) return 29; else return 28; } int date::days_in(int y, int m) // static { if (in_julian_period(y, m, 1)) return julian_days_in(y, m); else return gregorian_days_in(y, m); } bool date::in_julian_period() const { return in_julian_period(year, month, day); } int date::julian_day_number(int y, int m, int d) // static { int n = di4jy * (y / 4) + 1; n += 365 * (y % 4); if (y % 4 != 0) n += 1; for (int i = 1; i < m; i += 1) n += days_in(y, i); n += d - 1; return n; } int date::gregorian_day_number(int y, int m, int d) // static { int n = y * 365; n += (y + 3) / 4; n -= (y + 99) / 100; n += (y + 399) / 400; for (int i = 1; i < m; i += 1) n += days_in(y, i); n += d; return n + goffset; } int date::day_number(int y, int m, int d) // static { if (in_julian_period(y, m, d)) return julian_day_number(y, m, d); else return gregorian_day_number(y, m, d); } void date::print(ostream & out) const { out << dname[dow] << " " << day << stndrdth(day) << " " << mname[month] << " " << year; } int date::day_number() const { return date::day_number(year, month, day); } void date::gregorian_decode_day_number(int dn, int & y, int & m, int & d) // static { y = m = d = 0; int y400, y100 = 0, y4 = 0, y1 = 0; int firstfour = 4 * 365; int firstone = 366; dn -= goffset + 1; y400 = dn / fourhundreds; dn -= y400 * fourhundreds; y = y400 * 400; if (dn > firsthundred) { y100 = (dn - firsthundred) / otherhundreds + 1; dn -= firsthundred + (y100 - 1) * otherhundreds; } y += y100 * 100; if (gregorian_is_leap_year(y)) firstfour += 1; if (dn >= firstfour) { y4 = (dn - 1 - firstfour) / otherfours + 1; dn -= firstfour + (y4 - 1) * otherfours; } y += y4 * 4; if (! gregorian_is_leap_year(y)) firstone -= 1; if (dn >= firstone) { y1 = (dn - firstone) / otherones + 1; dn -= firstone + (y1 - 1) * otherones; } y += y1; for (int i = 1; i <= 12; i += 1) { int mlen = days_in(y, i); if (dn < mlen) { m = i; d = dn + 1; break; } dn -= mlen; } } void date::julian_decode_day_number(int dn, int & y, int & m, int & d) // static { int a1 = (dn - 1) / 1461; int a2 = dn - 1 - a1 * 1461; int a3 = (a2 - 1) / 365; y = a1 * 4 + a3; int a4 = a3 == 0 ? a2 + 1 : a2 - a3 * 365; d = a4; int a5; m = 1; while (m <= 12) { if (d <= date::julian_days_in(y, m)) break; d -= date::julian_days_in(y, m); m += 1; } } void date::decode_day_number(int dn, int & y, int & m, int & d) // static { julian_decode_day_number(dn, y, m, d); if (in_julian_period(y, m, d)) return; gregorian_decode_day_number(dn, y, m, d); } date::date(int y, int m, int d) { set(y, m, d); count += 1; } void date::set(int y, int m, int d) { year = y; month = m; day = d; int dn = day_number(year, month, day); dow = dn_to_dow(dn); } int date::dn_to_dow(int daynumber) // this was declared as static in the class definition { return (daynumber + 3) % 7; } void date::set(int daynumber) { decode_day_number(daynumber, year, month, day); dow = dn_to_dow(daynumber); } date::date() { time_t now = time(NULL); tm * details = localtime(& now); set(details->tm_year + 1900, details->tm_mon + 1, details->tm_mday); count += 1; } date::date(const date & source) { set(source.year, source.month, source.day); count += 1; } date::date(int daynumber) { set(daynumber); count += 1; } date::~date() { count -= 1; } ostream & operator<<(ostream & out, const date & d) { d.print(out); return out; } void date::increment() { dow = (dow + 1) % 7; if (is_last_julian()) { become_first_gregorian(); return; } day += 1; if (day > days_in(year, month)) { day = 1; month += 1; if (month == 13) { month = 1; year += 1; } } } void date::decrement() { dow = (dow + 6) % 7; if (is_first_gregorian()) { become_last_julian(); return; } day += 1; if (day > days_in(year, month)) { day = 1; month += 1; if (month == 13) { month = 1; year += 1; } } } date & date::operator++() { increment(); return * this; } date date::operator++(int dummy) { date original = * this; increment(); return original; } date & date::operator--() { decrement(); return * this; } date date::operator--(int dummy) { date original = * this; decrement(); return original; } int date::operator-(const date & other) const { return day_number() - other.day_number(); } date date::operator+(int days) const { return date(day_number() + days); } bool date::operator<(const date & other) const { if (year != other.year) return year < other.year; if (month != other.month) return month < other.month; return day < other.day; } date & date::operator+=(int days) { set(day_number() + days); return * this; } date & date::operator-=(int days) { set(day_number() - days); return * this; } int date::how_many() // static { return count; } void complete_test() { int ljy, ljm, ljd, fgy, fgm, fgd; date::set_crossover_date(1456, 3, 7, 1456, 7, 3); date::get_crossover_date(ljy, ljm, ljd, fgy, fgm, fgd); int d = 1, m = 1, y = 0, nsb = 1, y2, m2, d2; while (y < 3000) { int n = date::day_number(y, m, d); if (n != nsb) cout << "BAD!!! 1 " << y << " " << m << " " << d << ": " << n << ", should be " << nsb << "\n"; else if (y % 131 == 1 && m == 3 && d == 4) cout << "sample ok " << y << " " << setw(2) << m << " " << setw(2) << d << ": " << n << ", should be " << nsb << "\n"; date::decode_day_number(n, y2, m2, d2); if (y != y2 || m != m2 || d != d2) cout << "BAD!!! 2 " << n << " -> " << y2 << " " << m2 << " " << d2 << ", should be " << y << " " << m << " " << d << "\n"; nsb += 1; if (y == ljy && m == ljm && d == ljd) { y = fgy; m = fgm; d = fgd; } else { d += 1; if (d > date::days_in(y, m)) { m += 1; d = 1; if (m == 13) { y += 1; m = 1; } } } } cout << "\n"; for (int n1 = 1; n1 <= 1077896; n1 += 1) { date::decode_day_number(n1, y, m, d); int n2 = date::day_number(y, m, d); if (n1 != n2) cout << "BAD!!! 3 " << n1 << " -> " << y << " " << m << " " << d << " -> " << n2 << "\n"; else if (y % 137 == 1 && m == 9 && d == 21) cout << "sample ok " << n1 << " -> " << y << " " << m << " " << d << " -> " << n2 << "\n"; } cout << "\n"; date::reset_crossover_date(); } void test() { cout << "\n"; cout << "March 1990 had " << date::days_in(1990, date::March) << " days\n\n"; cout << "2000 " << (date::is_leap_year(2000) ? "was" : "was not") << " a leap year\n\n"; cout << "23rd February 2024 was day number " << date::day_number(2024, 2, 23) << " A.D.\n\n"; date today; cout << "Today is " << today << ", it is day number " << today.day_number() << " A.D.\n\n"; date other(1968, date::November, 5); cout << "The day after " << other << " is "; other.increment(); cout << other << "\n\n"; other.set(1987, date::December, 31); cout << "The day after " << other << " is "; other.increment(); cout << other << "\n"; cout << "++date gives " << ++other << "\n"; cout << "date++ gives " << other++ << "\n"; cout << "but date is now " << other << "\n\n"; cout << "365 days after " << other << " is "; other += 365; cout << other << "\n\n"; date old(1752, 9, 2); cout << old << " was followed by "; old += 1; cout << old << "\n\n"; cout << "Today is " << today - other << " days after " << other << "\n"; cout << "1000 days after " << today << " will be " << today + 1000 << "\n\n"; } int main() { date::adjust_offset(); complete_test(); test(); } /* The output from test() is: March 1990 had 31 days 2000 was a leap year 23rd February 2024 was day number 739307 A.D. Today is Tuesday 25th February 2025, it is day number 739675 A.D. The day after Tuesday 5th November 1968 is Wednesday 6th November 1968 The day after Thursday 31st Decemeber 1987 is Friday 1st January 1988 ++date gives Saturday 2nd January 1988 date++ gives Saturday 2nd January 1988 but date is now Sunday 3rd January 1988 365 days after Sunday 3rd January 1988 is Monday 2nd January 1989 Wednesday 2nd September 1752 was followed by Thursday 14th September 1752 Today is 13203 days after Monday 2nd January 1989 1000 days after Tuesday 25th February 2025 will be Monday 22nd November 2027 */