AceTime  1.3
Date and time classes for Arduino that support timezones from the TZ Database, and a system clock that can synchronize from an NTP server or an RTC chip.
LocalDate.h
1 /*
2  * MIT License
3  * Copyright (c) 2018 Brian T. Park
4  */
5 
6 #ifndef ACE_TIME_LOCAL_DATE_H
7 #define ACE_TIME_LOCAL_DATE_H
8 
9 #include <stdint.h>
10 #include "common/common.h"
11 #include "LocalTime.h"
12 
13 class Print;
14 
15 namespace ace_time {
16 
36 class LocalDate {
37  public:
39  static const int16_t kEpochYear = 2000;
40 
45  static const int8_t kInvalidYearTiny = INT8_MIN;
46 
51  static const int8_t kMinYearTiny = INT8_MIN + 1;
52 
57  static const int8_t kMaxYearTiny = INT8_MAX;
58 
60  static const acetime_t kInvalidEpochDays = INT32_MIN;
61 
64 
69  static const acetime_t kSecondsSinceUnixEpoch = 946684800;
70 
75  static const acetime_t kDaysSinceUnixEpoch = 10957;
76 
78  static const uint8_t kMonday = 1;
79 
81  static const uint8_t kTuesday = 2;
82 
84  static const uint8_t kWednesday = 3;
85 
87  static const uint8_t kThursday = 4;
88 
90  static const uint8_t kFriday = 5;
91 
93  static const uint8_t kSaturday = 6;
94 
96  static const uint8_t kSunday = 7;
97 
106  static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day) {
107  int8_t yearTiny = isYearValid(year)
109  return LocalDate(yearTiny, month, day);
110  }
111 
113  static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month,
114  uint8_t day) {
115  return LocalDate(yearTiny, month, day);
116  }
117 
125  static LocalDate forEpochDays(acetime_t epochDays) {
126  int16_t year;
127  uint8_t month;
128  uint8_t day;
129  if (epochDays == kInvalidEpochDays) {
130  year = month = day = 0;
131  } else {
132  extractYearMonthDay(epochDays, year, month, day);
133  }
134  return forComponents(year, month, day);
135  }
136 
138  static LocalDate forUnixDays(acetime_t unixDays) {
139  if (unixDays == kInvalidEpochDays) {
140  return forEpochDays(unixDays);
141  } else {
142  return forEpochDays(unixDays - kDaysSinceUnixEpoch);
143  }
144  }
145 
157  static LocalDate forEpochSeconds(acetime_t epochSeconds) {
158  if (epochSeconds == kInvalidEpochSeconds) {
160  } else {
161  // integer floor-division towards -infinity
162  acetime_t days = (epochSeconds < 0)
163  ? (epochSeconds + 1) / 86400 - 1
164  : epochSeconds / 86400;
165  return forEpochDays(days);
166  }
167  }
168 
174  static LocalDate forUnixSeconds(acetime_t unixSeconds) {
175  if (unixSeconds == kInvalidEpochSeconds) {
176  return forEpochSeconds(unixSeconds);
177  } else {
178  return forEpochSeconds(unixSeconds - kSecondsSinceUnixEpoch);
179  }
180  }
181 
191  static LocalDate forDateString(const char* dateString);
192 
200  static LocalDate forDateStringChainable(const char*& dateString);
201 
206  static LocalDate forError() {
207  return LocalDate(kInvalidYearTiny, 0, 0);
208  }
209 
211  static bool isLeapYear(int16_t year) {
212  return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
213  }
214 
216  static bool isYearValid(int16_t year) {
217  return year >= kEpochYear + kMinYearTiny
218  && year <= kEpochYear + kMaxYearTiny;
219  }
220 
222  static uint8_t daysInMonth(int16_t year, uint8_t month) {
223  uint8_t days = sDaysInMonth[month - 1];
224  return (month == 2 && isLeapYear(year)) ? days + 1 : days;
225  }
226 
228  explicit LocalDate() {}
229 
231  int16_t year() const { return mYearTiny + kEpochYear; }
232 
234  void year(int16_t year) { mYearTiny = year - kEpochYear; }
235 
241  int8_t yearTiny() const { return mYearTiny; }
242 
248  void yearTiny(int8_t yearTiny) { mYearTiny = yearTiny; }
249 
251  uint8_t month() const { return mMonth; }
252 
254  void month(uint8_t month) { mMonth = month; }
255 
257  uint8_t day() const { return mDay; }
258 
260  void day(uint8_t day) { mDay = day; }
261 
268  uint8_t dayOfWeek() const {
269  // The "year" starts in March to shift leap year calculation to end.
270  int16_t y = year() - (mMonth < 3);
271  int16_t d = y + y/4 - y/100 + y/400 + sDayOfWeek[mMonth-1] + mDay;
272 
273  // 2000-1-1 was a Saturday=6, so set the offsets accordingly
274  return (d < -1) ? (d + 1) % 7 + 8 : (d + 1) % 7 + 1;
275  }
276 
278  bool isError() const {
279  return mYearTiny == kInvalidYearTiny
280  || mDay < 1 || mDay > 31
281  || mMonth < 1 || mMonth > 12;
282  }
283 
300  acetime_t toEpochDays() const {
301  if (isError()) return kInvalidEpochDays;
302 
303  // From wiki article:
304  //
305  // JDN = (1461 x (Y + 4800 + (M - 14)/12))/4
306  // + (367 x (M - 2 - 12 x ((M - 14)/12)))/12
307  // - (3 x ((Y + 4900 + (M - 14)/12)/100))/4
308  // + D - 32075
309  // JDN2000 = JDN - 2451545
310  //
311  // It looks like the formula needs to be done using signed integers
312  // because it depends on the modulo operation (%) to truncate towards 0
313  // for negative numbers.
314 
315  int8_t mm = (mMonth - 14)/12;
316  int16_t yy = year();
317  int32_t jdn = ((int32_t) 1461 * (yy + 4800 + mm))/4
318  + (367 * (mMonth - 2 - 12 * mm))/12
319  - (3 * ((yy + 4900 + mm)/100))/4
320  + mDay - 32075;
321  return jdn - kDaysSinceJulianEpoch;
322  }
323 
325  acetime_t toUnixDays() const {
326  if (isError()) return kInvalidEpochDays;
327  return toEpochDays() + kDaysSinceUnixEpoch;
328  }
329 
340  acetime_t toEpochSeconds() const {
341  if (isError()) return kInvalidEpochSeconds;
342  return 86400 * toEpochDays();
343  }
344 
348  acetime_t toUnixSeconds() const {
349  if (isError()) return kInvalidEpochSeconds;
350  return 86400 * toUnixDays();
351  }
352 
359  int8_t compareTo(const LocalDate& that) const {
360  if (mYearTiny < that.mYearTiny) return -1;
361  if (mYearTiny > that.mYearTiny) return 1;
362  if (mMonth < that.mMonth) return -1;
363  if (mMonth > that.mMonth) return 1;
364  if (mDay < that.mDay) return -1;
365  if (mDay > that.mDay) return 1;
366  return 0;
367  }
368 
375  void printTo(Print& printer) const;
376 
377  // Use default copy constructor and assignment operator.
378  LocalDate(const LocalDate&) = default;
379  LocalDate& operator=(const LocalDate&) = default;
380 
381  private:
382  friend bool operator==(const LocalDate& a, const LocalDate& b);
383 
388  static const acetime_t kDaysSinceJulianEpoch = 2451545;
389 
391  static const uint8_t kDateStringLength = 10;
392 
398  static const uint8_t sDayOfWeek[12];
399 
401  static const uint8_t sDaysInMonth[12];
402 
404  explicit LocalDate(int8_t yearTiny, uint8_t month, uint8_t day):
405  mYearTiny(yearTiny),
406  mMonth(month),
407  mDay(day) {}
408 
414  static void extractYearMonthDay(acetime_t epochDays, int16_t& year,
415  uint8_t& month, uint8_t& day) {
416  uint32_t J = epochDays + kDaysSinceJulianEpoch;
417  uint32_t f = J + 1401 + (((4 * J + 274277 ) / 146097) * 3) / 4 - 38;
418  uint32_t e = 4 * f + 3;
419  uint32_t g = e % 1461 / 4;
420  uint32_t h = 5 * g + 2;
421  day = (h % 153) / 5 + 1;
422  month = (h / 153 + 2) % 12 + 1;
423  year = (e / 1461) - 4716 + (12 + 2 - month) / 12;
424 
425  // 2000-01-01 is Saturday (7)
426  //dayOfWeek = (epochDays + 6) % 7 + 1;
427  }
428 
433  int8_t mYearTiny; // [-127, 127], -128 indicates error
434 
435  uint8_t mMonth; // [1, 12], 0 indicates error
436  uint8_t mDay; // [1, 31], 0 indicates error
437 };
438 
440 inline bool operator==(const LocalDate& a, const LocalDate& b) {
441  return a.mDay == b.mDay
442  && a.mMonth == b.mMonth
443  && a.mYearTiny == b.mYearTiny;
444 }
445 
447 inline bool operator!=(const LocalDate& a, const LocalDate& b) {
448  return ! (a == b);
449 }
450 
451 }
452 
453 #endif
ace_time::LocalDate::printTo
void printTo(Print &printer) const
Print LocalDate to 'printer' in ISO 8601 format, along with the day of week.
Definition: LocalDate.cpp:46
ace_time::LocalDate::forError
static LocalDate forError()
Factory method that returns a LocalDate which represents an error condition.
Definition: LocalDate.h:206
ace_time::LocalDate::kSecondsSinceUnixEpoch
static const acetime_t kSecondsSinceUnixEpoch
Number of seconds from Unix epoch (1970-01-01 00:00:00Z) to the AceTime epoch (2000-01-01 00:00:00Z).
Definition: LocalDate.h:69
ace_time::LocalDate::LocalDate
LocalDate()
Default constructor does nothing.
Definition: LocalDate.h:228
ace_time::LocalDate::forEpochDays
static LocalDate forEpochDays(acetime_t epochDays)
Factory method using the number of days since AceTime epoch of 2000-01-01.
Definition: LocalDate.h:125
ace_time::LocalDate::month
uint8_t month() const
Return the month with January=1, December=12.
Definition: LocalDate.h:251
ace_time::LocalDate::kInvalidEpochSeconds
static const acetime_t kInvalidEpochSeconds
Sentinel epochSeconds which indicates an error.
Definition: LocalDate.h:63
ace_time::LocalDate::kMaxYearTiny
static const int8_t kMaxYearTiny
Sentinel yearTiny which represents the largest year, effectively +Infinity.
Definition: LocalDate.h:57
ace_time::LocalDate::kThursday
static const uint8_t kThursday
Thursday ISO 8601 number.
Definition: LocalDate.h:87
ace_time::LocalDate::kInvalidEpochDays
static const acetime_t kInvalidEpochDays
Sentinel epochDays which indicates an error.
Definition: LocalDate.h:60
ace_time::LocalDate::toEpochDays
acetime_t toEpochDays() const
Return number of days since AceTime epoch (2000-01-01 00:00:00Z).
Definition: LocalDate.h:300
ace_time::LocalTime::kInvalidSeconds
static const acetime_t kInvalidSeconds
An invalid seconds marker that indicates isError() true.
Definition: LocalTime.h:29
ace_time::LocalDate::isLeapYear
static bool isLeapYear(int16_t year)
True if year is a leap year.
Definition: LocalDate.h:211
ace_time::LocalDate::kMinYearTiny
static const int8_t kMinYearTiny
Sentinel yearTiny which represents the smallest year, effectively -Infinity.
Definition: LocalDate.h:51
ace_time::LocalDate::dayOfWeek
uint8_t dayOfWeek() const
Calculate the day of week given the (year, month, day).
Definition: LocalDate.h:268
ace_time::LocalDate::year
void year(int16_t year)
Set the year given the full year.
Definition: LocalDate.h:234
ace_time::LocalDate
The date (year, month, day) representing the date without regards to time zone.
Definition: LocalDate.h:36
ace_time::LocalDate::toUnixSeconds
acetime_t toUnixSeconds() const
Return the number of seconds since Unix epoch (1970-01-01 00:00:00).
Definition: LocalDate.h:348
ace_time::LocalDate::toEpochSeconds
acetime_t toEpochSeconds() const
Return the number of seconds since AceTime epoch (2000-01-01 00:00:00).
Definition: LocalDate.h:340
ace_time::LocalDate::operator==
friend bool operator==(const LocalDate &a, const LocalDate &b)
Return true if two LocalDate objects are equal in all components.
Definition: LocalDate.h:440
ace_time::LocalDate::forEpochSeconds
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since AceTime epoch of 2000-01-01.
Definition: LocalDate.h:157
ace_time::LocalDate::toUnixDays
acetime_t toUnixDays() const
Return the number of days since Unix epoch (1970-01-01 00:00:00).
Definition: LocalDate.h:325
ace_time::LocalDate::forDateStringChainable
static LocalDate forDateStringChainable(const char *&dateString)
Variant of forDateString() that updates the pointer to the next unprocessed character.
Definition: LocalDate.cpp:72
ace_time::LocalDate::kFriday
static const uint8_t kFriday
Friday ISO 8601 number.
Definition: LocalDate.h:90
ace_time::LocalDate::forDateString
static LocalDate forDateString(const char *dateString)
Factory method.
Definition: LocalDate.cpp:65
ace_time::LocalDate::kInvalidYearTiny
static const int8_t kInvalidYearTiny
Sentinel yearTiny which indicates an error condition or sometimes a year that 'does not exist'.
Definition: LocalDate.h:45
ace_time::LocalDate::day
uint8_t day() const
Return the day of the month.
Definition: LocalDate.h:257
ace_time::LocalDate::kSaturday
static const uint8_t kSaturday
Saturday ISO 8601 number.
Definition: LocalDate.h:93
ace_time::LocalDate::day
void day(uint8_t day)
Set the day of the month.
Definition: LocalDate.h:260
ace_time::LocalDate::forTinyComponents
static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month, uint8_t day)
Factory method using components with an int8_t yearTiny.
Definition: LocalDate.h:113
ace_time::LocalDate::isError
bool isError() const
Return true if any component indicates an error condition.
Definition: LocalDate.h:278
ace_time::LocalDate::forComponents
static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day)
Factory method using separated year, month and day fields.
Definition: LocalDate.h:106
ace_time::LocalDate::year
int16_t year() const
Return the full year instead of just the last 2 digits.
Definition: LocalDate.h:231
ace_time::LocalDate::kEpochYear
static const int16_t kEpochYear
Base year of epoch.
Definition: LocalDate.h:39
ace_time::LocalDate::forUnixSeconds
static LocalDate forUnixSeconds(acetime_t unixSeconds)
Factory method that takes the number of seconds since Unix Epoch of 1970-01-01.
Definition: LocalDate.h:174
ace_time::LocalDate::kSunday
static const uint8_t kSunday
Sunday ISO 8601 number.
Definition: LocalDate.h:96
ace_time::LocalDate::compareTo
int8_t compareTo(const LocalDate &that) const
Compare 'this' LocalDate to 'that' LocalDate, returning (<0, 0, >0) according to whether 'this' occur...
Definition: LocalDate.h:359
ace_time::LocalDate::kTuesday
static const uint8_t kTuesday
Tuesday ISO 8601 number.
Definition: LocalDate.h:81
ace_time::LocalDate::daysInMonth
static uint8_t daysInMonth(int16_t year, uint8_t month)
Return the number of days in the current month.
Definition: LocalDate.h:222
ace_time::LocalDate::yearTiny
void yearTiny(int8_t yearTiny)
Set the single-byte year offset from year 2000.
Definition: LocalDate.h:248
ace_time::LocalDate::forUnixDays
static LocalDate forUnixDays(acetime_t unixDays)
Factory method using the number of days since Unix epoch 1970-01-1.
Definition: LocalDate.h:138
ace_time::LocalDate::month
void month(uint8_t month)
Set the month.
Definition: LocalDate.h:254
ace_time::LocalDate::kDaysSinceUnixEpoch
static const acetime_t kDaysSinceUnixEpoch
Number of days from Unix epoch (1970-01-01 00:00:00Z) to the AceTime epoch (2000-01-01 00:00:00Z).
Definition: LocalDate.h:75
ace_time::LocalDate::isYearValid
static bool isYearValid(int16_t year)
Return true if year is within valid range of [1873, 2127].
Definition: LocalDate.h:216
ace_time::LocalDate::kMonday
static const uint8_t kMonday
Monday ISO 8601 number.
Definition: LocalDate.h:78
ace_time::LocalDate::yearTiny
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
Definition: LocalDate.h:241
ace_time::LocalDate::kWednesday
static const uint8_t kWednesday
Wednesday ISO 8601 number.
Definition: LocalDate.h:84