Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions RTClib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,45 @@ 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
@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
*/
/**************************************************************************/
static bool isLeapYear(uint16_t year) { return year % 4 == 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,
Expand Down Expand Up @@ -415,6 +454,97 @@ 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.

Does nothing if the DateTime object is already valid

@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;
newMin += temp;
ss -= 60 * temp;
}
if (newMin >= 60) {
temp = newMin / 60;
newHour += temp;
newMin -= 60 * temp;
}
if (newHour >= 24) {
temp = newHour / 24;
newDay += temp;
newHour -= temp * 24;
}

// make month valid to prevent getDaysInMonth() returning 0
// Otherwise, infinite loop possible
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);
}

if (newDay == 0) {
newMonth--;
if (newMonth == 0) {
newMonth = 12;
newYear--;
}
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;
}

/**************************************************************************/
/*!
@brief Writes the DateTime as a string in a user-defined format.
Expand Down
3 changes: 3 additions & 0 deletions RTClib.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class TimeSpan;
#define SECONDS_FROM_1970_TO_2000 \
946684800 ///< Unixtime for 2000-01-01 00:00:00, useful for initialization

uint8_t getDaysInMonth(uint16_t year, uint8_t month);

/**************************************************************************/
/*!
@brief Simple general-purpose date/time class (no TZ / DST / leap
Expand All @@ -81,6 +83,7 @@ class DateTime {
DateTime(const __FlashStringHelper *date, const __FlashStringHelper *time);
DateTime(const char *iso8601date);
bool isValid() const;
bool fixDateTime();
char *toString(char *buffer);

/*!
Expand Down