AceTime  2.1.0
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 <string.h> // strlen()
11 #include <Arduino.h>
12 #include <AceCommon.h> // printPad2To()
13 #include "Epoch.h"
14 #include "common/common.h"
15 #include "common/DateStrings.h" // DateStrings
16 
17 class Print;
18 
19 namespace ace_time {
20 
49 class LocalDate {
50  public:
61  static const int16_t kInvalidYear = INT16_MIN;
62 
72  static const int16_t kMinYear = 0;
73 
81  static const int16_t kMaxYear = 10000;
82 
84  static const int32_t kInvalidEpochDays = INT32_MIN;
85 
87  static const int32_t kInvalidEpochSeconds = INT32_MIN;
88 
90  static const int64_t kInvalidUnixSeconds64 = INT64_MIN;
91 
98  static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
99 
105  static const acetime_t kMaxEpochSeconds = INT32_MAX;
106 
108  static const uint8_t kMonday = 1;
109 
111  static const uint8_t kTuesday = 2;
112 
114  static const uint8_t kWednesday = 3;
115 
117  static const uint8_t kThursday = 4;
118 
120  static const uint8_t kFriday = 5;
121 
123  static const uint8_t kSaturday = 6;
124 
126  static const uint8_t kSunday = 7;
127 
128  // Utility functions
129  public:
131  static bool isLeapYear(int16_t year) {
132  return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
133  }
134 
136  static uint8_t daysInMonth(int16_t year, uint8_t month) {
137  uint8_t days = sDaysInMonth[month - 1];
138  return (month == 2 && isLeapYear(year)) ? days + 1 : days;
139  }
140 
142  static bool isYearValid(int16_t year) {
143  return year >= kMinYear && year <= kMaxYear;
144  }
145 
146  // Factory methods.
147  public:
157  int16_t year, uint8_t month, uint8_t day) {
159  return LocalDate(year, month, day);
160  }
161 
169  static LocalDate forEpochDays(int32_t epochDays) {
170  int16_t year;
171  uint8_t month;
172  uint8_t day;
173  if (epochDays == kInvalidEpochDays) {
174  year = kInvalidYear;
175  month = 0;
176  day = 0;
177  } else {
178  // shift relative to Epoch::kConverterEpochYear
180  ACE_TIME_EPOCH_CONVERTER::fromEpochDays(epochDays, year, month, day);
181  }
182  return forComponents(year, month, day);
183  }
184 
186  static LocalDate forUnixDays(int32_t unixDays) {
187  if (unixDays == kInvalidEpochDays) {
188  return forError();
189  }
190 
191  int32_t days = unixDays - Epoch::daysToCurrentEpochFromUnixEpoch();
192  return forEpochDays(days);
193  }
194 
208  static LocalDate forEpochSeconds(acetime_t epochSeconds) {
209  if (epochSeconds == kInvalidEpochSeconds) {
210  return forError();
211  }
212 
213  // integer floor-division towards -infinity
214  int32_t days = (epochSeconds < 0)
215  ? (epochSeconds + 1) / 86400 - 1
216  : epochSeconds / 86400;
217  return forEpochDays(days);
218  }
219 
227  static LocalDate forUnixSeconds64(int64_t unixSeconds) {
228  if (unixSeconds == kInvalidUnixSeconds64) {
229  return forError();
230  } else {
231  int64_t epochSeconds64 = unixSeconds
233  int32_t days = (epochSeconds64 < 0)
234  ? (epochSeconds64 + 1) / 86400 - 1
235  : epochSeconds64 / 86400;
236  return forEpochDays(days);
237  }
238  }
239 
249  static LocalDate forDateString(const char* dateString) {
250  if (strlen(dateString) < kDateStringLength) {
251  return forError();
252  }
253  return forDateStringChainable(dateString);
254  }
255 
263  static LocalDate forDateStringChainable(const char*& dateString) {
264  const char* s = dateString;
265 
266  // year (assumes 4 digit year)
267  int16_t year = (*s++ - '0');
268  year = 10 * year + (*s++ - '0');
269  year = 10 * year + (*s++ - '0');
270  year = 10 * year + (*s++ - '0');
271 
272  // '-'
273  s++;
274 
275  // month
276  uint8_t month = (*s++ - '0');
277  month = 10 * month + (*s++ - '0');
278 
279  // '-'
280  s++;
281 
282  // day
283  uint8_t day = (*s++ - '0');
284  day = 10 * day + (*s++ - '0');
285 
286  dateString = s;
287  return forComponents(year, month, day);
288  }
289 
294  static LocalDate forError() {
295  return LocalDate(kInvalidYear, 0, 0);
296  }
297 
298  // Instance methods.
299  public:
301  explicit LocalDate() = default;
302 
304  int16_t year() const { return mYear; }
305 
307  void year(int16_t year) { mYear = year; }
308 
310  uint8_t month() const { return mMonth; }
311 
313  void month(uint8_t month) { mMonth = month; }
314 
316  uint8_t day() const { return mDay; }
317 
319  void day(uint8_t day) { mDay = day; }
320 
327  uint8_t dayOfWeek() const {
328  // The "year" starts in March to shift leap year calculation to end.
329  int16_t y = year() - (mMonth < 3);
330 
331  // Each year shifts the day of week by one. Each leap year by one.
332  // Except every 100 years. Unless divisible by 400.
333  int16_t d = y + y/4 - y/100 + y/400 + sDayOfWeek[mMonth-1] + mDay;
334 
335  // 2000-01-01 was a Saturday=6, so set the offsets accordingly
336  return (d < -1) ? (d + 1) % 7 + 8 : (d + 1) % 7 + 1;
337  }
338 
340  bool isError() const {
341  return mYear == kInvalidYear
342  || mDay < 1 || mDay > 31
343  || mMonth < 1 || mMonth > 12;
344  }
345 
355  int32_t toEpochDays() const {
356  if (isError()) return kInvalidEpochDays;
357  int32_t days = ACE_TIME_EPOCH_CONVERTER::toEpochDays(mYear, mMonth, mDay)
359  return days;
360  }
361 
363  int32_t toUnixDays() const {
364  if (isError()) return kInvalidEpochDays;
366  }
367 
375  if (isError()) return kInvalidEpochSeconds;
376  return (int32_t) 86400 * toEpochDays();
377  }
378 
382  int64_t toUnixSeconds64() const {
383  if (isError()) return kInvalidUnixSeconds64;
384  return (int64_t) 86400 * toUnixDays();
385  }
386 
393  int8_t compareTo(const LocalDate& that) const {
394  if (mYear < that.mYear) return -1;
395  if (mYear > that.mYear) return 1;
396  if (mMonth < that.mMonth) return -1;
397  if (mMonth > that.mMonth) return 1;
398  if (mDay < that.mDay) return -1;
399  if (mDay > that.mDay) return 1;
400  return 0;
401  }
402 
409  void printTo(Print& printer) const {
410  if (isError()) {
411  printer.print(F("<Invalid LocalDate>"));
412  return;
413  }
414 
415  // Date
416  using ace_common::printPad2To;
417  printer.print(year());
418  printer.print('-');
419  printPad2To(printer, mMonth, '0');
420  printer.print('-');
421  printPad2To(printer, mDay, '0');
422  printer.print(' ');
423 
424  // Week day
425  DateStrings ds;
426  printer.print(ds.dayOfWeekLongString(dayOfWeek()));
427  }
428 
429  // Use default copy constructor and assignment operator.
430  LocalDate(const LocalDate&) = default;
431  LocalDate& operator=(const LocalDate&) = default;
432 
433  private:
434  friend bool operator==(
435  const LocalDate& a, const LocalDate& b);
436 
438  explicit LocalDate(int16_t year, uint8_t month, uint8_t day):
439  mYear(year),
440  mMonth(month),
441  mDay(day) {}
442 
443  private:
445  static const uint8_t kDateStringLength = 10;
446 
452  static const uint8_t sDayOfWeek[12];
453 
455  static const uint8_t sDaysInMonth[12];
456 
457  int16_t mYear; // [0,10000], INT16_MIN indicates error
458  uint8_t mMonth; // [1, 12], 0 indicates error
459  uint8_t mDay; // [1, 31], 0 indicates error
460 };
461 
463 inline bool operator==(const LocalDate& a, const LocalDate& b) {
464  return a.mDay == b.mDay
465  && a.mMonth == b.mMonth
466  && a.mYear == b.mYear;
467 }
468 
470 inline bool operator!=(const LocalDate& a, const LocalDate& b) {
471  return ! (a == b);
472 }
473 
474 }
475 
476 #endif
Class that translates a numeric month (1-12) or dayOfWeek (1-7) into a human readable string.
Definition: DateStrings.h:26
const char * dayOfWeekLongString(uint8_t dayOfWeek)
Return the short dayOfWeek name.
Definition: DateStrings.h:56
static int32_t daysToCurrentEpochFromConverterEpoch()
Number of days from the converter epoch (2000-01-01) to the current epoch.
Definition: Epoch.h:49
static int32_t daysToCurrentEpochFromUnixEpoch()
Return the number of days from the Unix epoch (1970-01-01T00:00:00) to the current epoch.
Definition: Epoch.h:57
static int64_t secondsToCurrentEpochFromUnixEpoch64()
Return the number of seconds from the Unix epoch (1970-01-01T00:00:00) to the current epoch.
Definition: Epoch.h:68
The date (year, month, day) representing the date without regards to time zone.
Definition: LocalDate.h:49
friend bool operator==(const LocalDate &a, const LocalDate &b)
Return true if two LocalDate objects are equal in all components.
Definition: LocalDate.h:463
static const uint8_t kWednesday
Wednesday ISO 8601 number.
Definition: LocalDate.h:114
static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day)
Factory method using separated year, month and day fields.
Definition: LocalDate.h:156
static const int16_t kMaxYear
The largest year that is expected to be handled by LocalDate.
Definition: LocalDate.h:81
static bool isLeapYear(int16_t year)
True if year is a leap year.
Definition: LocalDate.h:131
static const int32_t kInvalidEpochDays
Sentinel epochDays which indicates an error.
Definition: LocalDate.h:84
bool isError() const
Return true if any component indicates an error condition.
Definition: LocalDate.h:340
static const uint8_t kTuesday
Tuesday ISO 8601 number.
Definition: LocalDate.h:111
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:294
static const acetime_t kMinEpochSeconds
Minimum valid epochSeconds.
Definition: LocalDate.h:98
static LocalDate forDateString(const char *dateString)
Factory method.
Definition: LocalDate.h:249
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:227
void day(uint8_t day)
Set the day of the month.
Definition: LocalDate.h:319
static const int64_t kInvalidUnixSeconds64
Sentinel unixSeconds64 which indicates an error.
Definition: LocalDate.h:90
uint8_t dayOfWeek() const
Calculate the day of week given the (year, month, day).
Definition: LocalDate.h:327
static const uint8_t kFriday
Friday ISO 8601 number.
Definition: LocalDate.h:120
static const int16_t kMinYear
The smallest year that is expected to be handled by LocalDate.
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:382
void printTo(Print &printer) const
Print LocalDate to 'printer' in ISO 8601 format, along with the day of week.
Definition: LocalDate.h:409
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:393
static uint8_t daysInMonth(int16_t year, uint8_t month)
Return the number of days in the given (year, month).
Definition: LocalDate.h:136
void month(uint8_t month)
Set the month.
Definition: LocalDate.h:313
static bool isYearValid(int16_t year)
Return true if year is within the range of [0,10000]
Definition: LocalDate.h:142
static const int32_t kInvalidEpochSeconds
Sentinel epochSeconds which indicates an error.
Definition: LocalDate.h:87
int16_t year() const
Return the year.
Definition: LocalDate.h:304
static const acetime_t kMaxEpochSeconds
Maximum valid epochSeconds.
Definition: LocalDate.h:105
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since the current epoch year given by currentEpochYear().
Definition: LocalDate.h:208
int32_t toEpochDays() const
Return number of days since the current epoch year sCurrentEpochYear.
Definition: LocalDate.h:355
static const uint8_t kThursday
Thursday ISO 8601 number.
Definition: LocalDate.h:117
static const uint8_t kSaturday
Saturday ISO 8601 number.
Definition: LocalDate.h:123
static const uint8_t kMonday
Monday ISO 8601 number.
Definition: LocalDate.h:108
static LocalDate forUnixDays(int32_t unixDays)
Factory method using the number of days since Unix epoch 1970-01-01.
Definition: LocalDate.h:186
LocalDate()=default
Default constructor does nothing.
static const int16_t kInvalidYear
Sentinel year which indicates one or more of the following conditions:
Definition: LocalDate.h:61
static LocalDate forEpochDays(int32_t epochDays)
Factory method using the number of days since the current epoch (usually 2000-01-01).
Definition: LocalDate.h:169
static LocalDate forDateStringChainable(const char *&dateString)
Variant of forDateString() that updates the pointer to the next unprocessed character.
Definition: LocalDate.h:263
uint8_t month() const
Return the month with January=1, December=12.
Definition: LocalDate.h:310
acetime_t toEpochSeconds() const
Return the number of seconds since the currentEpochYear().
Definition: LocalDate.h:374
static const uint8_t kSunday
Sunday ISO 8601 number.
Definition: LocalDate.h:126
void year(int16_t year)
Set the year.
Definition: LocalDate.h:307
uint8_t day() const
Return the day of the month.
Definition: LocalDate.h:316
Identifiers used by implementation code which need to be publically exported.
int32_t acetime_t
Type for the number of seconds from epoch.
Definition: common.h:24