http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h
Chapter 9 // Exercise 12
Change the representation of a Date to be the number of days since January 1, 1970 (known as day 0), represented as a long int, and re-implement the functions from section 9.8. Be sure to reject dates outside the range we can represent that was (feel free to reject days before day 0, i.e no negative days).
dateclass.h
dateclass.cpp
dateclass.h
//dateclass.h
#ifndef _DATECLASS_H_
#define _DATECLASS_H_
#include "std_lib_facilities.h"
enum class Month
{
jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};
enum class Day
{
monday = 1, tuesday, wednesday, thursday, friday, saturday, sunday
};
//forward declaration
Month returnMonth(int month);
Day returnDay(int day);
//9.8 The Date Class
class Date
{
public:
Date();
~Date();
Date(int y, Month m, int d); //check for valid date & initialise
//non-modifying operations:
int day() const { return d; };
Month month() const { return m; }
int year() const { return y; }
//modifying operations:
void add_day(int n);
void add_month(int n);
void add_year(int n);
private:
int y;
Month m;
int d;
bool lastDay = false;
bool endYear = false;
};
//helper functions
bool is_date(int y, Month m, int d); //true for valid date
bool leapyear(int y); //true if y is leap year
Day findDayOfWeek(const Date& d); //returns the day for the given date
Day next_workday(const Date& d); //retrieves the next work day
int week_of_the_year(const Date& d); //returns the number of the week in the year
int howManyDaysSince(const Date& d); //returns how many days since the start of the year
long int daysSince(const Date& date1, const Date& date2); //returns how many days between two given dates
void printDay(const Day& day); //prints out day in written form
ostream& operator<<(ostream& os, Date& d);
istream& operator>>(istream& is, Date& dd);
bool operator==(const Date& a, const Date& b);
bool operator!=(const Date& a, const Date& b);
//to help with finding day for any given date
struct stringInt
{
int monthDay;
int key;
};
#endif // !_DATECLASS_H_
dateclass.cpp
//dateclass.cpp
#include "dateclass.h"
vector<stringInt> monthKey
{
{ 3, 1 },{ 4, 2 },{ 5, 3 },{ 6, 4 },{ 7, 5 },{ 8, 6 },{ 9, 7 },{ 10, 8 },{ 11, 9 },{ 12, 10 },{ 1, 11 },{ 2, 12 }
};
vector<stringInt> dayKey
{
{ 7, 0 },{ 1, 1 },{ 2, 2 },{ 3, 3 },{ 4, 4 },{ 5, 5 },{ 6, 6 }
};
//default Date Constructor
Date::Date()
{
y = 2001;
m = Month::jan;
d = 1;
}
//deconstructor
Date::~Date() {}
//Date Constructor - initialise the day
Date::Date(int y, Month m, int d)
{
//check that y m d is a valid date
if (!is_date(y, m, d))
cout << "Error, invalid date." << endl;
else
{
//if date is valid, initalise the date
Date::y = y;
Date::m = m;
Date::d = d;
}
return;
}
//increment date by n days
void Date::add_day(int n)
{
//if day goes above 31, increase month, set day to 1
//if month goes above 12, increase year, set month to 1
for (int i = 0; i < n; ++i)
{
//wrap days
if (Date::d == 31)
lastDay = true;
Date::d = (Date::d == 31) ? 1 : ++Date::d; //if day is equal to 31, make it 1, otherwise ++
if (lastDay)
{
//wrap month
lastDay = false;
int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1); //if month is equal to 12, make it 1, otherwise ++
Date::m = returnMonth(mon);
if (static_cast<int>(Date::m) == 12)
endYear = true;
if (endYear)
{
//just increase year by one
endYear = false;
++Date::y;
}
}
}
}
void Date::add_month(int n)
{
for (int i = 0; i < n; ++i)
{
//if month is equal to 12, make it 1, otherwise ++
int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1);
Date::m = returnMonth(mon);
if (static_cast<int>(Date::m) == 12)
endYear = true;
if (endYear)
{
//just increase year by one
endYear = false;
++Date::y;
}
}
}
void Date::add_year(int n)
{
for (int i = 0; i < n; ++i)
++Date::y;
}
//switch to return correct type of Month
Month returnMonth(int month)
{
switch (month)
{
case 1:
return Month::jan;
break;
case 2:
return Month::feb;
break;
case 3:
return Month::mar;
break;
case 4:
return Month::apr;
break;
case 5:
return Month::may;
break;
case 6:
return Month::jun;
break;
case 7:
return Month::jul;
break;
case 8:
return Month::aug;
break;
case 9:
return Month::sep;
break;
case 10:
return Month::oct;
break;
case 11:
return Month::nov;
break;
case 12:
return Month::dec;
break;
default:
cout << "Bad month" << endl;
}
}
//switch to return correct day
Day returnDay(int day)
{
switch (day)
{
case 7:
return Day::sunday;
break;
case 1:
return Day::monday;
break;
case 2:
return Day::tuesday;
break;
case 3:
return Day::wednesday;
break;
case 4:
return Day::thursday;
break;
case 5:
return Day::friday;
break;
case 6:
return Day::saturday;
break;
default:
cout << "Bad day" << endl;
}
}
void printDay(const Day& day)
{
switch (day)
{
case Day::sunday:
cout << "Sunday\n";
break;
case Day::monday:
cout << "Monday\n";
break;
case Day::tuesday:
cout << "Tuesday\n";
break;
case Day::wednesday:
cout << "Wednesday\n";
break;
case Day::thursday:
cout << "Thursday\n";
break;
case Day::friday:
cout << "Friday\n";
break;
case Day::saturday:
cout << "Saturday\n";
break;
default:
cout << "Bad day" << endl;
}
}
//true for valid date
bool is_date(int y, Month m, int d)
{
//check that y is valid
if (y < 1900 || y > 2018)
{
cout << "Error, invalid year." << endl;
return false;
}
//check that m is valid
if (m < Month::jan || m > Month::dec)
return false;
//check that d is valid
if (d <= 0)
return false; //d must be positive
int daysInMonth = 31; //most months have 31 days
switch (m)
{
case Month::feb:
//if leapyear, make it 29, if not make it 28
daysInMonth = (leapyear(y)) ? 29 : 28;
break;
case Month::apr: case Month::jun: case Month::sep: case Month::nov:
daysInMonth = 30; //the rest have 30 days
break;
}
if (daysInMonth < d)
return false;
return true;
}
//this was wrong in previous chapters, I've realised this now and changed it
//new version of leapyear
bool leapyear(int y)
{
//if century year, must be divisible by 400
if (y % 400 == 0)
return true;
//if normal year and divisible by 4
if (y % 4 == 0)
return true;
//else not leap year
else
return false;
}
//find day of the week for any year using Zeller's Rule
Day findDayOfWeek(const Date& d)
{
int day, month, year, century;
//get day
day = d.day();
//get key for month
for (int i = 0; i < monthKey.size(); ++i)
{
if (monthKey[i].monthDay == static_cast<int>(d.month()))
month = monthKey[i].key;
}
//get last two digits of year
year = d.year() % 100;
if (month == 11 || month == 12)
year -= 1;
//get the century - first 2 digits of year
string y = to_string(d.year());
char y1 = y.at(0);
char y2 = y.at(1);
string y3;
y3 += y1;
y3 += y2;
century = stoi(y3);
int f = d.day() + ((13 * month - 1) / 5) + year + (year / 4) + (century / 4) - (2 * century);
day = f % 7;
//get which day it is
for (int i = 0; i < dayKey.size(); ++i)
{
if (day == dayKey[i].key)
day = dayKey[i].monthDay;
}
return returnDay(day);
}
//retrieves the next work day
Day next_workday(const Date& d)
{
//find the current day
Day current = findDayOfWeek(d);
//find next day
Date nextDay(d);
nextDay.add_day(1);
Day next = findDayOfWeek(nextDay);
while (next == Day::saturday || next == Day::sunday)
{
nextDay.add_day(1);
next = findDayOfWeek(nextDay);
}
return next;
}
//returns how many days since the start of the year
int howManyDaysSince(const Date& d)
{
int day = d.day();
int month = static_cast<int>(d.month());
cout << "Month: " << month << endl;
int year = d.year();
vector<int> daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
vector<int> daysInMonthLeap = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int result = 0;
if (leapyear(year))
{
for (int i = 0; i < month - 1; ++i)
result += daysInMonthLeap[i];
result += day;
}
else
{
for (int i = 0; i < month - 1; ++i)
result += daysInMonth[i];
result += day;
}
return result;
}
//finds out what number week of the year it is
int week_of_the_year(const Date& d)
{
int daysSince1st = howManyDaysSince(d); //how many days have passed since jan 1st of given year?
cout << "Days since 1st: " << daysSince1st << endl;
int dow; //day of week. sun = 0, mon = 1 etc..
int dowJan1; //day of week on jan 1st
//find out day of week for current day
Day day = findDayOfWeek(d);
if (day == Day::sunday)
dow = 0;
else
dow = static_cast<int>(day);
//find out day of week for jan 1st of given year
day = findDayOfWeek(Date(d.year(), Month::jan, 1));
if (day == Day::sunday)
dowJan1 = 0;
else
dowJan1 = static_cast<int>(day);
//find week number
int weekNum = ((daysSince1st + 6) / 7);
if (dow < dowJan1)
++weekNum;
return weekNum;
}
//compares dates
bool operator==(const Date& a, const Date& b)
{
return a.year() == b.year()
&& a.month() == b.month()
&& a.day() == b.day();
}
bool operator!=(const Date& a, const Date& b)
{
return !(a == b);
}
//print to screen
ostream& operator<<(ostream& os, Date& d)
{
return os << d.day() << ", " << static_cast<int>(d.month()) << ", " << d.year() << endl;
}
//write into Date
istream& operator>>(istream& is, Date& dd)
{
int y, m, d;
char ch1, ch2, ch3, ch4;
is >> ch1 >> y >> ch2 >> m >> ch3 >> d >> ch4;
if (!is)
return is;
if (ch1 != '(' || ch2 != ',' || ch3 != ',' || ch4 != ')')
{
//format error
is.clear(ios_base::failbit); //set the failbit
return is;
}
dd = Date(y, Month(m), d); //update dd
return is;
}
//finds how many days have passed between two given dates
long int daysSince(const Date& date1, const Date& date2)
{
long int daysSince = 0;
Date tempDate = date1;
//get how many years have passed between two dates
int years = date2.year() - date1.year();
//add number of days to daysSince for every year
for (int i = years; i >= 1; --i)
{
//if leap year, add 366 days
if (leapyear(tempDate.year()))
{
daysSince += 366;
}
//if not leap year only add 365
else
daysSince += 365;
//increase tempDate so correct days are added
tempDate.add_year(1);
}
//when we get to current year, calculate remaining days & add to total
daysSince += howManyDaysSince(date2);
return daysSince;
}
main.cpp
//main.cpp
#include "std_lib_facilities.h"
#include "dateclass.h"
int main()
{
//intialise dates
Date today(2018, Month::sep, 24);
Date zero(1970, Month::jan, 1);
//print how many days between 2 given dates, earliest date first
cout << "Days since Jan 1st 1970: " << daysSince(zero, today) << endl;
keep_window_open();
return 0;
}
Ohhh kay. It's been a while. I didn't realise I had completely forgotten about this book over summer...whoops. Anyway, I'm going to try and do an exercise a night from now on.
First things first, I realised that my implementation of leapyear() was completely wrong. I've now changed it and it's working properly now.
First things first, I realised that my implementation of leapyear() was completely wrong. I've now changed it and it's working properly now.
Now for the exercise, I didn't want to change the implementation of Date so instead I created a new function which takes two dates and works out how many days have passed since then. This new function is called daysSince(Date, Date). It takes two dates (no error checking just to keep code size down, so please put the earliest date first), then works out how many years are between the two. It then has a variable called daysSince which starts at 0. For every year either 365 or 366 (for leap years, checked using leapyear()) is added to it until we get to the current year. It then works out how many days since the start of the year using howManyDaysSince(Date) and adds that to the running total to get how many days between the two dates.
There are many sites on the internet which can work this out for you, however, they all seem to be out by a couple of days or even a month. I strongly believe my version is correct, if not, it is only out by one day. That I'm sure of.
No comments:
Post a Comment