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


Satbeginner

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

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

Hi,

thanks for your reply!

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


Meanwhile I did some 'googleling' and found this page:

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

How about this?
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

Meanwhile I did some 'googleling' and found this page:

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

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

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