Arduino Forum

Using Arduino => Programming Questions => Topic started by: ryemac3 on Nov 09, 2013, 06:53 pm

Title: Anyone have a sketch to calculate the day of week based on the date?
Post by: ryemac3 on Nov 09, 2013, 06:53 pm
I can't use the Time Library in my project. To do so would require an entire rewrite, and it'll mess bunch of stuff up. I just need a simple function that can calculate the day of week based on a given date.

Does anyone have a code snippet that can do that?
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: PaulS on Nov 09, 2013, 06:56 pm
There is a function in the Time library to do that. Even without using the library, you could borrow the function.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: robtillaart on Nov 09, 2013, 07:06 pm
Code: [Select]
//
//    FILE: dayOfWeek.ino
//  AUTHOR: Rob Tillaart
// VERSION: 2013-09-01
// PURPOSE: experimental day of week code (hardly tested)
//     URL:
//
// Released to the public domain
//

void setup()
{
  Serial.begin(9600);

  for (int d =1; d<30; d++)
    Serial.println(dayOfWeek(2013,9,d));
}

void loop()
{
}

#define LEAP_YEAR(Y)     ( (Y>0) && !(Y%4) && ( (Y%100) || !(Y%400) ))     // from time-lib

int dayOfWeek(uint16_t year, uint8_t month, uint8_t day)
{
  uint16_t months[] =
  {
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 // days until 1st of month
  };   

  uint32_t days = year * 365;   // days until year
  for (uint16_t i = 4; i < year; i+=4)
  {
    if (LEAP_YEAR(i)) days++;  // adjust leap years
  }

  days += months[month-1] + day;
  if ((month > 2) && LEAP_YEAR(year)) days++;
  return days % 7;
}
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: odometer on Nov 09, 2013, 09:10 pm
Just a few days ago I wrote one.

I posted it here:
http://forum.arduino.cc/index.php?topic=197253.msg1456592#msg1456592 (http://forum.arduino.cc/index.php?topic=197253.msg1456592#msg1456592)
Mine not only computes the day of the week, it also checks for bad dates.

robtillaart's function has a longer date range, but will involve thousands of modulus operations (these are slow) for any reasonable choice of date. He also did not specify which number stands for which day of the week.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: doughboy on Nov 09, 2013, 09:55 pm
+1 on using the macro that comes with Time library.

the macro is defined as (and way simpler than the two examples above)
Code: [Select]

#define dayOfWeek(_time_)  ((( _time_ / SECS_PER_DAY + 4)  % DAYS_PER_WEEK)+1) // 1 = Sunday


just call
dayOfWeek(now()) to get the day of week for the current day.

why reinvent the wheel?
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: odometer on Nov 09, 2013, 10:12 pm

just call
dayOfWeek(now()) to get the day of week for the current day.

why reinvent the wheel?

He said he can't use the Time library.

Besides, did you actually read that macro?
How does one divide a calendar date by anything? Clearly, the thing is meant to operate on a count of seconds rather than on
a calendar date.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: doughboy on Nov 09, 2013, 10:21 pm
ok, I missed the part where OP said he cannot use time library.

I'd be more curious as to why and address that.

Just the same, the time library code is better, as Paul said, OP can adapt that. Just takes a little creativity. :P

At first I could not use Time library because I need to call time function now() from that ISR, so I modified the Time library with a few lines change so I have an interrupt friendly now() function. No need to reinvent the wheel.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: odometer on Nov 10, 2013, 12:27 am

Just the same, the time library code is better,

if you don't mind doing an awful lot of arithmetic.

Mine doesn't have division, or even multiplication for that matter.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: robtillaart on Nov 10, 2013, 01:18 pm
@Odometer,
Three remarks on your interesting code

First the return value of 0 for invalid days is a good added value.  I would have chosen 255 or -1  for error prevent interference with the ISO 8601 standard DOW numbering that defines the 0 AND 7 for Sunday.

Second, I would give the interface for year an int (uint16_t) so I can put in the real year. You still can support only 2000-2099 by means of one extra subtraction +test, and declare the other years "invalid".  Would be more user friendly imho.

