How to convert day of week from Sunday first to Monday first in c++? - c++

I have a function which returns the day of week as an integer, Sunday = 0, Monday = 1, etc. to 6. I want to convert this to Monday = 0, ..., Sunday = 6. I can not think of better way than the one listed below, but it is clumsy. Is there a better way?
if (!currTime.dayOfTheWeek()) { // if time.dow == 0
dayOfWeek = 6;
}
else {
dayOfWeek = currTime.dayOfTheWeek() - 1;
}
by the way, this is Arduino code, using RTCLib for time.

Alternative:
To map 0...6 from [Sun. to Sat.] to [Mon. to Sun.]
dayOfWeek_fromMonday = (dayOfWeek_fromSunday + 6)%7;
Say you wanted to start on Wednesday (something more interesting that a shift by 1) rather than Sunday and avoid naked magic numbers.
#define DaysWedToSun 4
#define DaysPerWeek 7
dayOfWeek_fromWed = (dayOfWeek_fromSun + DaysWedToSun)%DaysPerWeek;

Keep it simple. What are the rules? If the day is Sunday(0) we change it to 6, all others we subtract one.
int dayOfWeek = ..;
dayOfWeek = dayOfWeek == 0 ? 6 : dayOfWeek - 1;

Related

Solution doesn't work for number of days between two dates

I know this question has been asked a few times, and I'm asking again because I've got issues with existing solutions on SO.
My goal is to find number of days between 1900-01-01 and a given date. The date will be in the format as yyyy-mm-dd and the type is std::string.
The solution I've followed is https://stackoverflow.com/a/14219008/2633803
And below is my version:
std::string numberOfDaysSince1900v2(std::string aDate)
{
string year, month, day;
year = aDate.substr(0, 4);
month = aDate.substr(5, 2);
day = aDate.substr(8, 2);
struct std::tm a = { 0,0,0,1,1,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month),std::stoi(year) - 1900 };
std::time_t x = std::mktime(&a);
std::time_t y = std::mktime(&b);
double difference;
if (x != (std::time_t)(-1) && y != (std::time_t)(-1))
{
difference = std::difftime(y, x) / (60 * 60 * 24) + 36526; //36526 is number of days between 1900-01-01 and 2000-01-01
}
return std::to_string(difference);
}
It worked fine until the given date comes to 2019-01-29 and 2019-02-01. In both cases, the output is 43494. And for the whole Feb, the output is 3 days less than expected. Then, when it comes to March 2019, the output is back to normal again.
Another case is 2019-09-03, the output is 43710, whilst the expected output is 43711.
Why would this happen to these specific dates? I ran the solution step by step and closely watched the variables in the memory but couldn't explain it.
Any suggestion is appreciated. Thanks.
The month should be represented as an integer between 0 and 11, not 1 and 12.
So
struct std::tm a = { 0,0,0,1,0,100 }; /* Jan 1, 2000 */
struct std::tm b = { 0,0,0,std::stoi(day),std::stoi(month)-1,std::stoi(year) - 1900 };
I would say there are other problems with your code. You cannot reliably initialise a tm like that (the order of fields within the struct is not guaranteed). Neither does difftime necessarily return a number of seconds (which you are assuming).

How to find the date of the previous monday in C++

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 = ...

Find the date given the year, the month and the "nth" occurrance of day within the month C/C++

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;
}

Adding days to the date in c++

I am new to c++, so I am wondering whether there is some library which could help deal with dates more fluently.
I have a fairly plain task. I have a starting date in different values and I have to get what date it will be when I increase the date by a random number of days.
I figured mktime and time_t objects seams to be helpful with what I am trying to do. If they are the answer could someone give me a link to a good guide?
Boost: Boost.Date
Qt framework: QDateTime
Codeproject hosted: CTime
If you want handle date and time basically yourself: C/C++ standard
library
A day is usually 86400 seconds (except for leap seconds) . You can add that to a time_t and get a new time_t etc. Then you can use mktime & localtime to convert it to struct tm which is displayable with strftime and could be parsable with strptime
Well, there is either the Boost Date and time module. Of if your compiler is new enough there is the C++11 chrono namespace.
I just wrote my own function to add Days, Months and Years to an existing DATE class. I couldn't test it yet, but maybe it helps:
bool DATE::add(int Day, int Month, int Year){
int DaysPerMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
this -> Day += Day;
while(this -> Day > DaysPerMonth[ this-> Month ]){
if((this -> Year % 4 == 0 && this -> Year % 100 != 0) || this -> Year % 400 == 0){
DaysPerMonth[2] = 29;
}
this -> Day -= DaysPerMonth[ this-> Month ];
this -> Month++;
if(this -> Month > 12){
this -> Month = 1;
this -> Year++;
}
}
this -> Month = ( this -> Month + (Month % 12));
this -> Year = ( this -> Year + Year + (Month/12));
if((this -> Year % 4 == 0 && this -> Year % 100 != 0) || this -> Year % 400 == 0){
DaysPerMonth[2] = 29;
// check pathologic case wether date is 1 of March and added Year targets switchyear
if( this -> Day == 1 && this -> Month == 3){
this -> Day = 29;
this -> Month = 2;
}
}
if(this -> Month < 1 || this -> Month > 12 || this -> Day < 1 || this -> Day > DaysPerMonth[this->Month]){
valid = false;
cerr << "something went wrong, calculated Date is: " << this -> Day << "."<< this -> Month << "." << this -> Year << endl << flush;
return false;
}else{
return true;
}
}
A new answer for a decade-old question because times have changed and tools have gotten better.
In C++20, a date can be represented in several different succinct and useful ways.
std::chrono::sys_days
std::chrono::year_month_day
std::chrono::year_month_weekday
(this is not an exhaustive list)
Each data structure above represents a date, but has advantages and disadvantages, much like we have several container types that represent a sequence of values (vector, list, deque, etc.) with advantages and disadvantages.
For adding days to a date, sys_days is the clear best choice for a date representation. sys_days is nothing but a count of days since (or before) 1970-01-01. It is a type alias for:
time_point<system_clock, days>
where days is a std::chrono::duration:
duration<signed integer type of at least 25 bits, ratio_multiply<ratio<24>, hours::period>>
So adding days to sys_days is simply integral arithmetic under the hood.
And C++20 allows seamless conversion between {year, month, day} concepts and sys_days. So this looks like:
sys_days tp = sys_days{January/30/2022} + days{400}; // tp = 2023-03-06
Integrals can be used as inputs to the above formula. However when working with <chrono> it is best to try and stay within the strong type system of <chrono>.:
int y = 2022;
int m = 1;
int d = 30;
int num_days = 400;
sys_days tp = sys_days{month(m)/d/y} + days{num_days}; // tp = 2023-03-06
In any event, tp can be easily observed by just printing it out:
cout << tp << '\n';
Output:
2023-03-06
Other formatting options are available, and programmatic access to the values for the year, month and day are also available. It is best to keep these values within the chrono strong types year, month and day, but conversions to integral types are also available.
A portable method using the standard ctime header, is to construct an mt struct using localtime() (or gmtime() to use UTC time). Then, add the number of days to the tm_day member and convert the structure back to a time using mktime().
The tm_day member represents the day of the month, but mktime allows it to exceed the number of days in a month, and even allows it to be negative in case it is necessary to subtract days from the date, so you do not have to guard for month-wrapping.
An example:
#include <ctime>
time_t add_days(const time_t& time_value, int days) {
tm tm_value = *localtime(&time_value);
tm_value.tm_mday += days;
return mktime(&tm_value);
}
A downside of this method can be that the std libraries might not implement localtime() in a thread-safe way. This can be fixed by using localtime_r() instead, but that is less portable.

