http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h
Chapter 9 // Exercise 11
Design and implement a set of useful helper functions for the Date class with functions such as next_workday() (assume that any day that is not a Saturday or Sunday is a workday) and week_of_year() (assume that week 1 is the week with January 1 in it and that the first day of a week is Sunday).
(the above code on github is different to below as I re-did the book)
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 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; } //checks for leap year (gregorian calender) bool leapyear(int y) { //if divisible by 4 but not 100 - then leap year if (y % 4 == 0 && y % 100 != 0) return true; //if divisible by 4, 100 and 400 - then leap year if (y % 4 == 0 && y % 100 == 0 && y % 400 == 0) return true; 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; }
main.cpp
//main.cpp #include "std_lib_facilities.h" #include "dateclass.h" int main() { //intialise dates Date today(2018, Month::jun, 14); Date tomorrow(today); //increase day by one tomorrow.add_day(1); tomorrow.add_month(2); tomorrow.add_year(2); //print out results cout << "Today: " << today << endl; cout << "Tomorrow: " << tomorrow << endl; //prints todays day cout << "Today is a: "; printDay(findDayOfWeek(today)); //print next working day cout << "The next working day is: "; printDay(next_workday(today)); //print week number cout << "The week number is: " << week_of_the_year(today) << endl; keep_window_open(); return 0; }
This took much longer than I expected, mainly because I honestly (and naively) expected to be implementing just two functions. I ended up creating quite a few more than that.
First up, in order to find the next work day, the day of the week had to be figured out. We enter a number as the day, how does the date class know what day it is?
I went on a rabbit hole trying to figure this out. I eventually ended up using a formula called Zeller's Rule:
http://mathforum.org/dr.math/faq/faq.calendar.html
This had a few problems though as I needed to separate the year into the first and last two digits as well as assign keys to months and days. The keys were assigned by creating a new struct which allowed for two ints. Then the month-key pairs and day-key pairs were inputted manually. The function findDayOfWeek() returns an int, however I created another function which converts the day int into the Day enum value and returns that.
Just when I thought the hard part was over, he then wanted us to find what week number of the year it was. There are many different ways of doing this online however I eventually went with a function that figures out how many days have passed since the 1st of Jan of the given year and then a formula adapted from here:
https://stackoverflow.com/questions/274861/how-do-i-calculate-the-week-number-given-a-date
Getting the howManyDaysSince() to work properly was a bit of headache as there isn't really much out there from a C++ perspective. There was an excellent yet crazy answer from a commenter called Mmars from this site:
https://www.epochconverter.com/daynumbers
however I found this to be ridiculous to look at or even try and read. I prefer my brute force solution. I did go a bit further though than Bjarne's suggestion of assuming that the 1st of jan was a sunday.
I've purposefully left that function to take any date in as I may change it in the future to calculate how many days since any given date. However, it will need some tweaking.
First up, in order to find the next work day, the day of the week had to be figured out. We enter a number as the day, how does the date class know what day it is?
I went on a rabbit hole trying to figure this out. I eventually ended up using a formula called Zeller's Rule:
http://mathforum.org/dr.math/faq/faq.calendar.html
This had a few problems though as I needed to separate the year into the first and last two digits as well as assign keys to months and days. The keys were assigned by creating a new struct which allowed for two ints. Then the month-key pairs and day-key pairs were inputted manually. The function findDayOfWeek() returns an int, however I created another function which converts the day int into the Day enum value and returns that.
Just when I thought the hard part was over, he then wanted us to find what week number of the year it was. There are many different ways of doing this online however I eventually went with a function that figures out how many days have passed since the 1st of Jan of the given year and then a formula adapted from here:
https://stackoverflow.com/questions/274861/how-do-i-calculate-the-week-number-given-a-date
Getting the howManyDaysSince() to work properly was a bit of headache as there isn't really much out there from a C++ perspective. There was an excellent yet crazy answer from a commenter called Mmars from this site:
https://www.epochconverter.com/daynumbers
however I found this to be ridiculous to look at or even try and read. I prefer my brute force solution. I did go a bit further though than Bjarne's suggestion of assuming that the 1st of jan was a sunday.
I've purposefully left that function to take any date in as I may change it in the future to calculate how many days since any given date. However, it will need some tweaking.
i used these two functions to determine the day on given date after 1970. https://pastebin.com/LPiQvs5Q
ReplyDeletenow fed up with this date thing , moving up to next chapter. lol