Go Down

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

#### ryemac3

##### 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?

#### PaulS

#1
##### 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.

#### robtillaart

#2
##### 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-libint 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;}`
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### odometer

#3
##### 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
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.

#### doughboy

#4
##### 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?

#### odometer

#5
##### 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.

#### doughboy

#6
##### 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.

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.

#### odometer

#7
##### 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.

#### robtillaart

#8
##### 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
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### robtillaart

#9
##### 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
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### odometer

#10
##### 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;}`

#### odometer

#11
##### 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}`

#### robtillaart

#12
##### 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.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### odometer

#13
##### Nov 12, 2013, 06:13 pm
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;}`

#### robtillaart

#14
##### 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;
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up