AceTime  0.1
Date and time classes for Arduino that supports the TZ DAtabase, and a system clock synchronized from an NTP server or an RTC chip.
BasicZoneSpecifier.h
1 #ifndef ACE_TIME_BASIC_ZONE_SPECIFIER_H
2 #define ACE_TIME_BASIC_ZONE_SPECIFIER_H
3 
4 #include <Arduino.h>
5 #include <string.h> // strchr()
6 #include <stdint.h>
7 #include "common/ZonePolicy.h"
8 #include "common/ZoneInfo.h"
9 #include "common/logger.h"
10 #include "TimeOffset.h"
11 #include "LocalDate.h"
12 #include "OffsetDateTime.h"
13 #include "ZoneSpecifier.h"
14 
15 class BasicZoneSpecifierTest_init_primitives;
16 class BasicZoneSpecifierTest_init;
17 class BasicZoneSpecifierTest_createAbbreviation;
18 class BasicZoneSpecifierTest_calcStartDayOfMonth;
19 class BasicZoneSpecifierTest_calcRuleOffsetCode;
20 
21 namespace ace_time {
22 
23 namespace basic {
24 
31 struct Transition {
39  static const uint8_t kAbbrevSize = 6 + 1;
40 
42  const ZoneEra* era;
43 
51  const ZoneRule* rule;
52 
54  int8_t yearTiny;
55 
57  acetime_t startEpochSeconds;
58 
64  int8_t offsetCode;
65 
68 
70  void log() const {
71  if (sizeof(acetime_t) == sizeof(int)) {
72  logging::println("startEpochSeconds: %d", startEpochSeconds);
73  } else {
74  logging::println("startEpochSeconds: %ld", startEpochSeconds);
75  }
76  logging::println("offsetCode: %d", offsetCode);
77  logging::println("abbrev: %s", abbrev);
78  if (rule != nullptr) {
79  logging::println("Rule.fromYear: %d", rule->fromYearTiny);
80  logging::println("Rule.toYear: %d", rule->toYearTiny);
81  logging::println("Rule.inMonth: %d", rule->inMonth);
82  logging::println("Rule.onDayOfMonth: %d", rule->onDayOfMonth);
83  }
84  }
85 };
86 
87 } // namespace basic
88 
141  public:
146  explicit BasicZoneSpecifier(const basic::ZoneInfo* zoneInfo):
147  ZoneSpecifier(kTypeBasic),
148  mZoneInfo(zoneInfo) {}
149 
151  const basic::ZoneInfo* getZoneInfo() const { return mZoneInfo; }
152 
153  TimeOffset getUtcOffset(acetime_t epochSeconds) const override {
154  const basic::Transition* transition = getTransition(epochSeconds);
155  if (!transition) return TimeOffset::forError();
156  return TimeOffset::forOffsetCode(transition->offsetCode);
157  }
158 
159  TimeOffset getDeltaOffset(acetime_t epochSeconds) const override {
160  const basic::Transition* transition = getTransition(epochSeconds);
161  if (!transition) return TimeOffset::forError();
162  if (transition->rule == nullptr) return TimeOffset();
163  return TimeOffset::forOffsetCode(transition->rule->deltaCode);
164  }
165 
166  const char* getAbbrev(acetime_t epochSeconds) const override {
167  const basic::Transition* transition = getTransition(epochSeconds);
168  if (!transition) return "";
169  return transition->abbrev;
170  }
171 
187  TimeOffset getUtcOffsetForDateTime(const LocalDateTime& ldt) const override {
188  init(ldt.getLocalDate());
189  if (mIsOutOfBounds) return TimeOffset::forError();
190 
191  // First guess at the TimeOffset using Jan 1 of the given year.
192  acetime_t initialEpochSeconds =
193  LocalDate::forComponents(ldt.year(), 1, 1).toEpochSeconds();
194  TimeOffset initialTimeOffset = getUtcOffset(initialEpochSeconds);
195 
196  // Second guess at the TimeOffset using the first TimeOffset.
197  OffsetDateTime odt(ldt, initialTimeOffset);
198  acetime_t epochSeconds = odt.toEpochSeconds();
199  return getUtcOffset(epochSeconds);
200  }
201 
202  void printTo(Print& printer) const override;
203 
205  void log() const {
206  if (!mIsFilled) {
207  logging::println("*not initialized*");
208  return;
209  }
210  logging::println("mYear: %d", mYear);
211  logging::println("mNumTransitions: %d", mNumTransitions);
212  logging::println("---- PrevTransition");
213  mPrevTransition.log();
214  for (int i = 0; i < mNumTransitions; i++) {
215  logging::println("---- Transition: %d", i);
216  mTransitions[i].log();
217  }
218  }
219 
220  private:
221  friend class ::BasicZoneSpecifierTest_init_primitives;
222  friend class ::BasicZoneSpecifierTest_init;
223  friend class ::BasicZoneSpecifierTest_createAbbreviation;
224  friend class ::BasicZoneSpecifierTest_calcStartDayOfMonth;
225  friend class ::BasicZoneSpecifierTest_calcRuleOffsetCode;
226  friend class ExtendedZoneSpecifier; // calcStartDayOfMonth()
227 
229  static const uint8_t kMaxCacheEntries = 4;
230 
236  static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
237 
238  // Disable copy constructor and assignment operator.
239  BasicZoneSpecifier(const BasicZoneSpecifier&) = delete;
240  BasicZoneSpecifier& operator=(const BasicZoneSpecifier&) = delete;
241 
242  bool equals(const ZoneSpecifier& other) const override {
243  const auto& that = (const BasicZoneSpecifier&) other;
244  return getZoneInfo() == that.getZoneInfo();
245  }
246 
248  const basic::Transition* getTransition(acetime_t epochSeconds) const {
249  LocalDate ld = LocalDate::forEpochSeconds(epochSeconds);
250  init(ld);
251  return (mIsOutOfBounds) ? nullptr : findMatch(epochSeconds);
252  }
253 
279  void init(const LocalDate& ld) const {
280  int16_t year = ld.year();
281  if (ld.month() == 1 && ld.day() == 1) {
282  year--;
283  }
284  if (isFilled(year)) return;
285 
286  mYear = year;
287  mNumTransitions = 0; // clear cache
288 
289  if (year < mZoneInfo->zoneContext->startYear - 1
290  || mZoneInfo->zoneContext->untilYear < year) {
291  mIsOutOfBounds = true;
292  return;
293  }
294 
295  addRulePriorToYear(year);
296  addRulesForYear(year);
297  calcTransitions();
298  calcAbbreviations();
299 
300  mIsFilled = true;
301  mIsOutOfBounds = false;
302  }
303 
305  bool isFilled(int16_t year) const {
306  return mIsFilled && (year == mYear);
307  }
308 
313  void addRulePriorToYear(int16_t year) const {
314  int8_t yearTiny = year - LocalDate::kEpochYear;
315  int8_t priorYearTiny = yearTiny - 1;
316 
317  // Find the prior Era.
318  const basic::ZoneEra* const era = findZoneEraPriorTo(year);
319 
320  // If the prior ZoneEra is a simple Era (no zone policy), then create a
321  // Transition using a rule==nullptr. Otherwise, find the latest rule
322  // within the ZoneEra.
323  const basic::ZonePolicy* const zonePolicy = era->zonePolicy;
324  const basic::ZoneRule* latest = nullptr;
325  if (zonePolicy != nullptr) {
326  // Find the latest rule for the matching ZoneEra whose
327  // ZoneRule::toYearTiny < yearTiny. Assume that there are no more than
328  // 1 rule per month.
329  for (uint8_t i = 0; i < zonePolicy->numRules; i++) {
330  const basic::ZoneRule* const rule = &zonePolicy->rules[i];
331  // Check if rule is effective prior to the given year
332  if (rule->fromYearTiny < yearTiny) {
333  if ((latest == nullptr)
334  || compareZoneRule(year, rule, latest) > 0) {
335  latest = rule;
336  }
337  }
338  }
339  }
340 
341  mPrevTransition = {
342  era,
343  latest /*rule*/,
344  priorYearTiny /*yearTiny*/,
345  0 /*epochSeconds*/,
346  0 /*offsetCode*/,
347  {0} /*abbrev*/
348  };
349  }
350 
352  static int8_t compareZoneRule(int16_t year,
353  const basic::ZoneRule* a, const basic::ZoneRule* b) {
354  int16_t aYear = effectiveRuleYear(year, a);
355  int16_t bYear = effectiveRuleYear(year, b);
356  if (aYear < bYear) return -1;
357  if (aYear > bYear) return 1;
358  if (a->inMonth < b->inMonth) return -1;
359  if (a->inMonth > b->inMonth) return 1;
360  return 0;
361  }
362 
367  static int16_t effectiveRuleYear(int16_t year,
368  const basic::ZoneRule* rule) {
369  int8_t yearTiny = year - LocalDate::kEpochYear;
370  if (rule->toYearTiny < yearTiny) {
371  return rule->toYearTiny + LocalDate::kEpochYear;
372  }
373  if (rule->fromYearTiny < yearTiny) {
374  return year - 1;
375  }
376  return 0;
377  }
378 
380  void addRulesForYear(int16_t year) const {
381  const basic::ZoneEra* const era = findZoneEra(year);
382 
383  // If the ZonePolicy has no rules, then add a Transition which takes
384  // effect at the start time of the current year.
385  const basic::ZonePolicy* const zonePolicy = era->zonePolicy;
386  if (zonePolicy == nullptr) {
387  addRule(year, era, nullptr);
388  return;
389  }
390 
391  // If the ZonePolicy has rules, find all matching transitions, and add
392  // them to mTransitions, in sorted order according to the
393  // ZoneRule::inMonth field.
394  int8_t yearTiny = year - LocalDate::kEpochYear;
395  for (uint8_t i = 0; i < zonePolicy->numRules; i++) {
396  const basic::ZoneRule* const rule = &zonePolicy->rules[i];
397  if ((rule->fromYearTiny <= yearTiny) &&
398  (yearTiny <= rule->toYearTiny)) {
399  addRule(year, era, rule);
400  }
401  }
402  }
403 
418  void addRule(int16_t year, const basic::ZoneEra* era,
419  const basic::ZoneRule* rule) const {
420 
421  // If a zone needs more transitions than kMaxCacheEntries, the check below
422  // will cause the DST transition information to be inaccurate, and it is
423  // highly likely that this situation would be caught in the
424  // BasicValidationTest unit test. Since BasicValidationTest passes, I
425  // feel confident that those zones which need more than kMaxCacheEntries
426  // are already filtered out by tzcompiler.py due to constraints of
427  // BasicValidationTest which are checked by tzcompiler.py.
428  //
429  // Ideally, the tzcompiler.py script would explicitly remove those zones
430  // which need more than kMaxCacheEntries Transitions. But this would
431  // require a Python version of the BasicZoneSpecifier, and unfortunately,
432  // zone_specifier.py implements only the ExtendedZoneSpecifier algorithm
433  // An early version of zone_specifier.py may have implemented something
434  // close to BasicZoneSpecifier, and it may be available in the git
435  // history. But it seems like too much work right now to try to dig that
436  // out, just to implement the explicit check for kMaxCacheEntries. It
437  // would mean maintaining another version of zone_specifier.py.
438  if (mNumTransitions >= kMaxCacheEntries) return;
439 
440  // insert new element at the end of the list
441  int8_t yearTiny = year - LocalDate::kEpochYear;
442  mTransitions[mNumTransitions] = {
443  era,
444  rule,
445  yearTiny,
446  0 /*epochSeconds*/,
447  0 /*offsetCode*/,
448  {0} /*abbrev*/
449  };
450  mNumTransitions++;
451 
452  // perform an insertion sort
453  for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
454  basic::Transition& left = mTransitions[i - 1];
455  basic::Transition& right = mTransitions[i];
456  // assume only 1 rule per month
457  if ((left.rule != nullptr && right.rule != nullptr &&
458  left.rule->inMonth > right.rule->inMonth)
459  || (left.rule != nullptr && right.rule == nullptr)) {
460  basic::Transition tmp = left;
461  left = right;
462  right = tmp;
463  }
464  }
465  }
466 
472  const basic::ZoneEra* findZoneEra(int16_t year) const {
473  for (uint8_t i = 0; i < mZoneInfo->numEras; i++) {
474  const basic::ZoneEra* era = &mZoneInfo->eras[i];
475  if (year < era->untilYearTiny + LocalDate::kEpochYear) return era;
476  }
477  // Return the last ZoneEra if we run off the end.
478  return &mZoneInfo->eras[mZoneInfo->numEras-1];
479  }
480 
492  const basic::ZoneEra* findZoneEraPriorTo(int16_t year) const {
493  for (uint8_t i = 0; i < mZoneInfo->numEras; i++) {
494  const basic::ZoneEra* era = &mZoneInfo->eras[i];
495  if (year <= era->untilYearTiny + LocalDate::kEpochYear) return era;
496  }
497  // Return the last ZoneEra if we run off the end.
498  return &mZoneInfo->eras[mZoneInfo->numEras-1];
499  }
500 
502  void calcTransitions() const {
503  // Calculate epochSeconds and offsetCode for the prevTransition.
504  mPrevTransition.startEpochSeconds = kMinEpochSeconds;
505  int8_t deltaCode = (mPrevTransition.rule == nullptr)
506  ? 0 : mPrevTransition.rule->deltaCode;
507  mPrevTransition.offsetCode = mPrevTransition.era->offsetCode + deltaCode;
508  const basic::Transition* prevTransition = &mPrevTransition;
509 
510  // Loop through Transition items to calculate:
511  // 1) Transition::startEpochSeconds
512  // 2) Transition::offsetCode
513  for (uint8_t i = 0; i < mNumTransitions; i++) {
514  basic::Transition& transition = mTransitions[i];
515  const int16_t year = transition.yearTiny + LocalDate::kEpochYear;
516 
517  if (transition.rule == nullptr) {
518  // If the transition is simple (has no named rule), then the
519  // ZoneEra applies for the entire year (since BasicZoneSpecifier
520  // supports only whole year in the UNTIL field). The whole year UNTIL
521  // field has an implied 'w' modifier on 00:00, we don't need to call
522  // calcRuleOffsetCode() with a 'w', we can just use the previous
523  // transition's offset to calculate the startDateTime of this
524  // transition.
525  //
526  // Also, when transition.rule == nullptr, the mNumTransitions should
527  // be 1, since only a single transition is added by
528  // addRulesForYear().
529  const int8_t offsetCode = prevTransition->offsetCode;
531  year, 1, 1, 0, 0, 0,
532  TimeOffset::forOffsetCode(offsetCode));
533  transition.startEpochSeconds = startDateTime.toEpochSeconds();
534  transition.offsetCode = transition.era->offsetCode;
535  } else {
536  // In this case, the transition points to a named ZonePolicy, which
537  // means that there could be multiple ZoneRules associated with the
538  // given year. For each transition, determine the startEpochSeconds,
539  // and the effective offset code.
540 
541  // Determine the start date of the rule.
542  const uint8_t startDayOfMonth = calcStartDayOfMonth(
543  year, transition.rule->inMonth, transition.rule->onDayOfWeek,
544  transition.rule->onDayOfMonth);
545 
546  // Determine the offset of the 'atTimeModifier'. The 'w' modifier
547  // requires the offset of the previous transition.
548  const int8_t offsetCode = calcRuleOffsetCode(
549  prevTransition->offsetCode,
550  transition.era->offsetCode,
551  transition.rule->atTimeModifier);
552 
553  // startDateTime
554  const uint8_t atHour = transition.rule->atTimeCode / 4;
555  const uint8_t atMinute = (transition.rule->atTimeCode % 4) * 15;
557  year, transition.rule->inMonth, startDayOfMonth,
558  atHour, atMinute, 0 /*second*/,
559  TimeOffset::forOffsetCode(offsetCode));
560  transition.startEpochSeconds = startDateTime.toEpochSeconds();
561 
562  // Determine the effective offset code
563  transition.offsetCode =
564  transition.era->offsetCode + transition.rule->deltaCode;
565  }
566 
567  prevTransition = &transition;
568  }
569  }
570 
577  static uint8_t calcStartDayOfMonth(int16_t year, uint8_t month,
578  uint8_t onDayOfWeek, uint8_t onDayOfMonth) {
579  if (onDayOfWeek == 0) return onDayOfMonth;
580 
581 
582  // Convert "last{Xxx}" to "last{Xxx}>={daysInMonth-6}".
583  if (onDayOfMonth == 0) {
584  onDayOfMonth = LocalDate::daysInMonth(year, month) - 6;
585  }
586 
588  year, month, onDayOfMonth);
589  uint8_t dayOfWeekShift = (onDayOfWeek - limitDate.dayOfWeek() + 7) % 7;
590  return onDayOfMonth + dayOfWeekShift;
591  }
592 
599  static int8_t calcRuleOffsetCode(int8_t prevEffectiveOffsetCode,
600  int8_t currentBaseOffsetCode, uint8_t atModifier) {
601  if (atModifier == 'w') {
602  return prevEffectiveOffsetCode;
603  } else if (atModifier == 's') {
604  return currentBaseOffsetCode;
605  } else { // 'u', 'g' or 'z'
606  return 0;
607  }
608  }
609 
611  void calcAbbreviations() const {
612  calcAbbreviation(&mPrevTransition);
613  for (uint8_t i = 0; i < mNumTransitions; i++) {
614  calcAbbreviation(&mTransitions[i]);
615  }
616  }
617 
619  static void calcAbbreviation(basic::Transition* transition) {
620  createAbbreviation(
621  transition->abbrev,
623  transition->era->format,
624  transition->rule != nullptr ? transition->rule->deltaCode : 0,
625  transition->rule != nullptr ? transition->rule->letter : '\0');
626  }
627 
663  static void createAbbreviation(char* dest, uint8_t destSize,
664  const char* format, uint8_t deltaCode, char letter) {
665  // Check if RULES column empty.
666  if (deltaCode == 0 && letter == '\0') {
667  strncpy(dest, format, destSize);
668  dest[destSize - 1] = '\0';
669  return;
670  }
671 
672  // Check if FORMAT contains a '%'.
673  if (strchr(format, '%') != nullptr) {
674  copyAndReplace(dest, destSize, format, '%', letter);
675  } else {
676  // Check if FORMAT contains a '/'.
677  const char* slashPos = strchr(format, '/');
678  if (slashPos != nullptr) {
679  if (deltaCode == 0) {
680  uint8_t headLength = (slashPos - format);
681  if (headLength >= destSize) headLength = destSize - 1;
682  memcpy(dest, format, headLength);
683  dest[headLength] = '\0';
684  } else {
685  uint8_t tailLength = strlen(slashPos+1);
686  if (tailLength >= destSize) tailLength = destSize - 1;
687  memcpy(dest, slashPos+1, tailLength);
688  dest[tailLength] = '\0';
689  }
690  } else {
691  // Just copy the FORMAT disregarding the deltaCode and letter.
692  strncpy(dest, format, destSize);
693  dest[destSize - 1] = '\0';
694  }
695  }
696  }
697 
703  static void copyAndReplace(char* dst, uint8_t dstSize, const char* src,
704  char oldChar, char newChar) {
705  while (*src != '\0' && dstSize > 0) {
706  if (*src == oldChar) {
707  if (newChar == '-') {
708  src++;
709  } else {
710  *dst = newChar;
711  dst++;
712  src++;
713  dstSize--;
714  }
715  } else {
716  *dst++ = *src++;
717  dstSize--;
718  }
719  }
720 
721  if (dstSize == 0) {
722  --dst;
723  }
724  *dst = '\0';
725  }
726 
728  const basic::Transition* findMatch(acetime_t epochSeconds) const {
729  const basic::Transition* closestMatch = &mPrevTransition;
730  for (uint8_t i = 0; i < mNumTransitions; i++) {
731  const basic::Transition* m = &mTransitions[i];
732  if (m->startEpochSeconds <= epochSeconds) {
733  closestMatch = m;
734  }
735  }
736  return closestMatch;
737  }
738 
739  const basic::ZoneInfo* const mZoneInfo;
740 
741  mutable int16_t mYear = 0;
742  mutable bool mIsFilled = false;
743  mutable bool mIsOutOfBounds = false; // year is too early or late
744  mutable uint8_t mNumTransitions = 0;
745  mutable basic::Transition mTransitions[kMaxCacheEntries];
746  mutable basic::Transition mPrevTransition; // previous year's transition
747 };
748 
749 }
750 
751 #endif
static TimeOffset forError()
Return an error indicator.
Definition: TimeOffset.h:90
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
An entry in ZoneInfo which describes which ZonePolicy was being followed during a particular time per...
Definition: ZoneInfo.h:15
const ZoneEra * era
The ZoneEra that matched the given year.
static const uint8_t kAbbrevSize
Longest abbreviation currently seems to be 5 characters (https://www.timeanddate.com/time/zones/) but...
uint8_t month() const
Return the month with January=1, December=12.
Definition: LocalDate.h:215
static OffsetDateTime forComponents(int16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, TimeOffset timeOffset=TimeOffset())
Factory method using separated date, time, and UTC offset fields.
char abbrev[kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
A collection of transition rules which describe the DST rules of a given administrative region...
Definition: ZonePolicy.h:91
Representation of a given time zone, implemented as an array of ZoneEra records.
Definition: ZoneInfo.h:72
An implementation of ZoneSpecifier that works for all zones defined by the TZ Database (with some zon...
uint8_t const inMonth
Determined by the IN column.
Definition: ZonePolicy.h:21
acetime_t toEpochSeconds() const
Return seconds since AceTime epoch (2000-01-01 00:00:00Z), taking into account the offset zone...
acetime_t startEpochSeconds
The calculated transition time of the given rule.
int8_t const deltaCode
Determined by the SAVE column, containing the offset from UTC, in 15-min increments.
Definition: ZonePolicy.h:60
int8_t yearTiny
Year which applies to the ZoneEra or ZoneRule.
Base interface for ZoneSpecifier classes.
Definition: ZoneSpecifier.h:39
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
int16_t year() const
Return the year.
int8_t const deltaCode
If zonePolicy is nullptr, then this indicates the DST offset in 15 minute increments.
Definition: ZoneInfo.h:32
const ZoneRule * rule
The Zone transition rule that matched for the the given year.
int16_t year() const
Return the full year instead of just the last 2 digits.
Definition: LocalDate.h:203
uint8_t const atTimeModifier
Determined by the suffix in the AT column: &#39;w&#39;=wall; &#39;s&#39;=standard; &#39;u&#39;=meridian (&#39;g&#39; and &#39;z&#39; mean the...
Definition: ZonePolicy.h:54
const basic::ZoneInfo * getZoneInfo() const
Return the underlying ZoneInfo.
void log() const
Used only for debugging.
uint8_t day() const
Return the day of the month.
Definition: LocalDate.h:221
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since AceTime epoch of 2000-01-01.
Definition: LocalDate.h:145
static TimeOffset forOffsetCode(int8_t offsetCode)
Create TimeOffset from the offset code.
Definition: TimeOffset.h:97
const ZonePolicy *const zonePolicy
Zone policy, determined by the RULES column.
Definition: ZoneInfo.h:26
TimeOffset getUtcOffset(acetime_t epochSeconds) const override
Return the total UTC offset at epochSeconds, including DST offset.
The date (year, month, day) and time (hour, minute, second) fields representing the time with an offs...
Data structure that defines the start of a specific UTC offset as described by the matching ZoneEra a...
A thin wrapper that represents a time offset from a reference point, usually 00:00 at UTC...
Definition: TimeOffset.h:53
static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day)
Factory method using separated year, month and day fields.
Definition: LocalDate.h:102
uint8_t const onDayOfWeek
Determined by the ON column.
Definition: ZonePolicy.h:35
The date (year, month, day) representing the date without regards to time zone.
Definition: LocalDate.h:32
A time zone transition rule.
Definition: ZonePolicy.h:7
int8_t const fromYearTiny
FROM year as an offset from year 2000 stored as a single byte.
Definition: ZonePolicy.h:15
static const int16_t kEpochYear
Base year of epoch.
Definition: LocalDate.h:35
int8_t offsetCode
The total effective UTC offsetCode at the start of transition, including DST offset.
static uint8_t daysInMonth(int16_t year, uint8_t month)
Return the number of days in the current month.
Definition: LocalDate.h:194
uint8_t dayOfWeek() const
Calculate the day of week given the (year, month, day).
Definition: LocalDate.h:232
int8_t const offsetCode
UTC offset in 15 min increments.
Definition: ZoneInfo.h:20
BasicZoneSpecifier(const basic::ZoneInfo *zoneInfo)
Constructor.
TimeOffset getUtcOffsetForDateTime(const LocalDateTime &ldt) const override
Return the UTC offset matching the given the date/time components.
int8_t const toYearTiny
TO year as an offset from year 2000 stored as a single byte.
Definition: ZonePolicy.h:18
uint8_t const atTimeCode
Determined by the AT column in units of 15-minutes from 00:00.
Definition: ZonePolicy.h:47
uint8_t const letter
Determined by the LETTER column.
Definition: ZonePolicy.h:78
uint8_t const onDayOfMonth
Determined by the ON column.
Definition: ZonePolicy.h:41
An implementation of ZoneSpecifier that supports a subset of the zones containing in the TZ Database...
void log() const
Used only for debugging.
const char *const format
Zone abbreviations (e.g.
Definition: ZoneInfo.h:40