Go Down

### Topic: Anyone have a sketch to calculate the day of week based on the date? (Read 8673 times)previous topic - next topic

#### ryemac3

#15
##### Nov 17, 2013, 07:48 pm
Hey, thanks guys. This worked a treat.

#### Satbeginner

#16
##### Aug 03, 2016, 02:38 pm
Hello odometer & robtillaart

I used your code, and it works great, but....I really would like to understand it.
So I started placing comments, but I still do not get it completely, maybe you can help me out here?

Specifically statements like:   y -= ( 2000 * (y >> 11) );

or :                                    boolean leap = ((y & 3) == 0);

or:                                     w += (y + (y >> 2));

and:                                   if (d > (leap ? 29 : 28)) return 0;

I assumed I could call the routine with YY, MM, DD (two digits max per variable), it does work, so that must be right.
BTW, I reversed the order of the three variables to DD, MM, YY, made more sense in the rest of my application.

Thanks and regards,

Satbeginner

Code: [Select]
`//================================================================================// Begin calcdayofweek( D, M, Y)//================================================================================byte calcDayOfWeek( unsigned int d, byte m, byte y )                // Call routine using DD, MM, YY, it returns the dayoftheweek, where Sun=1, Mon=2, Tue=3, Wed=4, Thu=5, Fri=6, Sat=7{  y -= ( 2000 * (y >> 11) );                                        // y = y - (2000 * (y >> 11) ) ?? convert '16' into '2016' ?? How??    while (y >= 400) { y -= 400; }                                    // while (y >= 400) { y = y - 400; }cast out multiples of 400 from the entered year (step down in steps of 400?)                                           boolean leap = ((y & 3) == 0);                                    // do we have a leap-year?? if so, leap = 'true' (year can be divided by 4)     if ((y == 100) || (y == 200) || (y == 300)) leap = false;         // check if the year is a 'century', they are NO leapyears, but any multiple of '400' IS a leapyear  if (d > 31 || d == 0) return 0;                                   // check for false dates, if so, exit in error, return '0'  byte w = 6;                                                       // temp weekday variable used to determine the weekday, starts at '6' because of .....????  while (y >= 100) {y -= 100; w -= 2; }                             // while (y >= 100) {y = y - 100; w = w - 2; } step down the year, and step down weekday too. Weekday -2 for every 100 year?  w += (y + (y >> 2));                                              // w = w + (y + (y >> 2));  // correction for Jan. and Feb. of leap year  if (leap && (m <= 2)) { w--; }                                    // if ( leap = 'true' && (m <= 2) ) { w = w - 1; }                                                                    // using substraction iso addition makes the while at end possible 1 iteration faster ???  switch (m)                                                        // find weekday in the 12 month's        {        case 1:                                                     // if January         w++;                                                        // weekday = weekday +1 ;        break;                case 2:                                                     // if February        if (d > (leap ? 29 : 28)) return 0;                         // ???? if leap AND date = 29 or 28, return 0; so exit in error, return '0'         w += 4;                                                     // weekday = weekday + 4;        break;          case 3:                                                     // if March        w += 4;                                                     // weekday = weekday + 4;        break;          case 5:                                                     // if May        w += 2;                                                     // weekday = weekday +2;        break;          case 7:                                                     // if July        break;                                                      // do nothing          case 8:                                                     // if August        w += 3;                                                     // weekday = weekday + 3;        break;         case 10:                                                    // if October        w++;                                                        // weekday = weekday + 1;        break;          case 12:                                                    // if December        w += 6;                                                     // weekday = weekday + 6;        break;          default:                                                    // here when April, June, September, November        if (d > 30) return 0;                                       // if date bigger than 30: impossible in these months, so exit with error, return '0'        switch (m)                                                  // process these other months              {              case 4:                                               // if April              break;                                                // do nothing                  case 6:                                               // if June              w += 5;                                               // weekday = weekday + 5;              break;                  case 9:                                               // if September              w += 6;                                               // weekday = weekday + 6;              break;                  case 11:                                              // if November              w += 4;                                               // weekday = weekday + 4;              break;                  default:                                              // no month's left to process, an answer must have been found before this point              return 0;                                             // so exit in error, return '0'              }        }   w += d;                                                           // weekday = weekday + date;                                                                    // there are only 7 days in a week, so we "cast out" sevens  while (w > 7) { w = ( w >> 3 ) + ( w & 7 ); }                     // while (w > 7) { w = (w >> 3) + (w & 7); }  return w;                                                         // end of routine, return the weekday, where Sun=1, Mon=2, Tue=3, Wed=4, Thu=5, Fri=6, Sat=7}//================================================================================// End calcdayofweek( D, M, Y)//================================================================================`

