Appendix J. Source Code for the file scpp_date.hpp and scpp_date.cpp

File scpp_date.hpp

#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__

File scpp_date.cpp

#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
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.67.16