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