Here is my implementation of a Date class.
I am interested mainly in the class implementation and design, whether or not it has any major drawbacks and inefficiencies. But I will appreciate any given recommendations, warnings and advises.
The creation of the class is only for learning purposes, production usage is not intended.
Date.h:
#pragma once
#ifndef _DATE_H_
#define _DATE_H_
#include <iostream>
#include <string>
class Date {
public:
struct BadDate;
enum Month {
jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};
private:
static std::string monthNames [12];
static bool defaultSet;
static Date defaultDate;
int _day, _month, _year;
bool leapYear(const int) const;
void fillDate(int d, Month m, int y);
void checkIllFormed();
bool isIllFormed() const;
void setVal(int&, const int, const int);
public:
static void setDefault(const int d = 1, Month = Month(1), const int y = 2000);
static void showDefault(std::ostream& os);
static const std::string monthNameByNumber(const int);
Date(
int d = (Date::defaultSet)?Date::defaultDate.day():1,
Month m = (Date::defaultSet)?Date::defaultDate.month():Month(1),
int y = (Date::defaultSet)?Date::defaultDate.year():2000
);
Date(int d, int m, int y);
Date(const Date&);
Date& operator=(const Date&);
~Date();
int day() const;
Month month() const;
int year() const;
const std::string getMonthName() const;
void setDay(const int);
void setMonth(const int);
void setYear(const int);
const Date& operator++();
const Date operator++(int);
const Date& operator--();
const Date operator--(int);
};
struct Date::BadDate {
int _day, _month, _year;
BadDate(int d, int m, int y);
};
std::ostream& operator<<(std::ostream&, const Date&);
std::ostream& operator<<(std::ostream&, const Date::BadDate&);
#endif
Date.cpp:
#include "Date.h"
Date::BadDate::BadDate(int d, int m, int y)
: _day(d), _month(m), _year(y) {
};
std::string Date::monthNames[12] = {
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
bool Date::defaultSet = true;
Date Date::defaultDate = Date(1, 1, 2000);
void Date::setDefault(const int d, Month m, const int y) {
Date::defaultDate.setDay(d);
Date::defaultDate.setMonth(m);
Date::defaultDate.setYear(y);
Date::defaultSet = true;
}
void Date::showDefault(std::ostream& os) {
os << Date::defaultDate;
}
const std::string Date::monthNameByNumber(const int n) {
return Date::monthNames[n-1];
}
Date::Date(int d, Month m, int y)
: _day(d), _month(m), _year(y) {
checkIllFormed();
}
Date::Date(int d, int m, int y)
: _day(d), _month(Month(m)), _year(y) {
checkIllFormed();
}
Date::Date(const Date& that)
: _day(that.day()), _month(that.month()), _year(that.year()) {
checkIllFormed();
}
void Date::checkIllFormed() {
if(isIllFormed()) {
BadDate bd = BadDate(day(), month(), year());
*this = Date::defaultDate;
throw bd;
}
}
Date& Date::operator=(const Date& that) {
fillDate(that.day(), that.month(), that.year());
return *this;
}
Date::~Date(void) {
}
int Date::day() const {
return _day;
}
Date::Month Date::month() const {
return Month(_month);
}
int Date::year() const {
return _year;
}
const std::string Date::getMonthName() const {
return Date::monthNameByNumber(month());
}
void Date::setDay(const int d) {
setVal(_day, d, day());
}
void Date::setMonth(const int m) {
setVal(_month, m, month());
}
void Date::setYear(const int y) {
setVal(_year, y, year());
}
void Date::setVal(int& val, const int newVal, const int prevVal) {
val = newVal;
if(isIllFormed()) {
BadDate bd = BadDate(day(), month(), year());
val = prevVal;
throw bd;
}
}
const Date& Date::operator++() {
setDay(day() + 1);
return *this;
}
const Date Date::operator++(int) {
setDay(day() + 1);
return *this;
}
const Date& Date::operator--() {
setDay(day() - 1);
return *this;
}
const Date Date::operator--(int) {
setDay(day() - 1);
return *this;
}
std::ostream& operator<<(std::ostream& os, const Date& d) {
os << d.day() << '.' << d.getMonthName() << '.' << d.year();
return os;
}
std::ostream& operator<<(std::ostream& os, const Date::BadDate& bd) {
os << bd._day << '.' << Date::monthNameByNumber(bd._month) << '.' << bd._year;
return os;
}
bool Date::leapYear(const int y) const {
/*
1.If the year is evenly divisible by 4, go to step 2. Otherwise, go to step 5.
2.If the year is evenly divisible by 100, go to step 3. Otherwise, go to step 4.
3.If the year is evenly divisible by 400, go to step 4. Otherwise, go to step 5.
4.The year is a leap year (it has 366 days).
5.The year is not a leap year (it has 365 days).
*/
if(y%4) {
return false;
}
if(y%100) {
return true;
}
if(y%400) {
return false;
}
return true;
}
void Date::fillDate(int d, Month m, int y) {
setDay(d);
setMonth(m);
setYear(y);
}
bool Date::isIllFormed() const {
const int d(day()), m(month()), y(year());
//check year
if(y < 0) {
return true;
}
//check month
if(m < 1 || 12 < m) {
return true;
}
//check day
int maxDay((7<m)?31-m%2:30+m%2);
if(m == 2) {
if(leapYear(y)) {
maxDay = 29;
} else {
maxDay = 28;
}
}
if(d<1 || maxDay<d) {
return true;
}
return false;
}
I saw this other question, but my Date
class is different and may have its own pitfalls which I am not aware of and I would like to know.