AceTime  1.11.7
Date and time classes for Arduino that support timezones from the TZ Database.
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 int32_t kSecondsSinceUnixEpoch = 946684800;
46 
51  static const int8_t kInvalidYearTiny = INT8_MIN;
52 
57  static const int8_t kMinYearTiny = INT8_MIN + 1;
58 
63  static const int8_t kMaxYearTiny = INT8_MAX;
64 
66  static const int32_t kInvalidEpochDays = INT32_MIN;
67 
69  static const int32_t kInvalidEpochSeconds = INT32_MIN;
70 
72  static const int32_t kInvalidUnixDays = INT32_MIN;
73 
75  static const int32_t kInvalidUnixSeconds = INT32_MIN;
76 
78  static const int64_t kInvalidUnixSeconds64 = INT64_MIN;
79 
81  static const int64_t kMaxValidUnixSeconds64 =
82  (int64_t) (INT32_MAX) + kSecondsSinceUnixEpoch;
83 
85  static const int64_t kMinValidUnixSeconds64 =
86  (int64_t) (INT32_MIN + 1) + kSecondsSinceUnixEpoch;
87 
92  static const int32_t kDaysSinceUnixEpoch = 10957;
93 
95  static const uint8_t kMonday = 1;
96 
98  static const uint8_t kTuesday = 2;
99 
101  static const uint8_t kWednesday = 3;
102 
104  static const uint8_t kThursday = 4;
105 
107  static const uint8_t kFriday = 5;
108 
110  static const uint8_t kSaturday = 6;
111 
113  static const uint8_t kSunday = 7;
114 
123  static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day) {
124  int8_t yearTiny = isYearValid(year)
125  ? year - kEpochYear
127  return LocalDate(yearTiny, month, day);
128  }
129 
131  static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month,
132  uint8_t day) {
133  return LocalDate(yearTiny, month, day);
134  }
135 
143  static LocalDate forEpochDays(int32_t epochDays) {
144  int16_t year;
145  uint8_t month;
146  uint8_t day;
147  if (epochDays == kInvalidEpochDays) {
148  year = month = day = 0;
149  } else {
150  extractYearMonthDay(epochDays, year, month, day);
151  }
152  return forComponents(year, month, day);
153  }
154 
156  static LocalDate forUnixDays(int32_t unixDays) {
157  if (unixDays == kInvalidEpochDays) {
158  return forError();
159  } else {
160  return forEpochDays(unixDays - kDaysSinceUnixEpoch);
161  }
162  }
163 
175  static LocalDate forEpochSeconds(acetime_t epochSeconds) {
176  if (epochSeconds == kInvalidEpochSeconds) {
177  return forError();
178  } else {
179  // integer floor-division towards -infinity
180  int32_t days = (epochSeconds < 0)
181  ? (epochSeconds + 1) / 86400 - 1
182  : epochSeconds / 86400;
183  return forEpochDays(days);
184  }
185  }
186 
194  static LocalDate forUnixSeconds(int32_t unixSeconds) {
195  if (unixSeconds == kInvalidUnixSeconds) {
196  return forError();
197  } else {
198  return forEpochSeconds(unixSeconds - kSecondsSinceUnixEpoch);
199  }
200  }
201 
209  static LocalDate forUnixSeconds64(int64_t unixSeconds) {
210  if (unixSeconds == kInvalidUnixSeconds64
211  || unixSeconds > kMaxValidUnixSeconds64
212  || unixSeconds < kMinValidUnixSeconds64) {
213  return forError();
214  } else {
215  return forEpochSeconds(
216  (acetime_t) (unixSeconds - kSecondsSinceUnixEpoch));
217  }
218  }
219 
229  static LocalDate forDateString(const char* dateString);
230 
238  static LocalDate forDateStringChainable(const char*& dateString);
239 
244  static LocalDate forError() {
245  return LocalDate(kInvalidYearTiny, 0, 0);
246  }
247 
249  static bool isLeapYear(int16_t year) {
250  return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
251  }
252 
254  static bool isYearValid(int16_t year) {
255  return year >= kEpochYear + kMinYearTiny
256  && year <= kEpochYear + kMaxYearTiny;
257  }
258 
260  static uint8_t daysInMonth(int16_t year, uint8_t month) {
261  uint8_t days = sDaysInMonth[month - 1];
262  return (month == 2 && isLeapYear(year)) ? days + 1 : days;
263  }
264 
266  explicit LocalDate() {}
267 
269  int16_t year() const { return mYearTiny + kEpochYear; }
270 
272  void year(int16_t year) { mYearTiny = year - kEpochYear; }
273 
279  int8_t yearTiny() const { return mYearTiny; }
280 
286  void yearTiny(int8_t yearTiny) { mYearTiny = yearTiny; }
287 
289  uint8_t month() const { return mMonth; }
290 
292  void month(uint8_t month) { mMonth = month; }
293 
295  uint8_t day() const { return mDay; }
296 
298  void day(uint8_t day) { mDay = day; }
299 
306  uint8_t dayOfWeek() const {
307  // The "year" starts in March to shift leap year calculation to end.
308  int16_t y = year() - (mMonth < 3);
309  int16_t d = y + y/4 - y/100 + y/400 + sDayOfWeek[mMonth-1] + mDay;
310 
311  // 2000-1-1 was a Saturday=6, so set the offsets accordingly
312  return (d < -1) ? (d + 1) % 7 + 8 : (d + 1) % 7 + 1;
313  }
314 
316  bool isError() const {
317  return mYearTiny == kInvalidYearTiny
318  || mDay < 1 || mDay > 31
319  || mMonth < 1 || mMonth > 12;
320  }
321 
338  int32_t toEpochDays() const {
339  if (isError()) return kInvalidEpochDays;
340 
341  // From wiki article:
342  //
343  // JDN = (1461 x (Y + 4800 + (M - 14)/12))/4
344  // + (367 x (M - 2 - 12 x ((M - 14)/12)))/12
345  // - (3 x ((Y + 4900 + (M - 14)/12)/100))/4
346  // + D - 32075
347  // JDN2000 = JDN - 2451545
348  //
349  // It looks like the formula needs to be done using signed integers
350  // because it depends on the modulo operation (%) to truncate towards 0
351  // for negative numbers.
352 
353  int8_t mm = (mMonth - 14)/12;
354  int16_t yy = year();
355  int32_t jdn = ((int32_t) 1461 * (yy + 4800 + mm))/4
356  + (367 * (mMonth - 2 - 12 * mm))/12
357  - (3 * ((yy + 4900 + mm)/100))/4
358  + mDay - 32075;
359  return jdn - kDaysSinceJulianEpoch;
360  }
361 
363  int32_t toUnixDays() const {
364  if (isError()) return kInvalidUnixDays;
365  return toEpochDays() + kDaysSinceUnixEpoch;
366  }
367 
378  acetime_t toEpochSeconds() const {
379  if (isError()) return kInvalidEpochSeconds;
380  return 86400 * toEpochDays();
381  }
382 
386  int32_t toUnixSeconds() const {
387  if (isError()) return kInvalidUnixSeconds;
388  return 86400 * toUnixDays();
389  }
390 
394  int64_t toUnixSeconds64() const {
395  if (isError()) return kInvalidUnixSeconds64;
396  return (int64_t) 86400 * toUnixDays();
397  }
398 
405  int8_t compareTo(const LocalDate& that) const {
406  if (mYearTiny < that.mYearTiny) return -1;
407  if (mYearTiny > that.mYearTiny) return 1;
408  if (mMonth < that.mMonth) return -1;
409  if (mMonth > that.mMonth) return 1;
410  if (mDay < that.mDay) return -1;
411  if (mDay > that.mDay) return 1;
412  return 0;
413  }
414 
421  void printTo(Print& printer) const;
422 
423  // Use default copy constructor and assignment operator.
424  LocalDate(const LocalDate&) = default;
425  LocalDate& operator=(const LocalDate&) = default;
426 
427  private:
428  friend bool operator==(const LocalDate& a, const LocalDate& b);
429 
434  static const int32_t kDaysSinceJulianEpoch = 2451545;
435 
437  static const uint8_t kDateStringLength = 10;
438 
444  static const uint8_t sDayOfWeek[12];
445 
447  static const uint8_t sDaysInMonth[12];
448 
450  explicit LocalDate(int8_t yearTiny, uint8_t month, uint8_t day):
451  mYearTiny(yearTiny),
452  mMonth(month),
453  mDay(day) {}
454 
460  static void extractYearMonthDay(int32_t epochDays, int16_t& year,
461  uint8_t& month, uint8_t& day) {
462  uint32_t J = epochDays + kDaysSinceJulianEpoch;
463  uint32_t f = J + 1401 + (((4 * J + 274277 ) / 146097) * 3) / 4 - 38;
464  uint32_t e = 4 * f + 3;
465  uint32_t g = e % 1461 / 4;
466  uint32_t h = 5 * g + 2;
467  day = (h % 153) / 5 + 1;
468  month = (h / 153 + 2) % 12 + 1;
469  year = (e / 1461) - 4716 + (12 + 2 - month) / 12;
470 
471  // 2000-01-01 is Saturday (7)
472  //dayOfWeek = (epochDays + 6) % 7 + 1;
473  }
474 
479  int8_t mYearTiny; // [-127, 127], -128 indicates error
480 
481  uint8_t mMonth; // [1, 12], 0 indicates error
482  uint8_t mDay; // [1, 31], 0 indicates error
483 };
484 
486 inline bool operator==(const LocalDate& a, const LocalDate& b) {
487  return a.mDay == b.mDay
488  && a.mMonth == b.mMonth
489  && a.mYearTiny == b.mYearTiny;
490 }
491 
493 inline bool operator!=(const LocalDate& a, const LocalDate& b) {
494  return ! (a == b);
495 }
496 
497 }
498 
499 #endif
The date (year, month, day) representing the date without regards to time zone.
Definition: LocalDate.h:36
static const int16_t kEpochYear
Base year of epoch.
Definition: LocalDate.h:39
friend bool operator==(const LocalDate &a, const LocalDate &b)
Return true if two LocalDate objects are equal in all components.
Definition: LocalDate.h:486
static const uint8_t kWednesday
Wednesday ISO 8601 number.
Definition: LocalDate.h:101
static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day)
Factory method using separated year, month and day fields.
Definition: LocalDate.h:123
static bool isLeapYear(int16_t year)
True if year is a leap year.
Definition: LocalDate.h:249
static const int32_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:45
static const int32_t kInvalidEpochDays
Sentinel epochDays which indicates an error.
Definition: LocalDate.h:66
bool isError() const
Return true if any component indicates an error condition.
Definition: LocalDate.h:316
static const uint8_t kTuesday
Tuesday ISO 8601 number.
Definition: LocalDate.h:98
int32_t toUnixDays() const
Return the number of days since Unix epoch (1970-01-01 00:00:00).
Definition: LocalDate.h:363
static LocalDate forError()
Factory method that returns a LocalDate which represents an error condition.
Definition: LocalDate.h:244
static const int64_t kMaxValidUnixSeconds64
Maximum 64-bit Unix seconds supported by acetime_t.
Definition: LocalDate.h:81
static LocalDate forUnixSeconds64(int64_t unixSeconds)
Factory method that takes the 64-bit number of seconds since Unix Epoch of 1970-01-01.
Definition: LocalDate.h:209
void day(uint8_t day)
Set the day of the month.
Definition: LocalDate.h:298
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
Definition: LocalDate.h:279
static const int64_t kInvalidUnixSeconds64
Sentinel 64-bit unixSeconds which indicates an error.
Definition: LocalDate.h:78
uint8_t dayOfWeek() const
Calculate the day of week given the (year, month, day).
Definition: LocalDate.h:306
static const uint8_t kFriday
Friday ISO 8601 number.
Definition: LocalDate.h:107
static const int32_t kInvalidUnixDays
Sentinel unixDays which indicates an error.
Definition: LocalDate.h:72
int64_t toUnixSeconds64() const
Return the number of seconds since Unix epoch (1970-01-01 00:00:00).
Definition: LocalDate.h:394
LocalDate()
Default constructor does nothing.
Definition: LocalDate.h:266
void printTo(Print &printer) const
Print LocalDate to 'printer' in ISO 8601 format, along with the day of week.
Definition: LocalDate.cpp:46
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:405
int32_t toUnixSeconds() const
Return the number of seconds since Unix epoch (1970-01-01 00:00:00).
Definition: LocalDate.h:386
static const int64_t kMinValidUnixSeconds64
Minimum 64-bit Unix seconds supported by acetime_t.
Definition: LocalDate.h:85
void yearTiny(int8_t yearTiny)
Set the single-byte year offset from year 2000.
Definition: LocalDate.h:286
static const int8_t kMinYearTiny
Sentinel yearTiny which represents the smallest year, effectively -Infinity.
Definition: LocalDate.h:57
static const int8_t kMaxYearTiny
Sentinel yearTiny which represents the largest year, effectively +Infinity.
Definition: LocalDate.h:63
static uint8_t daysInMonth(int16_t year, uint8_t month)
Return the number of days in the current month.
Definition: LocalDate.h:260
static const int32_t kInvalidUnixSeconds
Sentinel unixSeconds which indicates an error.
Definition: LocalDate.h:75
void month(uint8_t month)
Set the month.
Definition: LocalDate.h:292
static LocalDate forUnixSeconds(int32_t unixSeconds)
Factory method that takes the number of seconds since Unix Epoch of 1970-01-01.
Definition: LocalDate.h:194
static bool isYearValid(int16_t year)
Return true if year is within valid range of [1873, 2127].
Definition: LocalDate.h:254
static LocalDate forDateString(const char *dateString)
Factory method.
Definition: LocalDate.cpp:65
static const int32_t kInvalidEpochSeconds
Sentinel epochSeconds which indicates an error.
Definition: LocalDate.h:69
int16_t year() const
Return the full year instead of just the last 2 digits.
Definition: LocalDate.h:269
static const int32_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:92
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since AceTime epoch of 2000-01-01.
Definition: LocalDate.h:175
int32_t toEpochDays() const
Return number of days since AceTime epoch (2000-01-01 00:00:00Z).
Definition: LocalDate.h:338
static const int8_t kInvalidYearTiny
Sentinel yearTiny which indicates an error condition or sometimes a year that 'does not exist'.
Definition: LocalDate.h:51
static const uint8_t kThursday
Thursday ISO 8601 number.
Definition: LocalDate.h:104
static const uint8_t kSaturday
Saturday ISO 8601 number.
Definition: LocalDate.h:110
static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month, uint8_t day)
Factory method using components with an int8_t yearTiny.
Definition: LocalDate.h:131
static const uint8_t kMonday
Monday ISO 8601 number.
Definition: LocalDate.h:95
static LocalDate forUnixDays(int32_t unixDays)
Factory method using the number of days since Unix epoch 1970-01-1.
Definition: LocalDate.h:156
static LocalDate forEpochDays(int32_t epochDays)
Factory method using the number of days since AceTime epoch of 2000-01-01.
Definition: LocalDate.h:143
uint8_t month() const
Return the month with January=1, December=12.
Definition: LocalDate.h:289
acetime_t toEpochSeconds() const
Return the number of seconds since AceTime epoch (2000-01-01 00:00:00).
Definition: LocalDate.h:378
static const uint8_t kSunday
Sunday ISO 8601 number.
Definition: LocalDate.h:113
static LocalDate forDateStringChainable(const char *&dateString)
Variant of forDateString() that updates the pointer to the next unprocessed character.
Definition: LocalDate.cpp:72
void year(int16_t year)
Set the year given the full year.
Definition: LocalDate.h:272
uint8_t day() const
Return the day of the month.
Definition: LocalDate.h:295