arduino-libraries / RTCZero

RTC Library for SAMD21 based boards
http://arduino.cc/en/Reference/RTC
GNU Lesser General Public License v2.1
77 stars 78 forks source link

Add function to return day of week #31

Open Nikospax opened 7 years ago

Nikospax commented 7 years ago

Is there a option to find the day of the week?

GabrielNotman commented 7 years ago

It doesn't currently support querying the day of the week. However, similar to how the epoch time is calculated, you could use mktime(). http://www.cplusplus.com/reference/ctime/mktime/

drewfish commented 3 years ago

There's actually a whole wikipedia article on just this :) https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week

Here's code which I've used:

// returns 0=Sunday, etc
uint8_t getDayOfWeek() {
    uint16_t y = 2000 + rtc.getYear();
    uint16_t m = rtc.getMonth();
    uint16_t d = rtc.getDay();
    // http://stackoverflow.com/a/21235587
    return (d+=m<3?y--:y-2,23*m/9+d+4+y/4-y/100+y/400)%7;
}
Nikospax commented 3 years ago

i have use this code. Works but yours is too compact!!

hours = rtc.getHours() + GMT;
if(hours>=24)hours = hours - 24;
minutes = rtc.getMinutes();
secs = rtc.getSeconds();
year = rtc.getYear();
month = rtc.getMonth();
day = rtc.getDay();

// Calc the day of the week STARTS HERE
// Leap Year Calculation STARTS HERE
    if((fmod(year,4) == 0 && fmod(year,100) != 0) || (fmod(year,400) == 0))
    { leap = 1; }
    else 
    { leap = 0; }
// Leap Year Calculation ENDS HERE

// Calculating century
    c = year/100;

    // Calculating two digit year
    yy = fmod(year, 100);

    // Century value based on Table
    if(c == 19) { cTable = 1; }
    if(c == 20) { cTable = 0; }
    if(c == 21) { cTable = 5; }
    if(c == 22) { cTable = 3; }

    // Jan and Feb calculations affected by leap years
    if(month == 1)
    { if(leap == 1) { mTable = 6; }
      else          { mTable = 0; }}
    if(month == 2)
    { if(leap == 1) { mTable = 2; }
      else          { mTable = 3; }}
    // Other months not affected and have set values
    if(month == 10) { mTable = 0; }
    if(month == 8) { mTable = 2; }
    if(month == 3 || month == 11) { mTable = 3; }
    if(month == 4 || month == 7) { mTable = 6; }
    if(month == 5) { mTable = 1; }
    if(month == 6) { mTable = 4; }
    if(month == 9 || month== 12) { mTable = 5; }

// Enter the data into the formula
    SummedDate = day + mTable + yy + (yy/4) + cTable;

    // Find remainder
    DoW = fmod(SummedDate,7);  

    // Output result
    // Remainder determines day of the week

// Calc the day of the week ENDS HERE
rtek1000 commented 7 months ago

Hi, I found this code:

https://www.hackster.io/erikuchama/day-of-the-week-calculator-cde704

I'm having problems with the DS3232RTC library (which makes use of the Time library). So I'm testing the uRTCLib library, but it seems like it's very basic and doesn't have automatic calculation of the day of the week.

I haven't tested the code for all dates in the functional range, but for the current date it is correct.

As the DS3232RTC library operates with Sunday at 1 and Saturday at 7, you need to get this right. In this code, Saturday is 0, and Sunday is 1.