An algorithm to get the next weekday set in a bitmask

I've got this small question - given a bitmask of weekdays (e.g., Sunday = 0x01, Monday = 0x02, Tuesday = 0x04, etc...) and today's day (in a form of Sunday = 1, Monday = 2, Tuesday = 3, etc...) - what's the most elegant way to find out the next day from today, that's set in the bitmask? By elegant I mean, is there a way to do this without if/switch/etc..., because I know the non-elegant way?
Edit I probably should've mentioned (to make this more clear) that the variable holding the bitmask can have several of the days set, so for example (roughly):
uDay = Sunday | Monday;
today = Tuesday;
I need to get "Sunday"
int getNextDay(int days_mask, int today) {
if (!days_mask) return -1; // no days set
days_mask |= days_mask << 7; // duplicate days into next week
mask = 1 << (today % 7); // keep track of the day
while (!(mask & days_mask)) {
mask <<= 1;
++today;
}
return today % 7;
}
So that's just one if at the beginning and while loop. How's that?
Edit: I just realized there was a degenerate case where if the use passes today>=14 (or greater than the highest bit set) the while loop becomes infinite. The (today % 7) on line 4 fixes this case.
And if I may grouse (light-heartedly) about the other version getting the checkmark, my version only have 2 modulus calls, while the checked solution will have a minimum of 1 and a maximum of 6 modulus calls.
Also, the comment about does the function return "today" if today is set is interesting. If the function should not return today unless today is the only day in the set would require that you pre-increment today on line 3 of my solution.
You don't need any extra variables at all. The simplest idea -- start with "tomorrow", look at successive days until you find a day in the mask -- is also the most elegant to implement. The trick to doing it nicely is to think of the days as Sunday=0, Monday=1 and so on (only inside this function). Then, "today" is actually t-1 (where t is the input to the function, so it goes from 1 to 7), and "tomorrow" is (t-1+1)%7 i.e t%7, etc.
This is simple and has been tested against litb's code exhaustively, just to be sure :-)
int getNextDay(int m, int t) {
if((m&127)==0) return t; //If no day is set, return today
t=t%7; //Start with tomorrow
while((m&(1<<t))==0) t = (t+1)%7; //Try successive days
return t+1; //Change back to Sunday=1, etc.
}
Edit: If you want "next" to mean "today or later", then the "t=t%7" line should be changed to t=t-1 or --t.
I understand your question this way:
// returns t (today) if no weekday is set in the mask.
int getNextDay(int m, int t) {
int i, idx;
for(i = 0, idx=t%7; i<7 && !((1<<idx)&m); i++, idx=(idx+1)%7)
/* body empty */ ;
return (i == 7) ? t : (idx + 1);
}
// getNextDay(8|2, 2) == 4, getNextDay(64, 2) == 7
// getNextDay(128, 2) == 2
By elegant I mean, is there a way to do this without if/switch/etc...
You bet! Whether that will mean 'elegant' in any usual sense, well:
static unsigned next_day_set (unsigned today, unsigned set) {
unsigned arev = bitreverse (highest_bit_set (bitreverse ((set << 7) | set)
& (bitreverse (today) - 1)));
return ((arev >> 7) | arev) & 0x7f;
}
That's assuming you have 'elegant' functions to reverse the bits in a word and find the leftmost bit set -- see Hacker's Delight. If you represented the weekday bits in the reverse order, it'd be simpler and actually even sort of elegant for real, assuming I didn't screw up:
enum {
Sunday = 1 << 6
Monday = 1 << 5
Tuesday = 1 << 4,
/* etc */
Saturday = 1 << 0
};
static unsigned next_day_set (unsigned today, unsigned set) {
unsigned a = highest_bit_set (((set << 7) | set) & ((today << 7) - 1));
return ((a >> 7) | a) & 0x7f;
}