#### INTP

#17
##### Aug 03, 2016, 02:59 pmLast Edit: Aug 03, 2016, 03:06 pm by INTP
What is your date input for your current setup? Are you using an RTC module?

The thing giving you the date may already have info on day of week, but we have no info on your setup.

#### Satbeginner

#18
##### Aug 04, 2016, 09:30 amLast Edit: Aug 04, 2016, 10:05 am by Satbeginner
Hi,

My D,M,Y come from a GPS receiver (NEO-6), which I modified to use the available 1PPS-signal (one Pulse per Second) to build a very accurate frequency generator.
The Neo is quite sensitive, even indoors, so I am pretty sure there will not be any invalid dates send to the routine.

Like this:  http://www.arrl.org/files/file/QEX_Next_Issue/2015/Jul-Aug_2015/Marcus.pdf

https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week

where they have a real lot information on the DayOfWeek topic.

From there I got this bit of (very compact!) code that does work too:

Code: [Select]
`//================================================================================// Begin calcdayofweek( D, M, Y)//================================================================================byte   calcDayOfWeek(int d, int m, int y)                                    // 1 <= m <= 12,  y > 1752 (in the U.K.)    {                                                                        // https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week        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;                   // Sun=0, Mon=1, Tue=2, Wed=3, Thu=4, Fri=5, Sat=6    }//================================================================================// End calcdayofweek( D, M, Y)//================================================================================`

By supplying known dates to the original code by odometer & robtillaart,  I figured out that the first bit of code strips the high years, so 3116 will become 1116, later the years will be reduced by subtracting 400, etc.

So I now know what it does, but still do not understand the syntax of these code-lines.
(The shifting part, etc)

The 'magic' number '6' -I think- is a known day on 1-1-0001, so from there on the shift in days is calculated?

This new code I found on Wikipedia has a similar but different approach, and seems to work too.

Attached you find my complete code for both the GPS-disciplined generator, single-wire rotate menu encoder and GPS clock.
Here more on the 'single wire rotary encoder' : http://forum.arduino.cc/index.php?topic=414019.msg2850663#msg2850663

Un saludo,

Satbeginner

#### jurs

#19
##### Aug 04, 2016, 01:01 pmLast Edit: Aug 04, 2016, 01:13 pm by jurs
The code posted in #13 calculates wrong results in several cases.

Give it a try with this test code:
Code: [Select]
`void setup(){ Serial.begin(9600); Serial.println(calcDayOfWeek(1999,12,31));  Serial.println(calcDayOfWeek(2000,1,1));   }`

DayOfWeek is surely never 7, it has to be in the range 0 to 6 only!

#### INTP

#20
##### Aug 04, 2016, 01:51 pm

https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week

where they have a real lot information on the DayOfWeek topic.

From there I got this bit of (very compact!) code that does work too
Hmm, sounds like that should've been step one

#### odometer

#21
##### Aug 04, 2016, 03:26 pm
The code posted in #13 calculates wrong results in several cases.

Give it a try with this test code:
Code: [Select]
`void setup(){ Serial.begin(9600); Serial.println(calcDayOfWeek(1999,12,31));  Serial.println(calcDayOfWeek(2000,1,1));   }`

DayOfWeek is surely never 7, it has to be in the range 0 to 6 only!
I think I intentionally used 1 to 7 for that.

#### bricoleau

#22
##### Aug 04, 2016, 06:50 pm
Hello

Here is my tiny code with very few 8-bits calculations.
It works fine with RTCs (no century handling)

Code: [Select]
`uint8_t calcDayOfWeek(uint8_t y, uint8_t m, uint8_t d) {  uint8_t dow = y + (y >> 2) + d;  switch (m) {    case  2 :    case  3 :    case 11 : dow++;    break;    case  6 : dow += 2; break;    case  9 :    case 12 : dow += 3; break;    case  4 :    case  7 : dow += 4; break;    case  1 :    case 10 : dow += 5; break;    case  5 : dow += 6;  }  if (m > 2 || (y & 3)) dow++;  return dow % 7;}`
Tutoriels arduino : http://forum.arduino.cc/index.php?topic=398112.0

Go Up