Checked the footprint of your code size  against mine (which I derived from time lib)
yours=2454 
mine=2674
so your code clearly wins for size AND also for speed. The price of the support for extra centuries is quite high.
(I feel challenged ;)
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: robtillaart on Nov 10, 2013, 02:12 pm
@odometer
tweaked your code to have a smaller footprint by combining double code, and use a nested switch.
The size of this variation == 2406

Code: [Select]

void setup()
{
  Serial.begin(115200);
  Serial.println("Day of Week ");

  Serial.println(calcDayOfWeekU(13,11,11));
}

void loop()
{
}
byte calcDayOfWeek(byte y, byte m, byte d)
{
  if (y > 99 || d > 31 || d == 0) return 0;

  byte w = 6 + (y + (y >> 2));  // w is 6++

  // correction for Jan. and Feb. of leap year
  if (((y & 3) == 0) && (m <= 2)) w--;  // w=5++

  w += d;  // w = 6++

  // using subtraction iso addition makes the while at end possible 1 iteration faster.
  switch (m)
  {
  case 1:
    w -= 6;
    break;
  case 2: 
    if (d > ((y & 3) ? 28 : 29)) return 0;
    w -= 3;
    break;
  case 3:
    w -= 3;
    break;
  case 5: 
    w -= 5;
    break;
  case 7:
    break;
  case 8: 
    w -= 4;
    break;
  case 10:
    w -= 6;
    break;
  case 12:
    w -= 1;
    break;
  default:
    if (d > 30) return 0;
    switch (m)
    {
    case 4: 
      break;
    case 6: 
      w -= 2;
      break;
    case 9: 
      w -= 1;
      break;
    case 11: 
      w -= 3;
      break;
    default:
      return 0;
    }
  }

  // there are only 7 days in a week, so we "cast out" sevens
  while (w > 7) w = (w >> 3) + (w & 7);
  return w;
}



Thanks for the challenge ;)
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: odometer on Nov 10, 2013, 05:16 pm
@robtillaart:
It looks like, in your version of my function, [font=Courier]w[/font] can get as low as 0, which makes the "casting out sevens" return 0 in that case. Not good.
Here is my corrected version:
Code: [Select]

void setup()
{
  Serial.begin(115200);
  Serial.println("Day of Week ");

  Serial.println(calcDayOfWeekU(13,11,11));
}

void loop()
{
}
byte calcDayOfWeek(byte y, byte m, byte d)
{
  if (y > 99 || d > 31 || d == 0) return 0;

  byte w = 6 + (y + (y >> 2));  // w is 6++

  // correction for Jan. and Feb. of leap year
  if (((y & 3) == 0) && (m <= 2)) w--;  // w=5++

  w += d;  // w = 6++

  // using subtraction iso addition makes the while at end possible 1 iteration faster.
  switch (m)
  {
  case 1:
    w++;
    break;
  case 2: 
    if (d > ((y & 3) ? 28 : 29)) return 0;
    w -= 3;
    break;
  case 3:
    w -= 3;
    break;
  case 5: 
    w -= 5;
    break;
  case 7:
    break;
  case 8: 
    w -= 4;
    break;
  case 10:
    w++;
    break;
  case 12:
    w -= 1;
    break;
  default:
    if (d > 30) return 0;
    switch (m)
    {
    case 4: 
      break;
    case 6: 
      w -= 2;
      break;
    case 9: 
      w -= 1;
      break;
    case 11: 
      w -= 3;
      break;
    default:
      return 0;
    }
  }

  // there are only 7 days in a week, so we "cast out" sevens
  while (w > 7) w = (w >> 3) + (w & 7);
  return w;
}
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: odometer on Nov 10, 2013, 06:08 pm
Here is my version of your function:
Code: [Select]

//
//    FILE: dayOfWeek.ino
//  AUTHOR: Rob Tillaart (with changes by odometer)
// VERSION: 2013-11-10
// PURPOSE: experimental day of week code (hardly tested)
//     URL:
//
// Released to the public domain
//

