How to check if week number is even or odd in ANSI C? - c

I`ve a small app that returns is week even or not.
time_t now = time(0);
tm *ltm = localtime(&now);
int twin=(ltm->tm_yday/7)%2
But independently from the 1st day of the year so it returns
mon, thu, we, etc
0,1,1,1,1,1,1
in the next week
1,0,0,0,0,0,0
In the next year
mon, thu, we, etc
0,0,1,1,1,1,1
in the next week
1,1,0,0,0,0,0
and so on..
Twin- if number modulo 2 = 0
So I have to add shift to change week number in each sunday or monday. Any suggestions?

You are assuming that first week has exactly 7 days which is incorrect.
For example Jan 1st 2013 was Tuesday, so the first week is only 5 days long.
How about using strftime? Something like:
time_t now = time(0);
tm *ltm = localtime(&now);
char weekNr[3];
strftime(weekNr, sizeof(weekNr), "%W", ltm);
int isOdd = atoi(weeknr) % 2;

What you call twin, in English is usually called even.
About your question, the issue here is that you are not calculating the week number correctly: you are simply dividing by 7, and that's not enough because the start of year and the start of week vary each year.
Moreover, there are several different ways to decide which one is week 1. See for example this code, to get started.
UPDATE: Copying shamelessly from the eglibc source code:
1) The week number of the current year as a decimal number, range 00 to 53, starting with the first Sunday as the first day of week 01 (strftime("%U")):
tp->tm_yday - tp->tm_wday + 7) / 7
2) The week number of the current year as a decimal number, range 00 to 53, starting with the first Monday as the first day of week 01 (strftime("%W")):
(tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7
3) The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, where week 1 is the first week that has at least 4 days in the new year (strftime("%V")):
Well this is complicated... so you are better with the idea by #MaikuMori of using strftime``, but with"%V", and then parse the result, withatoi()`.

Related

Why the function weekOfYear(2019.12.31) returns 1?

which week in the year does this function return? Why is the return of 2019.12.31 the first week?
I have checked this function in dolphindb-help, and it says that "weekOfYear(X): For each element in X, return a number indicating which week of the year it falls in. Each week starts on Monday." So I think it is not right for the date 12.31 to return 1 right?
The weekOfYear in dolphindb is consistent with the rule of weekofyear in pandas, and the specific rules are as follows:
If December 31st falls on a Monday, Tuesday or Wednesday, it returns as week 01 of the next year;
If it falls on a Thursday, it returns as week 53 of the year just ended;
If it falls on a Friday, it returns as week 52 (or week 53 if the year just ended is a leap year);
If it falls on a Saturday or Sunday, it returns as week 52 of the year just ended.

Encoding number to date time

