#ifndef __SCPP_DATE_HPP_INCLUDED__ #define __SCPP_DATE_HPP_INCLUDED__ #include <iostream> #include <string> #include "scpp_assert.hpp" #include "scpp_types.hpp" /* Date class. Features: All date arithmetic operators and comparisons are provided. Date arithmetic is implemented as an integer arithmetic. No Y2K problems -- all years must be >= 1900. Default output format is American (MM/DD/YYYY). In debug one can see the date in debugger as yyyymmdd -- just point your debugger to a yyyymmdd_ data member. No implicit type conversions are allowed. */ namespace scpp { class Date { public: // Creates an empty (invalid in terms of IsValid()) date. Date(); // Input format: "mm/dd/yyyy". explicit Date(const char* str_date); // Same as above. explicit Date(const std::string& str_date); // Date from integer in the YYYYMMDD format, e.g. Dec. 26, 2011 is 20111226. explicit Date(unsigned yyyymmdd); // Year must be 4-digit, // month is 1-based, i.e. 1 .. 12, // day is 1 .. MonthLength() <= 31 Date(unsigned year, unsigned month, unsigned day); // Returns true if the date is not empty, // as is the case when it is created by the default constructor. // Most operations on invalid date are not allowed // (will call error handler). bool IsValid() const { return date_!=0; } // Returns date in YYYYMMDD format, e.g. Dec. 26, 2011 is 20111226. unsigned AsYYYYMMDD() const; // 4-digit year. unsigned Year() const; enum { JAN=1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }; // Returns month number JAN .. DEC, i.e. 1..12. unsigned Month() const; // Day of month 1 .. MonthLength() <= 31. unsigned DayOfMonth() const; static bool IsLeap(unsigned year); typedef enum { SUN, MON, TUE, WED, THU, FRI, SAT } DayOfWeekType; // Returns day of week SUN .. SAT. DayOfWeekType DayOfWeek() const; // "Sunday", "Monday" .. "Saturday". const char* DayOfWeekStr() const; int Data() const { return date_; } typedef enum { FRMT_AMERICAN, // MM/DD/YYYY FRMT_EUROPEAN // MM.DD.YYYY // one can add formats in here if necessary. } DateOutputFormat; enum { MIN_BUFFER_SIZE=11 }; // The function prints a date into a user-provided buffer // and returns the same buffer. // Make sure the buffer size >= MIN_BUFFER_SIZE chars at least. char* AsString(char* buffer, unsigned bufLen, DateOutputFormat frmt=FRMT_AMERICAN) const; // Same as above, but C++ style. std::string AsString(DateOutputFormat frmt=FRMT_AMERICAN) const; // Returns negative int, 0 or positive int in cases of *this<d, *this==d and *this>d. int CompareTo(const Date& d) const { SCPP_TEST_ASSERT(IsValid(), "Date is not valid") SCPP_TEST_ASSERT(d.IsValid(), "Date is not valid") return date_ - d.date_; } SCPP_DEFINE_COMPARISON_OPERATORS(Date) Date& operator ++ () { ++date_; SyncDebug(); return *this; } Date operator ++ (int) { Date copy(*this); ++(*this); return copy; } Date& operator -- () { --date_; SyncDebug(); return *this; } Date operator -- (int) { Date copy(*this); --(*this); return copy; } Date& operator += (int nDays) { date_ += nDays; SyncDebug(); return *this; } Date& operator -= (int nDays) { (*this) += (-nDays); return *this; } private: int date_; // number of days from A.D., i.e. 01/01/0001 is 1. #ifdef _DEBUG int yyyymmdd_; #endif void SyncDebug() { #ifdef _DEBUG yyyymmdd_ = AsYYYYMMDD(); #endif } void SyncDebug(unsigned year, unsigned month, unsigned day) { #ifdef _DEBUG yyyymmdd_ = 10000*year + 100*month + day; #endif } // Returns month's length in days, // input: month = 1 .. 12 static unsigned MonthLength(unsigned month, unsigned year); // Returns number of calendar days before beginning of the month, // e.g. for JAN - 0, // for FEB - 31, // for MAR - 59 or 60 depending on the leap year. static unsigned NumberOfDaysBeforeMonth(unsigned month, unsigned year); }; } // namespace scpp inline std::ostream& operator<<(std::ostream& os, const scpp::Date& d) { char buffer[scpp::Date::MIN_BUFFER_SIZE]; os << d.AsString(buffer, scpp::Date::MIN_BUFFER_SIZE); return os; } inline scpp::Date operator + (const scpp::Date& d, int nDays) { scpp::Date copy(d); return (copy += nDays); } inline scpp::Date operator - (const scpp::Date& d, int nDays) { scpp::Date copy(d); return (copy -= nDays); } inline int operator - (const scpp::Date& lhs, const scpp::Date& rhs) { return lhs.Data() - rhs.Data(); } #endif // __SCPP_DATE_HPP_INCLUDED__
#include "scpp_date.hpp" #include <string.h> // strlen #include <stdlib.h> // atoi namespace scpp { Date::Date() : date_(0) { #ifdef _DEBUG yyyymmdd_ = 0; #endif } Date::Date(const char* str_date) { SCPP_ASSERT(str_date!=NULL, "Date(): string argument=0.") // must be mm/dd/yyyy, at least m/d/yyyy SCPP_TEST_ASSERT(strlen(str_date)>=8, "Bad Date input: '" << str_date << "'.") unsigned mm, dd=0, yyyy=0; mm = atoi(str_date); for(const char* p=str_date; (*p)!=' '; ++p) { if(*p=='/') { if(dd==0) dd = atoi(p+1); else { yyyy = atoi(p+1); break; } } } SCPP_TEST_ASSERT(mm!=0 && dd!=0 && yyyy!=0, "Bad Date input '" << str_date << "', must be MM/DD/YYYY."); *this = Date(yyyy, mm, dd); } Date::Date(const std::string& str) { *this = Date(str.c_str()); } Date::Date(unsigned yyyymmdd) { int yyyy = yyyymmdd / 10000; int mmdd = yyyymmdd - 10000 * yyyy; int mm = mmdd / 100; int dd = mmdd - 100 * mm; *this = Date(yyyy, mm, dd); } Date::Date(unsigned year, unsigned month, unsigned day) { SCPP_TEST_ASSERT(year>=1900, "Year must be >=1900.") SCPP_TEST_ASSERT(JAN<=month && month<=DEC, "Wrong month " << month << " must be 1..12.") #ifdef SCPP_TEST_ASSERT_ON unsigned ml = MonthLength(month, year); SCPP_TEST_ASSERT(1<=day && day<=ml, "Wrong day: " << day << " must be 1.." << ml << "."); #endif int n_years_before = year-1; date_ = 365*n_years_before + n_years_before/4 - n_years_before/100 + n_years_before/400 + day + NumberOfDaysBeforeMonth(month, year); SyncDebug(year, month, day); } unsigned Date::AsYYYYMMDD() const { unsigned y = Year(); unsigned m = Month(); unsigned d = Data() - Date(y, m, 1).Data() + 1; return y*10000 + m*100 + d; } bool Date::IsLeap(unsigned year) { if(year%4) return false; if(year%400 == 0) return true; if(year%100 == 0) return false; return true; } Date::DayOfWeekType Date::DayOfWeek() const { return (DayOfWeekType)(date_ % 7); } const char* Date::DayOfWeekStr() const { static const char* str_day_of_week[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; DayOfWeekType dow = DayOfWeek(); return str_day_of_week[(unsigned)dow]; } // static unsigned Date::MonthLength(unsigned month, unsigned year) { static int month_length[13] = { 0, 31,28,31,30,31,30,31,31,30,31,30,31 }; SCPP_TEST_ASSERT(year>=1900, "Wrong year: " << year << ", must be >=1900."); SCPP_TEST_ASSERT(JAN <= month && month <= DEC, "Wrong month " << month); if(month == FEB && IsLeap(year)) return 29; return month_length[month]; } // static unsigned Date::NumberOfDaysBeforeMonth(unsigned month, unsigned year) { static int days_before_month[12] = { 0, 31,59,90,120,151,181,212,243,273,304,334 }; SCPP_TEST_ASSERT(year>=1900, "Wrong year: " << year << ", must be >=1900."); SCPP_TEST_ASSERT(JAN <= month && month <= DEC, "Wrong month " << month); unsigned days_before = days_before_month[month - 1]; if (month >= MAR && IsLeap(year)) ++days_before; return days_before; } unsigned Date::Year() const { SCPP_TEST_ASSERT(IsValid(), "Date is not valid") unsigned y = Data() / 365; while(Date(y,1,1).Data() > Data()) --y; return y; } unsigned Date::Month() const { SCPP_TEST_ASSERT(IsValid(), "Date is not valid") unsigned y = Year(); Date endOfLastYear(y-1, DEC, 31); unsigned day = Data() - endOfLastYear.Data(); for(unsigned m=JAN; m<=DEC; ++m) { unsigned ml = MonthLength(m, y); if(day <= ml) return m; day -= ml; } SCPP_ASSERT(false, "Fatal algorith error.") return 0; } unsigned Date::DayOfMonth() const { SCPP_TEST_ASSERT(IsValid(), "Date is not valid") unsigned y = Year(); unsigned m = Month(); unsigned d = Data() - Date(y, m, 1).Data() + 1; SCPP_TEST_ASSERT(d > 0 && d <= MonthLength(m,y), "Wrong day " << d << " of month " << m << " year " << y ); return d; } char* Date::AsString(char* buffer, unsigned bufLen, DateOutputFormat frmt) const { SCPP_TEST_ASSERT(IsValid(), "Date is not valid") SCPP_TEST_ASSERT(bufLen>=MIN_BUFFER_SIZE, "Buffer is too short: " << bufLen << " must be at least " << MIN_BUFFER_SIZE) unsigned y = Year(); unsigned m = Month(); unsigned d = Data() - Date(y, m, 1).Data() + 1; switch(frmt) { case FRMT_AMERICAN: sprintf(buffer, "%02d/%02d/%04d", m, d, y); break; case FRMT_EUROPEAN: sprintf(buffer, "%02d.%02d.%4d", m, d, y); break; default: SCPP_ASSERT(false, "Wrong output format " << frmt); } return buffer; } std::string Date::AsString(DateOutputFormat frmt) const { char buffer[ 12 ]; return AsString(buffer, sizeof(buffer), frmt); } } // namespace scpp
18.191.233.43