// source: https://www.hackster.io/erikuchama/day-of-the-week-calculator-cde704
byte get_weekday(byte d, byte m, uint16_t yyyy) {
  int yy;          // Last 2 digits of the year (ie 2016 would be 16)
  int c;           // Century (ie 2016 would be 20)
  int mTable;      // Month value based on calculation table
  int SummedDate;  // Add values combined in prep for Mod7 calc
  int DoW;         // Day of the week value (0-6)
  int leap;        // Leap Year or not
  int cTable;      // Century value based on calculation table

  // Leap Year Calculation
  if ((fmod(yyyy, 4) == 0 && fmod(yyyy, 100) != 0) || (fmod(yyyy, 400) == 0)) {
    leap = 1;
  } else {
    leap = 0;
  }

  // Limit results to year 1900-2299 (to save memory)
  while (yyyy > 2299) { yyyy = yyyy - 400; }
  while (yyyy < 1900) { yyyy = yyyy + 400; }

  // Calculating century
  c = yyyy / 100;

  // Calculating two digit year
  yy = fmod(yyyy, 100);

  // Century value based on Table
  if (c == 19) { cTable = 1; }
  if (c == 20) { cTable = 0; }
  if (c == 21) { cTable = 5; }
  if (c == 22) { cTable = 3; }

  // Jan and Feb calculations affected by leap years
  if (m == 1) {
    if (leap == 1) {
      mTable = 6;
    } else {
      mTable = 0;
    }
  }
  if (m == 2) {
    if (leap == 1) {
      mTable = 2;
    } else {
      mTable = 3;
    }
  }
  // Other months not affected and have set values
  if (m == 10) { mTable = 0; }
  if (m == 8) { mTable = 2; }
  if (m == 3 || m == 11) { mTable = 3; }
  if (m == 4 || m == 7) { mTable = 6; }
  if (m == 5) { mTable = 1; }
  if (m == 6) { mTable = 4; }
  if (m == 9 || m == 12) { mTable = 5; }

  // Enter the data into the formula
  SummedDate = d + mTable + yy + (yy / 4) + cTable;

  // Find remainder
  DoW = fmod(SummedDate, 7);  // 0-6

  // // Remainder determines day of the week
  // if(DoW == 0) { Serial.println("Saturday"); }
  // if(DoW == 1) { Serial.println("Sunday"); }
  // if(DoW == 2) { Serial.println("Monday"); }
  // if(DoW == 3) { Serial.println("Tuesday"); }
  // if(DoW == 4) { Serial.println("Wednesday"); }
  // if(DoW == 5) { Serial.println("Thursday"); }
  // if(DoW == 6) { Serial.println("Friday"); }

  if (DoW == 0) {  // Saturday?
    DoW = 7;
  }

  return DoW;  // 1-7
}
rtek1000 commented 7 months ago

There's actually a whole wikipedia article on just this :) https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week

Here's code which I've used:

// returns 0=Sunday, etc
uint8_t getDayOfWeek() {
    uint16_t y = 2000 + rtc.getYear();
    uint16_t m = rtc.getMonth();
    uint16_t d = rtc.getDay();
    // http://stackoverflow.com/a/21235587
    return (d+=m<3?y--:y-2,23*m/9+d+4+y/4-y/100+y/400)%7;
}

This code appears to be wrong.

I made a sketch to compare 3 codes, and it gave an error:

11:58:07.789 -> Year: 1992 11:58:07.982 -> Year: 1993 11:58:08.206 -> Year: 1994 11:58:08.399 -> Year: 1995 11:58:08.592 -> Year: 1996 11:58:08.784 -> Year: 1997 11:58:09.009 -> Year: 1998 11:58:09.202 -> Year: 1999 11:58:09.427 -> Year: 2000 11:58:09.684 -> Year: 2001 11:58:09.910 -> Year: 2002 11:58:10.135 -> Year: 2003 11:58:10.393 -> Year: 2004 11:58:10.618 -> Year: 2005 11:58:10.875 -> Year: 2006 11:58:11.101 -> Year: 2007 11:58:11.358 -> Year: 2008 11:58:11.615 -> Year: 2009 11:58:11.873 -> Year: 2010 11:58:12.130 -> Year: 2011 11:58:12.388 -> Year: 2012 11:58:12.677 -> Year: 2013 11:58:12.932 -> Year: 2014 11:58:13.222 -> Year: 2015 11:58:13.480 -> Year: 2016 11:58:13.770 -> Year: 2017 11:58:14.059 -> Year: 2018 11:58:14.348 -> Year: 2019 11:58:14.637 -> Year: 2020 11:58:14.669 -> 27-01-2021: - Fail - wd0 (time lib):4 (Wednesday) wd1:7 (Saturday) wd2:4 (Wednesday)

https://www.calculator.net/day-of-the-week-calculator.html ==> January 27, 2021 is a Wednesday

// Code comparison, by Rtek1000
#include "Time.h"