void setup()
{
  Serial.begin(9600);

  int y = 2013;
  for (int m = 9; m <= 12; m++) {
    for (int d = 1; d <= 31; d++) {
    {
      Serial.print(y,DEC);
      Serial.print("-");
      Serial.print(m,DEC);
      Serial.print("-");
      Serial.print(d,DEC);
      Serial.print(" ");
      Serial.println(dayOfWeek(y,m,d));
    }
    Serial.println("");
  }
}

void loop()
{
}

int dayOfWeek(uint16_t year, uint8_t month, uint8_t day)
{
  uint16_t months[] =
  {
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 // days until 1st of month
  };
 
  // sanity checks for input
  if (year > 65000) return 0; // to prevent rollover of intermediate variables
  if ((month < 1) || (month > 12)) return 0;
  if ((month == 2) && (day == 29)) { // special case leap day
    if ((year % 4) > 0) return 0;
    if (((year % 100) == 0) && ((year % 400) > 0)) return 0;
  }
  else {
    if (days == 0) return 0;
    if (days > (months[month] - months[month-1])) return 0;
  }

  uint32_t days = year * 365;   // days before given year (ignoring leap days)

  uint16_t febs = year;
  if (m > 2) febs++; // number of completed Februaries
 
  // add in the leap days
  days += ((febs + 3) / 4);
  days -= ((febs + 99) / 100);
  days += ((febs + 399) / 400);

  days += months[month-1] + day;
  // now we have day number such that 0000-01-01(Sat) is day 1
 
  return ((days + 4) % 7) + 1; // for Mon = 1 ... Sun = 7
 
  // return ((days + 5) % 7) + 1; // for Sun = 1 ... Sat = 7
}
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: robtillaart on Nov 10, 2013, 08:54 pm
Quote
It looks like, in your version of my function, w can get as low as 0, which makes the "casting out sevens" return 0 in that case. Not good. Here is my corrected version:

You're right, w can indeed become 0, thanx for fixing!

It is clear that the price for the support for the extra years is quite high at least the way my version works.
I expect a more efficient version that support  more than current century can be written.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: odometer on Nov 12, 2013, 06:13 pm
How about this?
Code: [Select]

byte calcDayOfWeek(unsigned int y, byte m, byte d)
{
  // cast out multiples of 400 from y
  y -= (2000 * (y >> 11));
  while (y >= 400) y -= 400;

  boolean leap = ((y & 3) == 0);
  if ((y == 100) || (y == 200) || (y == 300)) leap = false;

  if (d > 31 || d == 0) return 0;

  byte w = 6;
  while (y >= 100) {y -= 100; w -= 2; }

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

  // correction for Jan. and Feb. of leap year
  if (leap && (m <= 2)) w--;

  // using subtraction iso addition makes the while at end possible 1 iteration faster.
  switch (m)
  {
  case 1:
    w++;
    break;
  case 2: 
    if (d > (leap ? 29 : 28)) return 0;
    w += 4;
    break;
  case 3:
    w += 4;
    break;
  case 5: 
    w += 2;
    break;
  case 7:
    break;
  case 8: 
    w += 3;
    break;
  case 10:
    w++;
    break;
  case 12:
    w += 6;
    break;
  default:
    if (d > 30) return 0;
    switch (m)
    {
    case 4: 
      break;
    case 6: 
      w += 5;
      break;
    case 9: 
      w += 6;
      break;
    case 11: 
      w += 4;
      break;
    default:
      return 0;
    }
  }
 
  w += d;

  // there are only 7 days in a week, so we "cast out" sevens
  while (w > 7) w = (w >> 3) + (w & 7);
  return w;
}
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: robtillaart on Nov 12, 2013, 06:37 pm
It looks good!, I knew you could do it :)

I'm busy for a project right now, maybe I'll give it a try later this evening when I have a spare duino.

this test should be moved up,  maybe even add   || (m==0) to it to "fail as fast as possible".
if (d > 31 || d == 0) return 0;
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: ryemac3 on Nov 17, 2013, 07:48 pm
Hey, thanks guys. This worked a treat.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: Satbeginner on 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)
//================================================================================
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: INTP on Aug 03, 2016, 02:59 pm
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.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: Satbeginner on Aug 04, 2016, 09:30 am
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
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: jurs on Aug 04, 2016, 01:01 pm
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!
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: INTP on Aug 04, 2016, 01:51 pm
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
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: odometer on 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.
Title: Re: Anyone have a sketch to calculate the day of week based on the date?
Post by: bricoleau on 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;
}