What is the algorithm that, given a day, month and year, returns a day of the week?
This can be done using the std::mktime and std::localtime functions. These functions are not just POSIX, they are mandated by the C++ Standard (C++03 ยง20.5).
#include <ctime>
std::tm time_in = { 0, 0, 0, // second, minute, hour
4, 9, 1984 - 1900 }; // 1-based day, 0-based month, year since 1900
std::time_t time_temp = std::mktime( & time_in );
// the return value from localtime is a static global - do not call
// this function from more than one thread!
std::tm const *time_out = std::localtime( & time_temp );
std::cout << "I was born on (Sunday = 0) D.O.W. " << time_out->tm_wday << '\n';
You need a starting point. Today is fine. Hard-code it.
Then, you need to represent the number of days in a month. This is 31, 28, 31, 30, 31, 30, ... . So you can start adding and subtracting 365 % 7 to the day of the week for each year, and (sum of days in difference of month) % 7 again. And so on.
The caveat: Leap years occur on every 4th year, but not every 100th, unless that year is also a multiple of 400.
One of the easiest algorithm for this is Tomohiko Sakamoto Algorithm:
static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
y -= m < 3;
return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
Check this out: https://iq.opengenus.org/tomohiko-sakamoto-algorithm/
I found Wang's method also interesting
w = (d - d^(m) + y^ - y* + [y^/4 - y*/2] - 2( c mod 4)) mod 7
http://rmm.ludus-opuscula.org/PDF_Files/Wang_Day_5_8(3_2015)_high.pdf This pdf is really helpful too.
Thanks!
Related
This question already has answers here:
bool to int conversion
(4 answers)
Closed 1 year ago.
I have recently come across a function which calculates the day of the week for any given date. The function is shown below.
unsigned int getDayOfWeek(const unsigned int day, const unsigned int month, unsigned int year)
{
static unsigned int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
year -= month < 3;
return ( year + year/4 - year/100 + year/400 + t[month-1] + day) % 7;
}
I am having trouble understanding the syntax of year -= month < 3. I am assuming it expands to year = year - (month < 3), however I still cannot understand what this does.
My question is: what does this syntax do in general, not just in the context of this function? For example a -= b < 3.
Thank you in advance.
month < 3 is a boolean expression.
false converts to 0
true converts to 1.
You might rewrite it as:
if (month < 3) { year = year - 1; }
I'm doing a practice exercise. It asks me to create a calendar of the month, year which is the current user time. I have looked up some code on the Internet, it works well, but I can't understand it clearly. Especially the line year -= month < 3. Can someone explain it, please?
//return the daycode of the first day of month.
int firstDayOfMonth(int month, int year) {
int dow = 0;
int day = 1;
int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
year -= month < 3; // I cannot understand this.
cout<<"This is year "<<year<<endl;
dow = ( year + year/4 - year/100 + year/400 + t[month-1] + day) % 7;
return dow;
}
int main()
{
int a;
cout<<firstDayOfMonth(2,2018)<<endl;
return 0;
}
In C++, boolean values can be implicitly converted to integers, with false becoming 0 and true becoming 1. (See bool to int conversion.)
So year -= month < 3; is equivalent to:
if (month < 3) {
year -= 1; // true -> 1
} else {
year -= 0; // false -> 0
}
which can be simplified to:
if (month < 3) {
--year;
}
The motivation is that January and February (months 1 and 2) come before any leap day, while the other months come after any leap day, so it's convenient to treat January and February as being at the end of the previous year, and let the leap day be added to the calculation for the entire March-to-February year.
This code is obviously not optimized for readability.
What that means is:
if the condition (month < 3) is true, then decrement by 1. if the condition (month < 3) is false, then decrement by 0 (year stays the same)
The value of 1 & 0 represent false & true of the month & number comparison.
I was making a function in C++ to get the day of the week given the day, month and year (from 1900 on). The way I have to do it (I'm following orders, it's an exercise) is with the modulus of 7 of the total of days passed.
For example, 21 November 2018 will be the 325th day of that year (Taking into account leap years). The day of the week will be 325 % 7, which will give a number between 0 and 6, 0 being Sunday, 1 being Monday and so on, until 6 which would be Saturday.
But this will only work in years that start on Monday. 2018 works, but 2019 will be off by 1 day, as it starts on Tuesday.
My idea of fixing this is by knowing on what day that year starts and adding it to the 0-6 number given (fixing it if it's higher than 6), but I'd have to use the function for the year before, which would do so until it reaches 1900, which would be set to Monday. It sounds terrible, and I can't figure out another way of doing it.
Thanks in advance
If you don't want to use any libraries and do it purely by calculation, here is a solution.
http://mathforum.org/dr.math/faq/faq.calendar.html (Web Archive page)
or a easy explanation video.
What you can do is convert this logic into your program and find out the day of the week.
int dayofweek(int d, int m, int y)
{
static int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };
y -= m < 3;
return ( y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
Code Source.
http://www.cplusplus.com/reference/ctime/tm/
http://www.cplusplus.com/reference/ctime/mktime/
int weakDayOfYearBegin(int year)
{
std::tm t {};
t.tm_year = year - 1900;
t.tm_mday = 1;
std::mktime(&t);
return t.tm_wday;
}
https://wandbox.org/permlink/1ZnByeurgMrEF3fA
I make a simple program to calculate the number of day between two days:
#include <stdio.h>
#include <iostream>
#include <ctime>
#include <utility>
using namespace std;
int main(){
struct tm t1 = {0,0,0,28,2,104};
struct tm t2 = {0,0,0,1,3,104};
time_t x = mktime(&t1);
time_t y = mktime(&t2);
cout << difftime(y,x)/3600/24 << endl;
}
The output is 4 however, my expected result is 1. May I know where is the problem is ?
In a struct tm, months are counted from 0 to 11 (not 1 to 12), thus 2 is March and 3 is April, you code output the number of days between March the 28th and April 1st, which is 4.
The correct version would be:
struct tm t1 = {0, 0, 0, 28, 1, 104};
struct tm t2 = {0, 0, 0, 1, 2, 104};
By the way, 2004 is a leap year and thus February had 29 days, there were two days between February the 28th and March the 1st (not one).
difftime gives you the number of seconds between 02/28/2004 00:00:00 and 03/01/2004 00:00:00 (the first day is accounted for in the difference).
To find the day (number) for a given date, I wrote below code using <ctime>:
tm time {ANY_SECOND, ANY_MINUTE, ANY_HOUR, 21, 7, 2015 - 1900};
mktime(&time); // today's date
PRINT(time.tm_wday); // prints 5 instead of 2 for Tuesday
According to the documentation, tm_wday can hold value among [0-6], where 0 is Sunday. Hence for Tuesday (today), it should print 2; but it prints 5.
Actually tm_wday gives consistent results, but with a difference of 3 days.
What is wrong here?
You got the month wrong, tm_mon is the offset since January, so July is 6. From the manpage:
tm_mon The number of months since January, in the range 0 to 11.
This outputs 2:
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(void) {
struct tm time;
memset(&time, 0, sizeof(time));
time.tm_mday = 21;
time.tm_mon = 6;
time.tm_year = 2015-1900;
mktime(&time);
printf("%d\n", time.tm_wday);
return 0;
}
Note that you should initialize the other fields to 0 with memset(3) or similar.
The reason you are getting invalid output is that you are using the wrong month. tm_mon starts at 0 and not 1. you can see tghis by using this code:
tm time {50, 50, 12, 21, 7, 2015 - 1900};
time_t epoch = mktime(&time);
printf("%s", asctime(gmtime(&epoch)));
Output:
Fri Aug 21 12:50:50 2015
Live Example