char daysOfWeek[7][10] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  Serial.println("\r\nStart");

  char line1[30] = { 0 };

  for (uint16_t y = 1970; y < 2100; y++) {
    for (byte m = 1; m < 12; m++) {
      for (byte d = 1; d < 28; d++) {
        byte wd0 = get_weekday_time_lib(d, m, y);
        byte wd1 = getDayOfWeek(d, m, y);
        byte wd2 = get_weekday(d, m, y);

        if ((wd0 != wd1) || (wd0 != wd2) || (wd1 != wd2)) {
          sprintf(line1, "%02d-%02d-%04u: ",
                  d, m, y);
          Serial.print(line1);

          Serial.print(" - Fail - wd0 (time lib):");
          Serial.print(wd0, DEC);
          Serial.print(" (");
          Serial.print(daysOfWeek[wd0 - 1]);
          Serial.print(") wd1:");
          Serial.print(wd1, DEC);
          Serial.print(" (");
          Serial.print(daysOfWeek[wd1 - 1]);
          Serial.print(") wd2:");
          Serial.print(wd2, DEC);
          Serial.print(" (");
          Serial.print(daysOfWeek[wd2 - 1]);
          Serial.println(")");

          while (1)
            ;
        }
      }
    }

    Serial.print("Year: ");
    Serial.println(y);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
}

time_t t;
tmElements_t tm;

uint8_t get_weekday_time_lib(byte d, byte m, uint16_t yyyy) {
  tm.Year = CalendarYrToTm(yyyy);
  tm.Month = m;
  tm.Day = d;
  tm.Hour = 12;
  tm.Minute = 0;
  tm.Second = 0;

  t = makeTime(tm);

  return weekday(t);
}

// source: // http://stackoverflow.com/a/21235587
// returns 0=Sunday, etc
uint8_t getDayOfWeek(byte d, byte m, uint16_t yyyy) {
  return ((d += m < 3 ? yyyy-- : yyyy - 2, 23 * m / 9 + d + 4 + yyyy / 4 - yyyy / 100 + yyyy / 400) % 7) + 1;
}

// source: https://www.hackster.io/erikuchama/day-of-the-week-calculator-cde704
byte get_weekday(byte d, byte m, uint16_t yyyy) {
  int yy = 0;          // Last 2 digits of the year (ie 2016 would be 16)
  int c = 0;           // Century (ie 2016 would be 20)
  int mTable = 0;      // Month value based on calculation table
  int SummedDate = 0;  // Add values combined in prep for Mod7 calc
  int DoW = 0;         // Day of the week value (0-6)
  int leap = 0;        // Leap Year or not
  int cTable = 0;      // Century value based on calculation table

  // Leap Year Calculation
  if ((fmod(yyyy, 4) == 0 && fmod(yyyy, 100) != 0) || (fmod(yyyy, 400) == 0)) {
    leap = 1;
  } else {
    leap = 0;
  }

  // Limit results to year 1900-2299 (to save memory)
  while (yyyy > 2299) { yyyy = yyyy - 400; }
  while (yyyy < 1900) { yyyy = yyyy + 400; }

  // Calculating century
  c = yyyy / 100;

  // Calculating two digit year
  yy = fmod(yyyy, 100);

  // Century value based on Table
  if (c == 19) { cTable = 1; }
  if (c == 20) { cTable = 0; }
  if (c == 21) { cTable = 5; }
  if (c == 22) { cTable = 3; }

  // Jan and Feb calculations affected by leap years
  if (m == 1) {
    if (leap == 1) {
      mTable = 6;
    } else {
      mTable = 0;
    }
  }
  if (m == 2) {
    if (leap == 1) {
      mTable = 2;
    } else {
      mTable = 3;
    }
  }
  // Other months not affected and have set values
  if (m == 10) { mTable = 0; }
  if (m == 8) { mTable = 2; }
  if (m == 3 || m == 11) { mTable = 3; }
  if (m == 4 || m == 7) { mTable = 6; }
  if (m == 5) { mTable = 1; }
  if (m == 6) { mTable = 4; }
  if (m == 9 || m == 12) { mTable = 5; }

  // Enter the data into the formula
  SummedDate = d + mTable + yy + (yy / 4) + cTable;

  // Find remainder
  DoW = fmod(SummedDate, 7);  // 0-6

  // // Remainder determines day of the week
  // if(DoW == 0) { Serial.println("Saturday"); }
  // if(DoW == 1) { Serial.println("Sunday"); }
  // if(DoW == 2) { Serial.println("Monday"); }
  // if(DoW == 3) { Serial.println("Tuesday"); }
  // if(DoW == 4) { Serial.println("Wednesday"); }
  // if(DoW == 5) { Serial.println("Thursday"); }
  // if(DoW == 6) { Serial.println("Friday"); }

  if (DoW == 0) {  // Saturday?
    DoW = 7;
  }

  return DoW;  // 1-7
}