AceTime  0.5
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 
48  static const acetime_t kInvalidEpochDays = INT32_MIN;
49 
52 
57  static const acetime_t kSecondsSinceUnixEpoch = 946684800;
58 
63  static const acetime_t kDaysSinceUnixEpoch = 10957;
64 
66  static const uint8_t kMonday = 1;
67 
69  static const uint8_t kTuesday = 2;
70 
72  static const uint8_t kWednesday = 3;
73 
75  static const uint8_t kThursday = 4;
76 
78  static const uint8_t kFriday = 5;
79 
81  static const uint8_t kSaturday = 6;
82 
84  static const uint8_t kSunday = 7;
85 
94  static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day) {
95  int8_t yearTiny = isYearValid(year)
96  ? year - kEpochYear : kInvalidYearTiny;
97  return LocalDate(yearTiny, month, day);
98  }
99 
101  static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month,
102  uint8_t day) {
103  return LocalDate(yearTiny, month, day);
104  }
105 
113  static LocalDate forEpochDays(acetime_t epochDays) {
114  int16_t year;
115  uint8_t month;
116  uint8_t day;
117  if (epochDays == kInvalidEpochDays) {
118  year = month = day = 0;
119  } else {
120  extractYearMonthDay(epochDays, year, month, day);
121  }
122  return forComponents(year, month, day);
123  }
124 
126  static LocalDate forUnixDays(acetime_t unixDays) {
127  if (unixDays == kInvalidEpochDays) {
128  return forEpochDays(unixDays);
129  } else {
130  return forEpochDays(unixDays - kDaysSinceUnixEpoch);
131  }
132  }
133 
145  static LocalDate forEpochSeconds(acetime_t epochSeconds) {
146  if (epochSeconds == kInvalidEpochSeconds) {
147  return forEpochDays(kInvalidEpochDays);
148  } else {
149  // integer floor-division towards -infinity
150  acetime_t days = (epochSeconds < 0)
151  ? (epochSeconds + 1) / 86400 - 1
152  : epochSeconds / 86400;
153  return forEpochDays(days);
154  }
155  }
156 
162  static LocalDate forUnixSeconds(acetime_t unixSeconds) {
163  if (unixSeconds == kInvalidEpochSeconds) {
164  return forEpochSeconds(unixSeconds);
165  } else {
166  return forEpochSeconds(unixSeconds - kSecondsSinceUnixEpoch);
167  }
168  }
169 
179  static LocalDate forDateString(const char* dateString);
180 
188  static LocalDate forDateStringChainable(const char*& dateString);
189 
194  static LocalDate forError() {
195  return LocalDate(kInvalidYearTiny, 0, 0);
196  }
197 
199  static bool isLeapYear(int16_t year) {
200  return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
201  }
202 
204  static bool isYearValid(int16_t year) {
205  return year >= kEpochYear + kMinYearTiny
206  && year <= kEpochYear + kMaxYearTiny;
207  }
208 
210  static uint8_t daysInMonth(int16_t year, uint8_t month) {
211  uint8_t days = sDaysInMonth[month - 1];
212  return (month == 2 && isLeapYear(year)) ? days + 1 : days;
213  }
214 
216  explicit LocalDate() {}
217 
219  int16_t year() const { return mYearTiny + kEpochYear; }
220 
222  void year(int16_t year) { mYearTiny = year - kEpochYear; }
223 
225  int8_t yearTiny() const { return mYearTiny; }
226 
228  void yearTiny(int8_t yearTiny) { mYearTiny = yearTiny; }
229 
231  uint8_t month() const { return mMonth; }
232 
234  void month(uint8_t month) { mMonth = month; }
235 
237  uint8_t day() const { return mDay; }
238 
240  void day(uint8_t day) { mDay = day; }
241 
248  uint8_t dayOfWeek() const {
249  // The "year" starts in March to shift leap year calculation to end.
250  int16_t y = year() - (mMonth < 3);
251  int16_t d = y + y/4 - y/100 + y/400 + sDayOfWeek[mMonth-1] + mDay;
252 
253  // 2000-1-1 was a Saturday=6, so set the offsets accordingly
254  return (d < -1) ? (d + 1) % 7 + 8 : (d + 1) % 7 + 1;
255  }
256 
258  bool isError() const {
259  return mYearTiny == kInvalidYearTiny
260  || mDay < 1 || mDay > 31
261  || mMonth < 1 || mMonth > 12;
262  }
263 
280  acetime_t toEpochDays() const {
281  if (isError()) return kInvalidEpochDays;
282 
283  // From wiki article:
284  //
285  // JDN = (1461 x (Y + 4800 + (M - 14)/12))/4
286  // + (367 x (M - 2 - 12 x ((M - 14)/12)))/12
287  // - (3 x ((Y + 4900 + (M - 14)/12)/100))/4
288  // + D - 32075
289  // JDN2000 = JDN - 2451545
290  //
291  // It looks like the formula needs to be done using signed integers
292  // because it depends on the modulo operation (%) to truncate towards 0
293  // for negative numbers.
294 
295  int8_t mm = (mMonth - 14)/12;
296  int16_t yy = year();
297  int32_t jdn = ((int32_t) 1461 * (yy + 4800 + mm))/4
298  + (367 * (mMonth - 2 - 12 * mm))/12
299  - (3 * ((yy + 4900 + mm)/100))/4
300  + mDay - 32075;
301  return jdn - kDaysSinceJulianEpoch;
302  }
303 
305  acetime_t toUnixDays() const {
306  if (isError()) return kInvalidEpochDays;
307  return toEpochDays() + kDaysSinceUnixEpoch;
308  }
309 
320  acetime_t toEpochSeconds() const {
321  if (isError()) return kInvalidEpochSeconds;
322  return 86400 * toEpochDays();
323  }
324 
328  acetime_t toUnixSeconds() const {
329  if (isError()) return kInvalidEpochSeconds;
330  return 86400 * toUnixDays();
331  }
332 
338  int8_t compareTo(const LocalDate& that) const {
339  if (mYearTiny < that.mYearTiny) return -1;
340  if (mYearTiny > that.mYearTiny) return 1;
341  if (mMonth < that.mMonth) return -1;
342  if (mMonth > that.mMonth) return 1;
343  if (mDay < that.mDay) return -1;
344  if (mDay > that.mDay) return 1;
345  return 0;
346  }
347 
354  void printTo(Print& printer) const;
355 
356  // Use default copy constructor and assignment operator.
357  LocalDate(const LocalDate&) = default;
358  LocalDate& operator=(const LocalDate&) = default;
359 
360  private:
361  friend bool operator==(const LocalDate& a, const LocalDate& b);
362 
367  static const int8_t kMinYearTiny = INT8_MIN + 1;
368 
373  static const int8_t kMaxYearTiny = INT8_MAX;
374 
379  static const acetime_t kDaysSinceJulianEpoch = 2451545;
380 
382  static const uint8_t kDateStringLength = 10;
383 
389  static const uint8_t sDayOfWeek[12];
390 
392  static const uint8_t sDaysInMonth[12];
393 
395  explicit LocalDate(int8_t yearTiny, uint8_t month, uint8_t day):
396  mYearTiny(yearTiny),
397  mMonth(month),
398  mDay(day) {}
399 
405  static void extractYearMonthDay(acetime_t epochDays, int16_t& year,
406  uint8_t& month, uint8_t& day) {
407  uint32_t J = epochDays + kDaysSinceJulianEpoch;
408  uint32_t f = J + 1401 + (((4 * J + 274277 ) / 146097) * 3) / 4 - 38;
409  uint32_t e = 4 * f + 3;
410  uint32_t g = e % 1461 / 4;
411  uint32_t h = 5 * g + 2;
412  day = (h % 153) / 5 + 1;
413  month = (h / 153 + 2) % 12 + 1;
414  year = (e / 1461) - 4716 + (12 + 2 - month) / 12;
415 
416  // 2000-01-01 is Saturday (7)
417  //dayOfWeek = (epochDays + 6) % 7 + 1;
418  }
419 
424  int8_t mYearTiny; // [-127, 127], -128 indicates error
425 
426  uint8_t mMonth; // [1, 12], 0 indicates error
427  uint8_t mDay; // [1, 31], 0 indicates error
428 };
429 
431 inline bool operator==(const LocalDate& a, const LocalDate& b) {
432  return a.mDay == b.mDay
433  && a.mMonth == b.mMonth
434  && a.mYearTiny == b.mYearTiny;
435 }
436 
438 inline bool operator!=(const LocalDate& a, const LocalDate& b) {
439  return ! (a == b);
440 }
441 
442 }
443 
444 #endif
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:57
static LocalDate forEpochDays(acetime_t epochDays)
Factory method using the number of days since AceTime epoch of 2000-01-01.
Definition: LocalDate.h:113
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:63
static const uint8_t kWednesday
Wednesday ISO 8601 number.
Definition: LocalDate.h:72
static LocalDate forError()
Factory method that returns a LocalDate which represents an error condition.
Definition: LocalDate.h:194
static const uint8_t kThursday
Thursday ISO 8601 number.
Definition: LocalDate.h:75
static const acetime_t kInvalidEpochDays
Sentinel epochDays which indicates an error.
Definition: LocalDate.h:48
static LocalDate forUnixDays(acetime_t unixDays)
Factory method using the number of days since Unix epoch 1970-01-1.
Definition: LocalDate.h:126
LocalDate()
Default constructor does nothing.
Definition: LocalDate.h:216
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
Definition: LocalDate.h:225
static const acetime_t kInvalidEpochSeconds
Sentinel epochSeconds which indicates an error.
Definition: LocalDate.h:51
static const acetime_t kInvalidSeconds
An invalid seconds marker that indicates isError() true.
Definition: LocalTime.h:29
static LocalDate forDateString(const char *dateString)
Factory method.
Definition: LocalDate.cpp:67
static bool isLeapYear(int16_t year)
True if year is a leap year.
Definition: LocalDate.h:199
void printTo(Print &printer) const
Print LocalDate to &#39;printer&#39; in ISO 8601 format, along with the day of week.
Definition: LocalDate.cpp:48
acetime_t toEpochDays() const
Return number of days since AceTime epoch (2000-01-01 00:00:00Z).
Definition: LocalDate.h:280
static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month, uint8_t day)
Factory method using components with an int8_t yearTiny.
Definition: LocalDate.h:101
static LocalDate forDateStringChainable(const char *&dateString)
Variant of forDateString() that updates the pointer to the next unprocessed character.
Definition: LocalDate.cpp:74
bool isError() const
Return true if any component indicates an error condition.
Definition: LocalDate.h:258
uint8_t day() const
Return the day of the month.
Definition: LocalDate.h:237
acetime_t toEpochSeconds() const
Return the number of seconds since AceTime epoch (2000-01-01 00:00:00).
Definition: LocalDate.h:320
acetime_t toUnixDays() const
Return the number of days since Unix epoch (1970-01-01 00:00:00).
Definition: LocalDate.h:305
friend bool operator==(const LocalDate &a, const LocalDate &b)
Return true if two LocalDate objects are equal in all components.
Definition: LocalDate.h:431
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since AceTime epoch of 2000-01-01.
Definition: LocalDate.h:145
void year(int16_t year)
Set the year given the full year.
Definition: LocalDate.h:222
acetime_t toUnixSeconds() const
Return the number of seconds since Unix epoch (1970-01-01 00:00:00).
Definition: LocalDate.h:328
void yearTiny(int8_t yearTiny)
Set the single-byte year offset from year 2000.
Definition: LocalDate.h:228
static LocalDate forUnixSeconds(acetime_t unixSeconds)
Factory method that takes the number of seconds since Unix Epoch of 1970-01-01.
Definition: LocalDate.h:162
static const int8_t kInvalidYearTiny
Sentinel yearTiny which indicates an error condition or sometimes a year that &#39;does not exist&#39;...
Definition: LocalDate.h:45
static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day)
Factory method using separated year, month and day fields.
Definition: LocalDate.h:94
static const uint8_t kSunday
Sunday ISO 8601 number.
Definition: LocalDate.h:84
The date (year, month, day) representing the date without regards to time zone.
Definition: LocalDate.h:36
static const uint8_t kFriday
Friday ISO 8601 number.
Definition: LocalDate.h:78
static const uint8_t kSaturday
Saturday ISO 8601 number.
Definition: LocalDate.h:81
static const uint8_t kTuesday
Tuesday ISO 8601 number.
Definition: LocalDate.h:69
void month(uint8_t month)
Set the month.
Definition: LocalDate.h:234
static const int16_t kEpochYear
Base year of epoch.
Definition: LocalDate.h:39
static uint8_t daysInMonth(int16_t year, uint8_t month)
Return the number of days in the current month.
Definition: LocalDate.h:210
uint8_t month() const
Return the month with January=1, December=12.
Definition: LocalDate.h:231
static bool isYearValid(int16_t year)
Return true if year is within valid range of [1873, 2127].
Definition: LocalDate.h:204
int16_t year() const
Return the full year instead of just the last 2 digits.
Definition: LocalDate.h:219
void day(uint8_t day)
Set the day of the month.
Definition: LocalDate.h:240
int8_t compareTo(const LocalDate &that) const
Compare this LocalDate to that LocalDate, returning (<0, 0, >0) according to whether the epochSeconds...
Definition: LocalDate.h:338
static const uint8_t kMonday
Monday ISO 8601 number.
Definition: LocalDate.h:66
uint8_t dayOfWeek() const
Calculate the day of week given the (year, month, day).
Definition: LocalDate.h:248