I have 32 bit encoded value for date and time value.I know the sample 32bit encoded value date and time but i don't know how to convert and get this value using c program or any other language or script. The sample data as below
54 FE C0 72 =(25-Oct-13 20:34:58)
55 01 DC 8B =(26-Oct-13 22:34:51)
57 01 DC 8B =(14-Apr-14 14:34:51)
42 23 8F 96 =(02-Jun-09 17:06:30)
3C F5 28 4B= (31-Mar-00 18:51:55)
3A F4 28 49 =(12-Oct-99 18:51:53)
For the above sample data i am tried using unix timestamp method but i am getting wrong date time value.The answers using unix time stamp method as below
54 FE C0 72 =(10 Mar 2015 09:59:14 GMT
55 01 DC 8B =(12 Mar 2015 18:35:55 GMT)
57 01 DC 8B =(04 Apr 2016 03:16:27 GMT)
Please give me your guidance to convert the above 32bit encoded value to correct date and time value. And please share any other methods other than unix timestamp.And i think some algorithms are encrypted inside the hex code
EDIT:
Adding examples from chat:
30067004 =24-mar-1997 07:57:56
2C067004=Tue Apr 16 23:57:56 1996
29567004=Thu Aug 31 15:57:56 1995
13567004=9-jul-90 7:57:56
17567004=15-jun-91 15:57:56
1C567004=15-aug-92 07:57:56
20567004=23-jul-93 15:57:56
24867004=15-jul-94 23:57:56
10067004=29 sep-89 15:57:56
2B067004=21-jan-96 15:57:56
00000000 = 1-Jan-1986
00030000 = 1-jan-1986
00038000 = 1-jan-1994
Let's start by looking at the first two:
54 FE C0 72 =(25-oct-13 20:34:58)
54 FE C0 78 =(25-oct-13 20:35:04)
These two dates are 6 seconds apart, and the values differ by 6. So we know that at least the last byte specifies seconds.
55 01 DC 8B =(26-oct-13 22:34:51)
55 01 E3 93 =(26-oct-13 23:04:51)
Similarly, these two are are 30 minutes (1800 seconds) apart, and the values differ by 1800. So at least the last two bytes specify seconds.
54 FE C0 78 =(25-oct-13 20:35:04)
55 01 DC 8B =(26-oct-13 22:34:51)
There's a larger range in the values, but note that the last two bytes seem to be fairly close in value. Taking DC8Bh - C078h gives us 1C13h = 7187d. That's a difference of 2 hours (7200 seconds) minus 13 seconds, which is how far apart the time portions of the two dates are. So it looks like the last two bytes specify the time. However, there are 86400 seconds in a day, and C078h = 49272d which would be closer to around 13:00:00 than 20:35:04, that and the largest value you can store in 16 bits is 65535. Also, the first two bytes differ by 3 but the dates differ by 1. Let's come back to that in a bit.
55 01 DC 8B =(26-oct-13 22:34:51)
57 01 DC 8B =(14-Apr-14 14:34:51)
Note here that the last two bytes are the same, and that the minutes and seconds are the same, although the hours differ by 8. So perhaps the last two bytes specify seconds in part of a day. Going back to the prior example, the first two bytes differed by 3 when the dates differed by 1. So perhaps the first two bytes specify an 8 hour interval. This would account for the last two bytes being the same when the time differs by 8 or 16 hours. If we take 5701h - 5501h we get 200h = 512d. Dividing by 3 we get 169 2/3 days. The two dates above differ by 170 days, and if you take the hours into account it's about 169 2/3.
So now we have dates. 5501h is the third 8-hour interval in 26-oct-13, so the start of the day is 54FFh = 21759d. Dividing by 3 gives us 7253. Counting back days, that gives us an epoch date of 1993-12-17.
Now lets go back to the time. Assuming the last two bytes are seconds in an 8-hour interval. That gives us a maximum value of 28800d. Note that this value only needs 15 bits to store. DC8Bh has the highest bit set, so let's see what we get if we mask that bit out. That gives us 5C8Bh = 23691d, and 23691 seconds is 6 hours 34 minutes 51 seconds. That matches the third and fifth examples with a difference of 8 hours.
As for the most significant bit in the third byte, my guess is that is specifies DST. All the examples have this bit set, and all the dates are when DST is active.
So to summarize:
The first two bytes divided by 3 is number of days since 1993-12-17.
The first two bytes mod 3 is the 8-hour interval in the day. Multiply this value by 28800 (i.e. seconds in 8 hours) to set the initial time in seconds.
The last two bytes with the most significant bit masked out are seconds from the start of the 8-hour interval. Add this value to the value from the prior step to get seconds from midnight.
Check the most significant bit in the third byte to set the DST flag.
EDIT:
So it seems the result this algorithm gives for 57 01 DC 8B =(14-Apr-14 14:34:51) is ahead by one day. Let's look at one of the new examples:
42 23 8F 96 =(02-Jun-09 17:06:30)
Our algorithm give a date of 30-May-09, so it's behind by 3 days. This is interesting because it differs from what we got for 25-Oct-13 and 26-Oct-13 by about 4 years. What's different is that there's a leap year in between. So perhaps this encoding is assuming all years have 366 days. If we go back to the epoch date of 1993-12-17, we see that there are 15 non-leap years from 1994 to 2013 inclusive. That give us a new epoch date of 1994-1-1, which makes more sense. So after doing the initial conversion with 1994-1-1 as the epoch, we need to count the number of non-leap years and subtract that many days.
Now let's look at this one:
3A F4 28 49 =(12-Oct-99 18:51:53)
The time is still correct, but the date is way off. Notice however that the most significant bit of byte 3 is NOT set. This seems to indicate a different epoch. The start of 12-Oct-99 is 3AF2h = 15090d. Dividing by 3 gives us 5030. Counting backward gives us an epoch of 1986-01-03. But then there's this:
00000000 = 1-Jan-1986
00030000 = 1-jan-1986
So it looks like 1986-1-1 is the epoch, but there's a special case in place for this date, so the actual epoch is 1985-12-31.
But, we're off by 3 days. If all years had 366 days, this would not be the case. It would work however if all years had 365 days. This means that for the 1985-12-31 epoch, we need to count the number of leap years and add that many days. This is the opposite of what we need to do with the 1994-1-1 epoch.
This now works for almost everything. Everything except these:
3C F5 28 4B= (31-Mar-00 18:51:55)
1C567004=15-aug-92 07:57:56
But it does work for this:
2B067004=21-jan-96 15:57:56
So it looks like this encoding does do a leap year check, but only for the current year.
Taking these changes to the algorithm and applying them to the code provided by LPs, we now have this:
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
int isleap(int year)
{
if (year % 4 != 0) return 0;
if (year % 100 != 0) return 1;
if (year % 400 != 0) return 0;
return 1;
}
int main(int argc, char *argv[])
{
// Read encoding from command line
uint32_t datetime = strtoul(argv[1],NULL,16);
uint16_t mydate = datetime >> 16;
uint16_t mytime = datetime & 0xFFFF;
int new_encoding = (mytime & 0x8000) != 0;
// Calculate days
time_t linuxSeconds = (mydate/3);
// Calculate the 8 hours on current day of date
uint8_t third_Count = mydate % 3;
// Add days from 1/1/1970, that is the base of time in linux
if (new_encoding)
{
// Days between 1970-1-1 and 1994-1-1 minus 1
linuxSeconds += 8765;
}
else
{
// Days between 1970-1-1 and 1986-1-1 minus 1
linuxSeconds += 5843;
}
// Calculate total amount of hours
linuxSeconds *= 24;
// Calculate total amount of seconds
linuxSeconds *= 3600;
// Add seconds of last 8 hours group
linuxSeconds += (mytime & 0x7FFF);
// Add alla seconds of grups of 8 hours of date
linuxSeconds += (third_Count * 28800);
// Add or subtract days depending on whether new_encoding is set
struct tm *mytm = gmtime(&linuxSeconds);
int daydiff = 0, year;
for (year = new_encoding ? 1994 : 1986; year <= mytm->tm_year + 1900; year++) {
if (year < mytm->tm_year + 1900) {
if (new_encoding) {
// remove a day for non-leap years
if (!isleap(year)) {
daydiff--;
}
} else {
// add a day for leap years unless it's the current year
if (year != (mytm->tm_year + 1900) && isleap(year)) {
daydiff++;
}
}
}
}
if (mydate < 0x0003) {
// special case for day 0
linuxSeconds += 86400;
} else {
linuxSeconds += daydiff * 86400;
}
// Print the date with actual GMT
printf(ctime(&linuxSeconds));
// Print Greenwich time
printf(asctime(gmtime(&linuxSeconds)));
return 0;
}
One caveat about this code: if it's run on a system where time_t is 32-bit, it won't be able to properly display dates after 2038. If time_t is 64-bit, those dates will display properly.
EDIT 2:
There was an issue with code 30068000 being one day ahead. There was a bug in the code when checking the current month. The tm_mon field in struct tm ranges from 0 to 11, not 1 to 12. Fixed.
EDIT 3:
So it seems the month/day check when adding/subtracting days was just plain wrong, as it was causing Feb 28 to appear twice. When I removed that, I found that the 1994 scheme was a day ahead, so it looks like it has the same special case for day 0 that the 1986 scheme has. Fixed again.
Using the perfect explanation of #dbush, below you can find a simple linux gcc compiled code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
void decrypt ( uint32_t data )
{
uint16_t mydate = data>>16;
uint16_t mytime = data&0x0000FFFF;
// Calculate days
time_t linuxSeconds = (mydate/3);
// Calculate the 8 hours on current day of date
uint8_t third_Count = mydate % 3;
// Add days from 1/1/1970, that is the base of time in linux
if (mytime&0x8000)
{
// Days from 171/1970 to 17/12/1993
linuxSeconds += 8751;
}
else
{
// Days from 171/1970 to 19/03/1984
linuxSeconds += 5846;
}
// Calculate total amount of hours
linuxSeconds *= 24;
// Calculate total amount of seconds
linuxSeconds *= 3600;
// Add seconds of last 8 hours group
linuxSeconds += (mytime&0x7FFF);
// Add alla seconds of grups of 8 hours of date
linuxSeconds += (third_Count * 28800);
// Print the date with actual GMT
// printf(ctime(&linuxSeconds));
// Print Greenwich time
printf(asctime(gmtime(&linuxSeconds)));
}
int main(int argc, char *argv[])
{
decrypt(0x54FEC072);
decrypt(0x5501DC8B);
decrypt(0x5701DC8B);
decrypt(0x42238F96);
decrypt(0x3CF5284B);
decrypt(0x3AF42849);
return 0;
}
As #rmrps pointed out the last example is out of 1 day.
OUTPUT OF THE EXAMPLE ABOVE
Fri Oct 25 20:34:58 2013
Sat Oct 26 22:34:51 2013
Tue Apr 15 14:34:51 2014
Sat May 30 17:06:30 2009
Fri Mar 31 18:51:55 2000
Tue Oct 12 18:51:53 1999
Building on dbush' excellent analysis, there still one issue regarding the days.
This can be explained by the format, instead of counting days since epoch counts years since epoch and day of year, but ignoring leap years and using a fixed 366 day year. This sets the epoch to 31 December 1993.
So from the days_since_epoch part in dbush' answer, calculate years_since_epoch = days_since_epoch / 366 and day_of_year = days_since_epoch % 366. Set the epoch at 1993-12-31, and add the lapsed time since epoch (taking leap days into account), and you'll get the correct dates from the timestamp.
The last two timestamps you added doesn't fit dbush' analysis and so seem follow a different format. Note that the bit that dbush assumed was a DST indicator is no longer set - it actually appears to be a date scheme selector rather than DST indicator.
The date part of the last two examples is simpler - it's simply days since epoch (but a different epoch - 3 January 1986).
Edit: following discussion in chat, I'm updating my code samples.
Because my implementation - and some of the results differ from dbush' implementation and results, I'm adding an explanation as well.
If the format is based on 4 hex bytes AA BB CC DD, we treat this as a big-endian 32-bit number (AA msb = bit 31, DD lsb = bit 0), the hex timestamp is decoded as follows:
bits 0-14: number of seconds into a 8-hour window
bit 15: date scheme/epoch selector (see below)
bits 31-16 mod 3: the 8-hour window of the day.
bits 31-16 div 3: an indication of days since epoch (let's call it day_count)
day_count is not the actual number of days as both schemes treats every year as having a fixed number of days. It also includes the current (partial) day, so we should remove the partial day by subtracting one from day_count. This also means that a zero day_count is probably invalid (This has been confirmed for one of the two schemes 00000000 and 00030000 yield the same date - if 00008000 and 00038000 yield the same value it also holds for the other scheme)
For each scheme, there are only two parameters that differ between the schemes, epoch and days_per_year. Given these parameters, the calculation is the same - work out the following:
years_since_epoch = day_count / days_per_year
days_since_new_year = day_count % days_per_year // whole days
Then calculate the actual number of days since epoch as days_since_new_year plus the number of days in each year since epoch, taking leap days into account.
The two schemes are selected by bit 15:
If 1, epoch is 1994-01-01 and days_per_year is 366
If 0, epoch is 1986-01-01 and days_per_year is 365
The following code decodes both timestamp formats:
#include <stdint.h>
#include <time.h>
int isLeapYear( int y )
{
if ( y % 400 == 0 ) return 1;
if ( y % 100 == 0 ) return 0;
if ( y % 4 == 0 ) return 1;
return 0;
}
time_t decodeTimestamp (uint32_t timestamp)
{
time_t result = 0;
int y, days_since_new_year, years_since_epoch, epoch_year;
int day_count = (timestamp >> 16 ) /3;
int part_of_day = (timestamp >> 16 ) %3;
int seconds_in_day = part_of_day * 8 * 3600 + ( (timestamp & 0x7FFF) % 28800 ) ;
if ( day_count > 0 )
{
--day_count; // remove current (partial) day from day_count
}
if ( ((timestamp >> 15) & 1) == 1 ) // bit 15 is date scheme
{
days_since_new_year = day_count % 366;
years_since_epoch = day_count / 366;
epoch_year = 1994;
result = 757382400; //1994-01-01
}
else
{
days_since_new_year = day_count % 365;
years_since_epoch = day_count / 365;
epoch_year = 1986;
result = 504921600;//1986-01-01
}
result += years_since_epoch * 365 *24*60*60;
for ( y = epoch_year ; y < epoch_year + years_since_epoch; ++y )
{
if ( isLeapYear( y ) )
{
result += 24 * 60 * 60;
}
}
result += days_since_new_year * 24 * 60 * 60;
result += seconds_in_day;
return result;
}
I've put this up on http://codepad.org/K4JC0zmf, which also includes a main function which test against all of the examples I've seen in this thread. The only one it falls over is C0068000, which is explained by time_t being 32 bits not 64 bits.
Edit 2: Updated dbush' implementation in the side-by-side on codepad.
I've also put both mine and dbush' (current) implementation on http://codepad.org/vMYzNM4g to see the differences.
Both methods give the same correct results, apart from the 2038 overflow case (C0068000). I expect that they would give the same result with a 64-bit time_t however, so I think both algorithms are now correct.
E393 - DC8B = 1800
23:04:51 - 22:34:51 = 1800s
(10 Mar 2015 09:59:14 GMT - 25-oct-13 20:34:58) = 43248256 seconds
Try subtracting 43248256 decimal (293EA80 hex) from the number and then use it as if it was a unix time stamp:
54FEC072h - 293EA80h = 526AD5F2h = 1382733298d = "Fri, 25 Oct 2013 20:34:58 GMT"

Converting human time to epoch Unix time returning negative number

I am currently working on some C code and I am trying to convert a human readable date into an epoch time stamp (unix timestamp). However, its always returning a negative number. I'm using struct tm and hard coding the values of the date until I get it working properly. Below is the code
struct tm t;
time_t t_of_day;
t.tm_year = 2012 - 1970;
t.tm_mon = 9;
t.tm_mday = 24;
t.tm_hour = 11;
t.tm_min = 34;
t.tm_sec = 30;
t.tm_isdst = 1;
t_of_day = mktime(&t);
printf("Epoch time stamp is: %ld\n", t_of_day);
When this code executes I get the output of -858000330.
Thanks for any help you can provide.
2012 - 1970 computes to 42. And year 1942 is before 1/1/1970. That is normal that mktime() result into a negative timestamp though.
from mktime man page:
tm_year The number of years since 1900.
change your year calculation to 2012 - 1900 and you should be fine.
Please read the man pages, prior to coming here:
Verbatim from man mktime():
The members of the tm structure are:
tm_sec The number of seconds after the minute, normally in the range 0 to 59, but can be up to 60 to allow for leap seconds.
tm_min The number of minutes after the hour, in the range 0 to 59.
tm_hour The number of hours past midnight, in the range 0 to 23.
tm_mday The day of the month, in the range 1 to 31.
tm_mon The number of months since January, in the range 0 to 11.
tm_year The number of years since 1900.
tm_wday The number of days since Sunday, in the range 0 to 6.
tm_yday The number of days since January 1, in the range 0 to 365.
tm_isdst A flag that indicates whether daylight saving time is in effect at the time described. The value is positive if daylight saving time is in effect, zero if it is not, and negative if the information is not available.
tm_year is the number of years since 1900, not 1970.

Is there something wrong with my date code?

Description: given a date after START_GREGORIAN_CALENDAR, this function returns the number of days until the next Thursday. For example, for 16 March 2011 (2011,3,16), the function will return 1, and for 17 March 2011 (2011,3,17), the function will return 7.
int daysToNextThursday (int year, int month, int day) {
int Thursday;
Thursday = 7;
return (Thursday - day);
}
The code compiles correctly, but when I input a date, e.g. 16 3 2011, I do not get the right answer. Note this is apart of a larger amount of code that I have written, which works perfectly.
Any ideas?
Yes, I have an idea. My idea is that you go back and rethink the algorithm you've selected for determining that a day is Thursday. It's dead wrong :-)
Now, like a broken clock that's right twice a day, you may find input parameters that give you the correct answer but they'll be the exception rather than the rule.
If you want to figure out when next Thursday is from a given date, C provides date and time functions for exactly that purpose:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
static char *textday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
int main (int argc, char *argv[]) {
int year, month, day, today, thursday;
struct tm *mytm;
time_t mytime;
// Get all arguments (minimal error checks).
if (argc != 3) {
printf ("Usage: next_thursday <year> <month>\n");
return -1;
}
year = atoi (argv[1]);
month = atoi (argv[2]);
// Do first fourteen days of the month.
for (day = 1; day <= 14; day++) {
Up until there, it's just getting parameters and starting the loop. The meat of the calculation is below, setting up a useful struct tm and then forcing our year, month and day into it. Then mktime will fill in the tm_wday (day of week) field for us and we can use that to figure out the number of days till Thursday.
// Make the tm structure based on date (and midday).
mytime = time (0);
mytm = localtime (&mytime);
mytm->tm_year = year - 1900;
mytm->tm_mon = month - 1;
mytm->tm_mday = day;
mytm->tm_hour = 12;
mytime = mktime (mytm);
// Output filled in fields and days till next Thursday.
today = mytm->tm_wday;
thursday = (11 - today) % 7;
if (thursday == 0)
thursday = 7;
printf ("%04d-%02d-%02d, weekday = %d (%s), days till Thu = %d\n",
mytm->tm_year + 1900, mytm->tm_mon + 1, mytm->tm_mday,
today, textday[today], thursday);
}
return 0;
}
Note that the thursday calculation is a bit of modulus trickery - it's simply used to give us the number of days based on the following table:
today thursday
------- --------
0 (sun) 4
1 (mon) 3
2 (tue) 2
3 (wed) 1
4 (thu) 7
5 (fri) 6
6 (sat) 5
If you want a more readable solution, you can use:
if (today < 4) thursday = 4 - today;
else thursday = 11 - today;
This program outputs the following for 2011-03:
2011-03-01, weekday = 2 (Tue), days till Thu = 2
2011-03-02, weekday = 3 (Wed), days till Thu = 1
2011-03-03, weekday = 4 (Thu), days till Thu = 7
2011-03-04, weekday = 5 (Fri), days till Thu = 6
2011-03-05, weekday = 6 (Sat), days till Thu = 5
2011-03-06, weekday = 0 (Sun), days till Thu = 4
2011-03-07, weekday = 1 (Mon), days till Thu = 3
2011-03-08, weekday = 2 (Tue), days till Thu = 2
2011-03-09, weekday = 3 (Wed), days till Thu = 1
2011-03-10, weekday = 4 (Thu), days till Thu = 7
2011-03-11, weekday = 5 (Fri), days till Thu = 6
2011-03-12, weekday = 6 (Sat), days till Thu = 5
2011-03-13, weekday = 0 (Sun), days till Thu = 4
2011-03-14, weekday = 1 (Mon), days till Thu = 3

Correctness of Sakamoto's algorithm to find the day of week

I am using Sakamoto's algorithm to find out the day of week from a given date.
Can anybody tell me the correctness of this algorithm? I just want this from 2000 to 2099.
The algorithm from Wikipedia is given for reference.
int dow(int y, int m, int d)
{
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;
}
Well, you can tell just by looking at it that it is correct... Assuming that the t[] array is correct, which you can verify with just 12 spot checks (one for each month using any day/year).
The y -= m < 3 is a nice trick. It creates a "virtual year" that starts on March 1 and ends on February 28 (or 29), putting the extra day (if any) at the end of the year; or rather, at the end of the previous year. So for example, virtual year 2011 began on Mar 1 and will end on February 29, while virtual year 2012 will begin on March 1 and end on the following February 28.
By putting the added day for leap years at the end of the virtual year, the rest of the expression is massively simplified.
Let's look at the sum:
(y + y/4 - y/100 + y/400 + t[m-1] + d) % 7
There are 365 days in a normal year. That is 52 weeks plus 1 day. So the day of the week shifts by one day per year, in general. That is what the y term is contributing; it adds one to the day for each year.
But every four years is a leap year. Those contribute an extra day every four years. Thanks to the use of virtual years, we can just add y/4 to the sum to count how many leap days happen in y years. (Note that this formula assumes integer division rounds down.)
But that is not quite right, because every 100 years is not a leap year. So we have to subtract off y/100.
Except that every 400 years is a leap year again. So we have to add y/400.
Finally we just add the day of the month d and an offset from a table that depends on the month (because the month boundaries within the year are fairly arbitrary).
Then take the whole thing mod 7 since that is how long a week is.
(If weeks were eight days, for example, what would change in this formula? Well, it would be mod 8, obviously. Also the y would need to be 5*y, because 365 % 8 == 5. Also the month table t[] would need adjusting. That's it.)
Incidentally, Wikipedia's statement that the calendar is "good until 9999" is totally arbitrary. This formula is good for however long we stick with the Gregorian calendar, whether that is 10 years, 100 years, 1000 years, or 1 million years.
[edit]
The above argument is essentially a proof by induction. That is, assuming that the formula works for a particular (y,m,d), you prove that it works for (y+1,m,d) and (y,m,d+1). (Where y is a "virtual year" starting March 1.) So the key question is, does the sum change by the correct amount as you move from one year to the next? With knowledge of the leap year rules, and with the "virtual year" having the extra day at year end, it trivially does.
Recently I wrote blog post about this algorithm here.
The basic idea behind algorithm is to for February and January to count day
of week from 31 Dec of the previous year. For all other months we will be counting day of week from current year 31 Dec. We do this in two
steps first we compute day of week of last day of month preceding current month m then we just add d modulo seven.
31 Dec 1 BC is Sunday that is encoded as 0, Monday is 1 etc.
So we have: 0 + y + y/4 - y/100 + y/400 this with y -= m < 3 computes
day of week of 31 Dec of current year or previous year (depending on month). Note: 365 % 7 == 1 this explains why we wrote y instead of 365*y. The last component d is obvious since we start counting day of week from previous month last day.
The last part that need to be explained are values in array, for first two values these are number of days since last year 31 Dec to start of the month % 7. For rest of the months they are negated modulo seven number of days from end of prev month to 31 Dec of the current year. In other words we are subtracting days by addition modulo 7 e.g. (a-b)%7 = (a+(7-b%7))%7.
More explanation you may find in my blog post.
This might not be a complete answer like some mentioned above, But just would like to add one thing regarding this array : 0 3 2 5 0 3 5 1 4 6 2 4
Consider months beginning from March and ending at February just like others said:
March
April
May
June
July
August
September
October
November
December
January
February
Writing January to December from above numbering style :
So consider this as an array :
int t[] = {11,12,1,2,3,4,5,6,7,8,9,10};
Now for all elements in array just do : (2.6*m - 0.2) mod 7
parse the result as integer and you will get this:
0 3 2 5 0 3 5 1 4 6 2 4
You can find this formula here : wikipedia
int dayOfWeek(int d, int m, int y){
// Months Array
int t[] = {11,12,1,2,3,4,5,6,7,8,9,10};
// Convert months array
for (int i = 0; i < 12; i++){
int ans = t[i] * 2.6 - 0.2;
t[i] = ans % 7;
}
// Continue Algo
if(m<3)
y -= 1;
int day = (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
return day;
}
this : + y/4 - y/100 + y/400 is related to leap year. The algo to check for leap year is :
Perfectly Divisible by 400 -> true
IF perfectly divisible by 100 but not by 400 -> False
Divisible by 4 -> True
perform checks on above order. Maybe that is why they subtracted y/100 and added y/4 & y/400. Yeah silly logic 😅
I know this might not be the answer, But this might help to those who find it difficult to remember/understand stuff, yeah! not all of us have high IQ levels of understanding stuff and sadly some of us can't remember stuff too, lol.
For Gregorian Calendar
int dayToWeekG(int d,int m,int y){
int i;
int t[12]={0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
//{0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
y-=m<3;
i=(y+y/4-y/100+y/400 +t[m-1]+d)%7;
return i;
}
Explanation:
See the commented array for
t[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
and compare it with a calendar of a whole year (run cal 2to generate calendar in terminal in linux/unix) notice the starting day of the week of the day for each month.
Every normal year shifting one day of the week and leap year shifting two days of the week. as (365%7)=1 and (366%7)=2
i= y+y/4-y/100+y/400
But we are should not calculate the extra day if y is a leap year for month 0 and 1
y-=m<3
but by this way we are also removing the extra day from non-leap years too. so we will fill up the gap by subtracting 1 day for every month after February.
int t[12]={0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};

Resources