I have an exercice, which I am having a little trouble with.
I must create a calculator which takes two parameters: Start date and days to add (except saturday and sunday, only business days, from monday to friday). Another thing is that the sum has to include the start date.
E.g. let's take the start day July 12th 2016, and add 8 days, which correspond to July 21th 2016 (Saturday and Sunday excluded, and Tuesday, July 21th 2016 is counted as one day).
I hope I'm clear.
I tried to code something, but it is not working.
// rStringGridEd1->IntCells[3][row] is a custom stringgrid
// and correspond to the number of days to add, j is the
// counter for the loop
while (j < rStringGridEd1->IntCells[3][row])
{
if (DayOfWeek(date) != 1 || DayOfWeek(date) !=7)
{
// if current date (TDate date = "12/07/16") is not Saturday or Sunday increment date by one day
date++;
}
else if(DayOfWeek(date) == 1)
{
//If date correspond to sunday increment the date by one and j the counter by one
date=date+1;
j++;
}
else if(DayOfWeek(date) == 7)
{
//If date correspond to saturday increment the date by two days and j the counter by one
date=date+2;
j++;
}
j++;
}
Can anyone help me, please?
Here is what Lee Painton's excellent (and up-voted) answer would look like using this free, open-source C++11/14 date library which is built on top of <chrono>:
#include "date.h"
#include <iostream>
date::year_month_day
get_end_job_date(date::year_month_day start, date::days length)
{
using namespace date;
--length;
auto w = weeks{length / days{5}};
length %= 5;
auto end = sys_days{start} + w + length;
auto wd = weekday{end};
if (wd == sat)
end += days{2};
else if (wd == sun)
end += days{1};
return end;
}
You could exercise it like this:
int
main()
{
using namespace date::literals;
std::cout << get_end_job_date(12_d/jul/2016, date::days{8}) << '\n';
}
Which outputs:
2016-07-21
This simplistic calculator has a precondition that start is not on a weekend. If that is not a desirable precondition then you could detect that prior to the computation and increment start internally by a day or two.
The date library takes care of things like the relationship between days and weeks, and how to add days to a date. It is based on very efficient (non-iterative) algorithms shown and described here.
If you aren't required to use a loop then you might want to consider refactoring your solution with a simpler calculation. Consider, for example, that every five business days automatically adds seven days to the date. Thus using the quotient and remainder of the days to add should tell you how many total days to add to your date variable without resorting to a brute force loop.
Since it's an exercise I won't get into specifics of code, but a few things to consider might be how you can figure out what day of the week you end on knowing the day that you started on. Also, if you end on a friday what happens with the weekend that immediately follows it.
Related
I want to make a program which takes no input and returns the date of the previous Monday. (I don't care about time zones. And I am only worried about Gregorian calendar). I am using date by Howard Hinnant. This is how I am doing it currently:
#include <iostream>
#include <date/date.h>
int main() {
auto todays_day = date::year_month_weekday(date::floor<date::days>(std::chrono::system_clock::now()));
auto todays_date = date::floor<date::days>(std::chrono::system_clock::now());
int offset = 0;
auto weekday = todays_day.weekday();
if(weekday == date::Tuesday)
offset = 1;
else if (weekday == date::Wednesday)
offset = 2;
else if (weekday == date::Thursday)
offset = 3;
else if (weekday == date::Friday)
offset = 4;
else if (weekday == date::Saturday)
offset = 5;
else if (weekday == date::Sunday)
offset = 6;
auto lastMonday = date::year_month_day(todays_date - date::days(offset));
std::cout << lastMonday;
}
Is there a better way to do this without boost::previous_weekday? (It's not a requirement not to use boost. I am just wondering if it is possible)
The key to understanding how to do this more simply is knowing this one fact about Howard Hinnant's date library:
weekday difference is circular (or modulo 7 if you prefer). That is, any weekday subtracted from any weekday results in a number of days in the range [0, 6]. This effectively hides the underlying encoding of weekday.
Thus there is no need to translate [Monday, Sunday] into [0, 6] (or any other encoding):
#include "date/date.h"
#include <iostream>
int
main()
{
auto todays_date = date::floor<date::days>(std::chrono::system_clock::now());
date::year_month_day lastMonday = todays_date -
(date::weekday{todays_date} - date::Monday);
std::cout << lastMonday << '\n';
}
Instead you just have to decide how many days you need to subtract from a sys_days (todays_date in this example). That number of days is today's weekday minus Monday. If today is Monday, the result is days{0}. If today is Sunday, the result is days{6}. We could just as well be talking about finding the previous Friday. The logic would not change.
Also, one can directly convert a sys_days to a weekday. No need to go though year_month_weekday.
The code in the OP's question considers the "previous Monday" to be today if today happens to be a Monday. And that is fine. That is what is desired in many "previous weekday" algorithms. And it is the logic I have coded above.
But it is also common to want the previous-weekday-algorithm to result in last week if the weekday you are seeking is today. I.e. if today is Monday, compute a week ago instead of today. That too is easily doable, and by pretty much the same algorithm. One just has to subtract a day at the beginning of the algorithm if you desire this behavior:
auto todays_date = ...
todays_date -= date::days{1};
date::year_month_day lastMonday = ...
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have a program that is getting a date from some protocol, in format of DD/MM/YYYY.
The Problem is that I need to know the info of that day(day of the week, day in the year...) and I don't know how to do it.
Usually, when I want to get a day info, I am using time(time_t*) and convert the result to tm struct using localltime_r(tm*, time_t*) and then I have everything i need.
But in this case, this is not the current time(so I can not use time(time_t*)) and I don't have nothing except the date.
I can create a new tm struct and fill only tm_year, tm_mon, tm_mday and use mktime(tm*), but I am not sure if this will give me the right detail's of the desired date.
You might consider using Howard Hinnant's free, open-source date/time library. It works with C++11 and later, ported to gcc, clang and VS. It has been accepted into the C++20 draft specification.
Example code:
#include "date/date.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace date;
istringstream in{"09/07/2018"};
sys_days sd;
in >> parse("%d/%m/%Y", sd);
cout << "Day of the week is " << weekday{sd} << '\n';
cout << "Day of the year is " << sd - sys_days{year_month_day{sd}.year()/1/0} << '\n';
}
Output:
Day of the week is Mon
Day of the year is 190d
If you would rather do the computations yourself, here are extremely efficient non-iterative public domain algorithms for calendrical computations. The library referenced above is nothing but a type-safe wrapper around these algorithms with more pleasant syntax.
In case you wanted to get write methods which get the day of week/year as opposed to using a library, here's what I would do.
You're going to need to take leap years into account. For any given year, determine whether or not it is a leap year. Something like this would work (using a formula found here: https://en.wikipedia.org/wiki/Leap_year#Algorithm:
bool isLeapYear(short year)
{
bool leapYear = false;
if (((year % 4 == 0 && year % 100 != 0)) || (year % 400 == 0))
leapYear = true;
return leapYear;
}
From this, calculating the day of the year is straightforward. Simply add up all the days for each month, if it is a leap year, add 29 days onto your tally for day of year if you come across February.
As for finding the day of the week, it really helps if you start with some lower bound for the year (in this case LOWYEAR = 1760) and start with the first day of that year (STARTDAYOFWEEK = 2). Each day of the week is corresponds to a number (0-6) where Sunday is 0, Monday is 1, etc.
int DayOfWeek(void)
{
//jan 1 1760 was tuesday (2)
short dayCount = STARTDAYOFWEEK;
for (unsigned i = LOWYEAR; i < year; i++)
{
if (isLeapYear(i))
dayCount += 2;
else
dayCount++;
}
return (dayCount + dayOfYear) - 1) % DAYSINWEEK;
}
Finding the day of week is now really easy, after calculating dayCount, the day of week is found by adding dayCount with dayOfYear and modding by DAYSINWEEK (6).
The resulting number will correspond to the day of week (0-6).
I was trying to make a program that calculated the fee of a membership at a swimming pool. The user would enter a date (the last time a member renewed the membership), and the program would calculate if their membership was overdue or not, using the current date.
The membership is meant to be overdue a week (or another arbitrary time period) before the start of the month they joined in a year's time. For example, if I joined in February 2016, I would have to pay on January 24 2017 or before to make sure the membership is overdue. As soon as it gets to January 25, a month fee should be charged ($15) and as soon as it reaches February 25, two months fee should be charged etc.
However, I do not know how to charge for subsequent months after the first one. For example, paying on February 3 should result in one month overdue but paying on February 26 should be two months but I do not know how to do this.
How can I fix my function because it doesn't seem to work?
E.g. I entered November 15 2016 and it should return 15 since the membership was due on October 24 2017 but it returns 0.
int membershipFine(int joinDay, int joinMonth, int joinYear, int currentDay, int currentMonth, int currentYear)
{
int dueDay[12] = {25, 22, 25, 24, 25, 24, 25, 25, 24, 25, 24, 25}; // the week before the end of each month in days
int correspondingMonth = joinMonth - 2; // finds the element position that corresponds
if (correspondingMonth == -1) // if they joined in january, the array will go to december
{
correspondingMonth = 11;
}
int differenceInMonths = currentMonth - joinMonth + 12 * (currentYear - joinYear);
if (differenceInMonths < 11)
{
return 0;
}
else if ((differenceInMonths == 11) && (joinDay < dueDay[correspondingMonth]))
{
return 0;
}
else if (differenceInMonths == 11)
{
return 15;
}
if (differenceInMonths > 11 && joinDay < dueDay[correspondingMonth]) // not sure about this if and else statement
{
return (differenceInMonths - 11) * 15;
}
else return (differenceInMonths - 10) * 15;
}
The best way to deal with dates and times is to use a library that raises the level of abstraction about integers, to dates and times. Howard Hinnant's free, open-source, header-only library is such a tool.
It has a {year, month, day} class called date::year_month_day that lends itself to year and month arithmetic. One could use this to change the API of membershipFine from taking 6 type-unsafe parameters to just two type-safe parameters:
int
membershipFine(date::year_month_day joinDate, date::year_month_day currentDate);
Your description of the due date appears to say that it is independent of the day of the month of the join date, and that it is 1 year, less 1 week from the first of the month of the join date. If this is true, this can be easily computed like this:
using namespace date;
year_month_day dueDate{
local_days{(joinDate.year()/joinDate.month() + years{1})/1} - weeks{1}};
The expression joinDate.year()/joinDate.month() creates a year_month object, which is just a year and month, neglecting the day-of-the-month from joinDate. I add 1 year to that year_month, which results in another year_month, exactly 1 year later.
To that sum, /1 is appended. This creates a year_month_day corresponding to the first day of the month of the aforementioned year_month.
Now even though year_month_day is great for year and month oriented arithmetic, it is not so great for day and week-oriented arithmetic. The best data structure for that is a {count-of-days} from some epoch. This library has such a data structure called local_days. So I convert to that, subtract 1 week, and then convert back to year_month_day.
All of this (to compute the due date) happens in the lines of code above.
Now I need to compute the fine based on the relationship between currentDate and dueDate. The fine is $0 if currentDate < dueDate, and otherwise is a function of the number of whole months (plus 1) currentDate is beyond dueDate (as I understand your problem statement):
int fine = 0;
if (currentDate >= dueDate)
{
auto differenceInMonths = currentDate.year()/currentDate.month() -
dueDate.year()/dueDate.month();
if (currentDate.day() >= dueDate.day())
++differenceInMonths;
fine = differenceInMonths.count() * 15;
}
The difference in months, neglecting the day-of-the-month, can be computed by converting to year_month objects and subtracting. Now if currentDate.day() < dueDate.day(), this is the correct answer. For example if the difference in months is 1, but the day of the month in currentDate has not yet exceeded the day of the month in dueDate, then we don't want to charge for a second month, else we do. If we do, differenceInMonths is incremented.
Then the fine is simply the differenceInMonths, converted from months to integral, times 15.
<aside> If there are any <chrono> fans out there, the type of differenceInMonths is actually a std::chrono::duration with a period that is exactly the average month. Thus the .count() member function to access the underlying integral value.
I've added some print statements to the above code, and below I show the whole thing put together plus a driver with a few examples:
#include "date/date.h"
#include <iostream>
int
membershipFine(date::year_month_day joinDate, date::year_month_day currentDate)
{
using namespace date;
year_month_day dueDate{
local_days{(joinDate.year()/joinDate.month() + years{1})/1} - weeks{1}};
int fine = 0;
if (currentDate >= dueDate)
{
auto differenceInMonths = currentDate.year()/currentDate.month() -
dueDate.year()/dueDate.month();
if (currentDate.day() >= dueDate.day())
++differenceInMonths;
fine = differenceInMonths.count() * 15;
}
std::cout << "join Date is " << joinDate << '\n';
std::cout << "due Date is " << dueDate << '\n';
std::cout << "current Date is " << currentDate << '\n';
std::cout << "fine is $" << fine << '\n';
return fine;
}
int
main()
{
using namespace date::literals;
std::cout << membershipFine(feb/29/2016, jan/24/2017) << '\n';
std::cout << membershipFine(feb/29/2016, jan/25/2017) << '\n';
std::cout << membershipFine(feb/29/2016, feb/24/2017) << '\n';
std::cout << membershipFine(feb/29/2016, feb/25/2017) << '\n';
}
This outputs:
join Date is 2016-02-29
due Date is 2017-01-25
current Date is 2017-01-24
fine is $0
0
join Date is 2016-02-29
due Date is 2017-01-25
current Date is 2017-01-25
fine is $15
15
join Date is 2016-02-29
due Date is 2017-01-25
current Date is 2017-02-24
fine is $15
15
join Date is 2016-02-29
due Date is 2017-01-25
current Date is 2017-02-25
fine is $30
30
In summary, the use of a library such as this frees you from having to think in terms of ints so you can concentrate on the logic you have to implement in terms of dates and calendars. The result is compact and readable code that is far more likely to be correct.
Update
In the comments below the OP asks about how to parse a date from cin and how to get the current date. There are several options.
Here is how I recommend asking for a date:
date::year_month_day join;
while (true)
{
std::cout << "Enter join date as yyyy-mm-dd: ";
std::cin >> date::parse("%F", join);
if (!std::cin.fail())
break;
std::cin.clear();
std::string garbage;
std::getline(std::cin, garbage);
std::cout << "Please try again.\n";
}
If you prefer to ask for some other format, here is the complete list of parsing flags available for use.
And the OP asks how to get the current date. There are multiple answers. If you are content with the current date in UTC, that is the simplest:
using namespace std::chrono;
using namespace date;
year_month_day today = floor<days>(system_clock::now());
If you want the current date in your local time zone, you need to use "date/tz.h" (requires some installation) and this syntax:
year_month_day today{floor<days>(make_zoned(current_zone(),
system_clock::now()).get_local_time())};
If you want the current date in some time zone other than your current local time zone, that can be done with:
year_month_day today{floor<days>(make_zoned("America/Los_Angeles",
system_clock::now()).get_local_time())};
No matter how you parse your join and today, they can be used like this:
std::cout << membershipFine(join, today) << '\n';
My idea is to construct a string array with all the holidays. Then compare the input date with the array. Seems it quite inconvenient that have to type in all the weekend's dates etc. by hands. Any better ideas?
Using Howard Hinnant's free, open source C++11/14 date library, here are all the weekends in 2017.
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
for (sys_days sd = jan/1/2017; sd < jan/1/2018; sd += days{1})
{
weekday wd = sd;
if (wd == sat || wd == sun)
std::cout << sd << '\n';
}
}
2017-01-01
2017-01-07
2017-01-08
2017-01-14
...
2017-12-23
2017-12-24
2017-12-30
2017-12-31
My suggestion is to first determine the day of the week of the given date, to decide it to be Sunday or Saturday.
Use this logic to determine it: https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
For the code use this link: GeeksforGeeksCode
and the compare the days, to mark it as holiday or a working day.
You can set an initial day, like Jan 1st 2017, it's Sunday. And you can calculate if other days are workday or not.
For example, you have an array called days has 7 elements, stores Monday to Sunday. Like:
std::string days[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
And when you want to know if a day is workday, let's say Jan 3rd 2017. First you can calculate difference between the day you want to calculate and initial day (which is Jan 1st 2017). In our case it is 2. Then the result is days[2 % 7 - 1] (-1 because our initial day is set to Sunday. If we set initial day as Monday, -1 is not necessary). Jan 3rd 2017 is Tuesday, that means it is a workday.
In order that a device (with limited memory) is able to manage its own timezone and daylight savings, I'm trying to calculate daylight savings triggers for 85 time zones based on a simplified description of each timezone. I have access to minimal C and C++ libraries within the device. The format of the timezone (inc. DST) description for each time zone is as follows:
UTC - the base time and date from system clock
GMTOffsetMinutes - offset from GMT with DST inactive
DSTDeltaMinutes - modifier to above with DST active (as applicable to TZ)
DSTStartMonth - month in which DST becomes active
DSTStartNthOccurranceOfDay - the nth occurrence of the day name in month
DSTDayOfWeek - Sun = 0 through to Sat = 6
DSTStartHour - hour at which DST becomes active
DSTStartMinute - minute at which DST becomes active
and corresponding EndMonth, EndNth..., EndHour, EndMinute
I have found numerous examples going the other way, i.e. starting with the date, but they involve using the modulus, keeping the remainder and dropping the quotient hence I have been unable to transpose the formula to suit my needs.
I also tried to reuse the standard "Jan = 6, Feb = 2, Mar = 2, Apr = 5, May = 0, etc. modifier table and year modifiers from the "tell me what day the 25th of June, 2067 is?" party trick and developed the following algorithm.
Date = DayOfWeek + ((NthOccuranceOfDay - 1) x 7 ) - MonthCode - YearCode
This worked for the first 6 random test dates I selected but then I started to see dates for which it failed. Is it possible that the basic algorithm is sound but I'm missing a further modifier or maybe that I'm applying the modifiers incorrectly?
Is there another solution I could utilize?
Using this open source, cross platform date library, one can write:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
year_month_day us_daylight_starts = sys_days(sun[2]/mar/2015);
year_month_day us_daylight_ends = sys_days(sun[1]/nov/2015);
std::cout << us_daylight_starts << '\n';
std::cout << us_daylight_ends << '\n';
}
which will output:
2015-03-08
2015-11-01
The formulas this library is based on are in the public domain and documented here.
The algorithms paper has very complete unit tests validating the date algorithms over a range of millions of years (a far larger range than is necessary).
Sometimes daylight savings rules are written in terms of the last weekday of a month. That is just as easily handled:
year_month_day ymd = sys_days(sun[last]/nov/2015);
std::cout << ymd << '\n'; // 2015-11-29
That formula will be off by one week (or even two weeks) if MonthCode + YearCode is greater than or equal to DayOfWeek, because in that case you will be counting NthOccurenceOfDay from a negative date.
As an alternative, with no tables, you can compute the day of week of the first of the month using, for example, Zeller's algorithm:
int NthOccurrence(int year, int month, int n, int dayOfWeek) {
// year is the current year (eg. 2015)
// month is the target month (January == 1...December == 12)
// Finds the date of the nth dayOfWeek (Sun == 0...Sat == 6)
// Adjust month and year
if (month < 3) { --year, month += 12; }
// The gregorian calendar is a 400-year cycle
year = year % 400;
// There are no leap years in years 100, 200 and 300 of the cycle.
int century = year / 100;
int leaps = year / 4 - century;
// A normal year is 52 weeks and 1 day, so the calendar advances one day.
// In a leap year, it advances two days.
int advances = year + leaps;
// This is either magic or carefully contrived,
// depending on how you look at it:
int month_offset = (13 * (month + 1)) / 5;
// From which, we can compute the day of week of the first of the month:
int first = (month_offset + advances) % 7;
// If the dayOfWeek we're looking for is at least the day we just
// computed, we just add the difference. Otherwise, we need to add 7.
// Then we just add the desired number of weeks.
int offset = dayOfWeek - first;
if (offset < 0) offset += 7;
return 1 + offset + (n - 1) * 7;
}