From cb0bf8da2a56bfebae8a6137e9e803869bdae107 Mon Sep 17 00:00:00 2001 From: Harrison-O Date: Thu, 30 Jul 2020 19:11:38 +0800 Subject: [PATCH 1/7] Wrote `isLeapYear()` function --- RTClib.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ RTClib.h | 10 ++++++++++ 2 files changed, 58 insertions(+) diff --git a/RTClib.cpp b/RTClib.cpp index b7562b04..45fa9cb0 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -118,6 +118,17 @@ static void write_i2c_register(uint8_t addr, uint8_t reg, uint8_t val) { const uint8_t daysInMonth[] PROGMEM = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30}; +/**************************************************************************/ +/*! + @brief checks if the year is a leap year + @param year The year to checks + @return true if a leap year, false otherwise +*/ +/**************************************************************************/ +bool isLeapYear(uint16_t year) { + return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0); +} + /**************************************************************************/ /*! @brief Given a date, return number of days since 2000/01/01, @@ -415,6 +426,43 @@ bool DateTime::isValid() const { mm == other.mm && ss == other.ss; } +/**************************************************************************/ +/*! + @author Harrison Outram + @brief Fixes DateTime object if invalid + + Determines if any date or time components are too high. + E.g. seconds == 65 + + Increments next component and reduces invalid component to fix. + E.g. if seconds == 125, then minutes goes up by 2 and seconds + goes down to 5. + + @warning Will still result in invalid DateTime if year is above 2099 + @return true if fixed, false if year becomes invalid +*/ +/**************************************************************************/ +bool DateTime::fixDateTime() { + uint8_t temp; + + if (ss >= 60) { + temp = ss / 60; + mm += temp; + ss -= 60 * temp; + } + if (mm >= 60) { + temp = mm / 60; + hh += temp; + mm -= 60 * temp; + } + if (hh >= 24) { + temp = hh / 24; + d += temp; + hh -= temp * 24; + } + +} + /**************************************************************************/ /*! @brief Writes the DateTime as a string in a user-defined format. diff --git a/RTClib.h b/RTClib.h index a1ba2a4b..66fd6421 100644 --- a/RTClib.h +++ b/RTClib.h @@ -55,6 +55,15 @@ class TimeSpan; #define SECONDS_FROM_1970_TO_2000 \ 946684800 ///< Unixtime for 2000-01-01 00:00:00, useful for initialization +/**************************************************************************/ +/*! + @brief checks if the year is a leap year + @param year The year to checks + @return true if a leap year, false otherwise +*/ +/**************************************************************************/ +bool isLeapYear(uint16_t year); + /**************************************************************************/ /*! @brief Simple general-purpose date/time class (no TZ / DST / leap @@ -81,6 +90,7 @@ class DateTime { DateTime(const __FlashStringHelper *date, const __FlashStringHelper *time); DateTime(const char *iso8601date); bool isValid() const; + bool fixDateTime(); char *toString(char *buffer); /*! From 0af4be5f1a5b17fb2b0dee667ed5429dd417a3a0 Mon Sep 17 00:00:00 2001 From: Harrison-O Date: Thu, 30 Jul 2020 19:31:47 +0800 Subject: [PATCH 2/7] Wrote `fixDateTime()` method to fix invalid DateTime objects --- RTClib.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ RTClib.h | 8 +------- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/RTClib.cpp b/RTClib.cpp index 45fa9cb0..fd83055d 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -129,6 +129,33 @@ bool isLeapYear(uint16_t year) { return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0); } +/**************************************************************************/ +/*! + @brief calculates the number of days in the month + + Considers leap years, e.g. if year == 2000 and month == 2 then + days in month is 29 + + @param year The year + @param month The month from 1-12 (1 being Jan and 12 being Dec) + @warning Will return 0 if month is invalid (i.e. month > 12) + @return The number of days in the month +*/ +/**************************************************************************/ +uint8_t getDaysInMonth(uint16_t year, uint8_t month) { + uint8_t days = 0; + + if (month == 12) { + days = 31; // needed since daysInMonth does have December days + } else if (month < 12) { + days += pgm_read_byte(daysInMonth + month - 1); + + if (month == 2 && isLeapYear(year)) days++; + } + + return days; +} + /**************************************************************************/ /*! @brief Given a date, return number of days since 2000/01/01, @@ -461,6 +488,26 @@ bool DateTime::fixDateTime() { hh -= temp * 24; } + // make month valid to prevent getDaysInMonth() returning 0 + // Otherwise, infinite loop possible + if (m > 12) { + temp = m % 12; + yOff += temp; + m -= temp * 12; + } + + temp = getDaysInMonth(yOff, m); + while (d > temp) { + d -= temp; + m++; + if (m > 12) { + yOff++; + m--; + } + temp = getDaysInMonth(yOff, m); + } + + return yOff <= 99; } /**************************************************************************/ diff --git a/RTClib.h b/RTClib.h index 66fd6421..89e4a3bc 100644 --- a/RTClib.h +++ b/RTClib.h @@ -55,14 +55,8 @@ class TimeSpan; #define SECONDS_FROM_1970_TO_2000 \ 946684800 ///< Unixtime for 2000-01-01 00:00:00, useful for initialization -/**************************************************************************/ -/*! - @brief checks if the year is a leap year - @param year The year to checks - @return true if a leap year, false otherwise -*/ -/**************************************************************************/ bool isLeapYear(uint16_t year); +uint8_t getDaysInMonth(uint16_t year, uint8_t month); /**************************************************************************/ /*! From 6f49b2b99828fbb84044498103749c367c042c2f Mon Sep 17 00:00:00 2001 From: Harrison-O Date: Thu, 30 Jul 2020 19:35:05 +0800 Subject: [PATCH 3/7] Write additional comment specifying that if DateTime is already valid, `fixDateTime()` will do nothing --- RTClib.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RTClib.cpp b/RTClib.cpp index fd83055d..09fdf686 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -465,7 +465,9 @@ bool DateTime::isValid() const { E.g. if seconds == 125, then minutes goes up by 2 and seconds goes down to 5. - @warning Will still result in invalid DateTime if year is above 2099 + Does nothing if the DateTime object is already valid + + @warning Will still result in invalid DateTime object if year is above 2099 @return true if fixed, false if year becomes invalid */ /**************************************************************************/ From 199ef8bade13b636f94516fb826b928d133aa9f7 Mon Sep 17 00:00:00 2001 From: Harrison-O Date: Thu, 30 Jul 2020 20:04:08 +0800 Subject: [PATCH 4/7] Fix indentation --- RTClib.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RTClib.cpp b/RTClib.cpp index 09fdf686..f09a5afd 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -146,11 +146,12 @@ uint8_t getDaysInMonth(uint16_t year, uint8_t month) { uint8_t days = 0; if (month == 12) { - days = 31; // needed since daysInMonth does have December days + days = 31; // needed since daysInMonth does have December days } else if (month < 12) { days += pgm_read_byte(daysInMonth + month - 1); - if (month == 2 && isLeapYear(year)) days++; + if (month == 2 && isLeapYear(year)) + days++; } return days; From 860960861ff383dc354b07d44ebdb7c46e076172 Mon Sep 17 00:00:00 2001 From: Harrison-O Date: Fri, 31 Jul 2020 20:49:49 +0800 Subject: [PATCH 5/7] Fixed bug with `fixDateTime()` not dealing with months or days of 0. Fixed bug with `fixDateTime()` overflowing DateTime data members. Simplified `isLeapYear()` function for efficiency at the cost of not working for years of multiples of 100 and not multiples fo 400. Also wrote warning for limitation. Made `isleapYear()` static to avoid misuse. Rewrote warning for `fixDateTime()` to specify that the year becomes 2100 if fixing DateTime requires setting the year to before 2000. --- RTClib.cpp | 85 ++++++++++++++++++++++++++++++++++++++---------------- RTClib.h | 1 - 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/RTClib.cpp b/RTClib.cpp index f09a5afd..8af1e264 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -122,11 +122,13 @@ const uint8_t daysInMonth[] PROGMEM = {31, 28, 31, 30, 31, 30, /*! @brief checks if the year is a leap year @param year The year to checks + @warning designed for year range 2000-2099. This does not work for years + that are multiples of 100 and not multiples of 400. @return true if a leap year, false otherwise */ /**************************************************************************/ -bool isLeapYear(uint16_t year) { - return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0); +static bool isLeapYear(uint16_t year) { + return year % 4 == 0; } /**************************************************************************/ @@ -468,48 +470,81 @@ bool DateTime::isValid() const { Does nothing if the DateTime object is already valid - @warning Will still result in invalid DateTime object if year is above 2099 + @note month of 0 is interpreted as December of previous year + + @warning Will still result in invalid DateTime object if year is after + 2099 or before 2000. If before 2000, year will be set to 2100 @return true if fixed, false if year becomes invalid */ /**************************************************************************/ bool DateTime::fixDateTime() { uint8_t temp; - + + // prevents overflow and underflow errors + uint16_t newMin = mm; + uint16_t newHour = hh; + int16_t newDay = d; + int16_t newMonth = m; + int16_t newYear = yOff; + if (ss >= 60) { temp = ss / 60; - mm += temp; + newMin += temp; ss -= 60 * temp; } - if (mm >= 60) { - temp = mm / 60; - hh += temp; - mm -= 60 * temp; + if (newMin >= 60) { + temp = newMin / 60; + newHour += temp; + newMin -= 60 * temp; } - if (hh >= 24) { - temp = hh / 24; - d += temp; - hh -= temp * 24; + if (newHour >= 24) { + temp = newHour / 24; + newDay += temp; + newHour -= temp * 24; } // make month valid to prevent getDaysInMonth() returning 0 // Otherwise, infinite loop possible - if (m > 12) { - temp = m % 12; - yOff += temp; - m -= temp * 12; + if (newMonth > 12) { + temp = newMonth / 12; + newYear += temp; + newMonth -= temp * 12; + } else if (newMonth == 0) { // interpret as December of previous year + newMonth = 12; + newYear--; + } + + temp = getDaysInMonth(newYear + 2000, newMonth); + while (newDay > temp) { + newDay -= temp; + newMonth++; + if (newMonth > 12) { + newYear++; + newMonth -= 12; + } + temp = getDaysInMonth(newYear + 2000, newMonth); } - temp = getDaysInMonth(yOff, m); - while (d > temp) { - d -= temp; - m++; - if (m > 12) { - yOff++; - m--; + if (newDay == 0) { + newMonth--; + if (newMonth == 0) { + newMonth = 12; + newYear--; } - temp = getDaysInMonth(yOff, m); + newDay = getDaysInMonth(newYear + 2000, newMonth); } + mm = newMin; + hh = newHour; + d = newDay; + m = newMonth; + + if (newYear >= 0) + yOff = newYear; + else + // can't assign negative to unsigned so assign value too high + yOff = 100; + return yOff <= 99; } diff --git a/RTClib.h b/RTClib.h index 89e4a3bc..ad791cc9 100644 --- a/RTClib.h +++ b/RTClib.h @@ -55,7 +55,6 @@ class TimeSpan; #define SECONDS_FROM_1970_TO_2000 \ 946684800 ///< Unixtime for 2000-01-01 00:00:00, useful for initialization -bool isLeapYear(uint16_t year); uint8_t getDaysInMonth(uint16_t year, uint8_t month); /**************************************************************************/ From a4602919875283d1446d1ad01dd2178d21043c56 Mon Sep 17 00:00:00 2001 From: Harrison-O Date: Mon, 3 Aug 2020 09:05:53 +0800 Subject: [PATCH 6/7] Fixed clang formatting issues --- RTClib.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/RTClib.cpp b/RTClib.cpp index 8af1e264..428561f8 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -127,9 +127,7 @@ const uint8_t daysInMonth[] PROGMEM = {31, 28, 31, 30, 31, 30, @return true if a leap year, false otherwise */ /**************************************************************************/ -static bool isLeapYear(uint16_t year) { - return year % 4 == 0; -} +static bool isLeapYear(uint16_t year) { return year % 4 == 0; } /**************************************************************************/ /*! @@ -479,7 +477,6 @@ bool DateTime::isValid() const { /**************************************************************************/ bool DateTime::fixDateTime() { uint8_t temp; - // prevents overflow and underflow errors uint16_t newMin = mm; uint16_t newHour = hh; From a01bab8fabe5f905106daad10fbbeed0265b7b8a Mon Sep 17 00:00:00 2001 From: Harrison-O Date: Mon, 3 Aug 2020 11:20:02 +0800 Subject: [PATCH 7/7] Fixed clang indentation issue --- RTClib.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RTClib.cpp b/RTClib.cpp index 428561f8..b97b86e6 100644 --- a/RTClib.cpp +++ b/RTClib.cpp @@ -483,7 +483,7 @@ bool DateTime::fixDateTime() { int16_t newDay = d; int16_t newMonth = m; int16_t newYear = yOff; - + if (ss >= 60) { temp = ss / 60; newMin += temp; @@ -540,8 +540,8 @@ bool DateTime::fixDateTime() { yOff = newYear; else // can't assign negative to unsigned so assign value too high - yOff = 100; - + yOff = 100; + return yOff <= 99; }