AceTime 2.4.0 Date and time classes for Arduino that support timezones from the TZ Database.

BasicZoneProcessor.h
1 /*
2 * MIT License
3 * Copyright (c) 2019 Brian T. Park
4 */
5
6 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_H
7 #define ACE_TIME_BASIC_ZONE_PROCESSOR_H
8
9 #include <stdint.h>
10 #include <AceCommon.h> // strncpy_T()
11 #include "../zoneinfo/infos.h"
12 #include "../zoneinfo/brokers.h"
13 #include "common/common.h" // kAbbrevSize
14 #include "common/logging.h"
15 #include "TimeOffset.h"
16 #include "LocalDate.h"
17 #include "OffsetDateTime.h"
18 #include "ZoneProcessor.h"
19
20 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG
21 #define ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG 0
22 #endif
23
24 class BasicZoneProcessorTest_priorYearOfRule;
25 class BasicZoneProcessorTest_compareRulesBeforeYear;
26 class BasicZoneProcessorTest_findLatestPriorRule;
27 class BasicZoneProcessorTest_findZoneEra;
28 class BasicZoneProcessorTest_init_primitives;
29 class BasicZoneProcessorTest_initForLocalDate;
30 class BasicZoneProcessorTest_setZoneKey;
31 class BasicZoneProcessorTest_calcRuleOffsetMinutes;
32
33 class Print;
34
35 namespace ace_time {
36 namespace basic {
37
58 template <typename ZIB, typename ZEB, typename ZPB, typename ZRB>
59 struct TransitionTemplate {
65 ZEB era;
66
76 ZRB rule;
77
79 acetime_t startEpochSeconds;
80
85 int16_t offsetMinutes;
86
88 int16_t deltaMinutes;
89
91 int16_t year;
92
98 uint8_t month;
99
107 char abbrev[internal::kAbbrevSize];
108
110 void log() const {
111 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
112 logging::printf("(%d/%d)", year, month);
113 if (sizeof(acetime_t) == sizeof(int)) {
114 logging::printf("; stEps: %d", startEpochSeconds);
115 } else {
116 logging::printf("; stEps: %ld", startEpochSeconds);
117 }
118 logging::printf("; offMin: %d", offsetMinutes);
119 logging::printf("; abbrev: %s", abbrev);
120 if (! rule.isNull()) {
121 logging::printf("; r.fromYear: %d", rule.fromYear());
122 logging::printf("; r.toYear: %d", rule.toYear());
123 logging::printf("; r.inMonth: %d", rule.inMonth());
124 logging::printf("; r.onDayOfMonth: %d", rule.onDayOfMonth());
125 }
126 logging::printf("\n");
127 }
128 }
129 };
130
132 inline int8_t compareYearMonth(int16_t aYear, uint8_t aMonth,
133 int16_t bYear, uint8_t bMonth) {
134 if (aYear < bYear) return -1;
135 if (aYear > bYear) return 1;
136 if (aMonth < bMonth) return -1;
137 if (aMonth > bMonth) return 1;
138 return 0;
139 }
140
141 } // namespace basic
142
200 template <typename ZIS, typename ZIB, typename ZEB, typename ZPB, typename ZRB>
201 class BasicZoneProcessorTemplate: public ZoneProcessor {
202 public:
204 typedef basic::TransitionTemplate<ZIB, ZEB, ZPB, ZRB> Transition;
205
206 bool isLink() const override {
207 return ! mZoneInfoBroker.targetInfo().isNull();
208 }
209
210 uint32_t getZoneId() const override {
211 return mZoneInfoBroker.zoneId();
212 }
213
242 FindResult findByLocalDateTime(
243 const LocalDateTime& ldt) const override {
244 FindResult result;
245 bool success = initForLocalDate(ldt.localDate());
246 if (!success) return result;
247
248 // 0) Use the UTC epochSeconds to get intial guess of offset.
249 acetime_t epochSeconds0 = ldt.toEpochSeconds();
250 auto result0 = findByEpochSeconds(epochSeconds0);
251 if (result0.type == FindResult::kTypeNotFound) return result;
252 auto offset0 = TimeOffset::forSeconds(
253 result0.reqStdOffsetSeconds + result0.reqDstOffsetSeconds);
254
255 // 1) Use offset0 to get the next epochSeconds and offset.
256 auto odt = OffsetDateTime::forLocalDateTimeAndOffset(ldt, offset0);
257 acetime_t epochSeconds1 = odt.toEpochSeconds();
258 auto result1 = findByEpochSeconds(epochSeconds1);
259 if (result1.type == FindResult::kTypeNotFound) return result;
260 auto offset1 = TimeOffset::forSeconds(
261 result1.reqStdOffsetSeconds + result1.reqDstOffsetSeconds);
262
263 // 2) Use offset1 to get the next epochSeconds and offset.
264 odt = OffsetDateTime::forLocalDateTimeAndOffset(ldt, offset1);
265 acetime_t epochSeconds2 = odt.toEpochSeconds();
266 auto result2 = findByEpochSeconds(epochSeconds2);
267 if (result2.type == FindResult::kTypeNotFound) return result;
268 auto offset2 = TimeOffset::forSeconds(
269 result2.reqStdOffsetSeconds + result2.reqDstOffsetSeconds);
270
271 // If offset1 and offset2 are equal, then we have an equilibrium
272 // and odt(1) must equal odt(2).
273 if (offset1 == offset2) {
274 // I think this happens for kTypeExact or kTypeOverlap, but the current
275 // algorithm cannot distinguish between the two, so let's pretend that
276 // it is kTypeExact. Pick either of result1 or result2.
277 result = result1;
278 } else {
279 // If the offsets don't match, then I think we have a kTypeGap.
280 // Pick the stdOffset and dstOffset that generate the later epochSeconds
281 // (the earlier transition), but convert into the LocalDateTime of the
282 // earlier epochSeconds (the later transition).
283 if (epochSeconds1 > epochSeconds2) {
284 result = result1;
285 } else {
286 result = result2;
287 }
288 result.type = FindResult::kTypeGap;
289 }
290
291 return result;
292 }
293
294 FindResult findByEpochSeconds(acetime_t epochSeconds) const override {
295 FindResult result;
296 const Transition* transition = getTransition(epochSeconds);
297 if (!transition) return result;
298
299 result.dstOffsetSeconds = transition->deltaMinutes * kSecPerMin;
300 result.stdOffsetSeconds = transition->offsetMinutes * kSecPerMin;
301 result.reqStdOffsetSeconds = result.stdOffsetSeconds;
302 result.reqDstOffsetSeconds = result.dstOffsetSeconds;
303 result.type = FindResult::kTypeExact;
304 result.abbrev = transition->abbrev;
305
306 return result;
307 }
308
309 void printNameTo(Print& printer) const override {
310 mZoneInfoBroker.printNameTo(printer);
311 }
312
313 void printShortNameTo(Print& printer) const override {
314 mZoneInfoBroker.printShortNameTo(printer);
315 }
316
317 void printTargetNameTo(Print& printer) const override {
318 if (isLink()) {
319 mZoneInfoBroker.targetInfo().printNameTo(printer);
320 }
321 }
322
323 void setZoneKey(uintptr_t zoneKey) override {
324 if (! mZoneInfoStore) return;
325 if (mZoneInfoBroker.equals(zoneKey)) return;
326
327 mZoneInfoBroker = mZoneInfoStore->createZoneInfoBroker(zoneKey);
328 mYear = LocalDate::kInvalidYear;
329 mNumTransitions = 0;
330 }
331
332 bool equalsZoneKey(uintptr_t zoneKey) const override {
333 return mZoneInfoBroker.equals(zoneKey);
334 }
335
337 void log() const {
338 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
339 logging::printf("BasicZoneProcessor:\n");
340 logging::printf(" mEpochYear: %d\n", mEpochYear);
341 logging::printf(" mYear: %d\n", mYear);
342 logging::printf(" mNumTransitions: %d\n", mNumTransitions);
343 for (int i = 0; i < mNumTransitions; i++) {
344 logging::printf(" mT[%d]=", i);
345 mTransitions[i].log();
346 }
347 }
348 }
349
356 void setZoneInfoStore(const ZIS* zoneInfoStore) {
357 mZoneInfoStore = zoneInfoStore;
358 }
359
360 protected:
361
372 explicit BasicZoneProcessorTemplate(
373 uint8_t type,
374 const ZIS* zoneInfoStore /*nullable*/,
375 uintptr_t zoneKey
376 ) :
377 ZoneProcessor(type),
378 mZoneInfoStore(zoneInfoStore)
379 {
380 setZoneKey(zoneKey);
381 }
382
383 private:
384 friend class ::BasicZoneProcessorTest_priorYearOfRule;
385 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
386 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
387 friend class ::BasicZoneProcessorTest_findZoneEra;
388 friend class ::BasicZoneProcessorTest_init_primitives;
389 friend class ::BasicZoneProcessorTest_initForLocalDate;
390 friend class ::BasicZoneProcessorTest_setZoneKey;
391 friend class ::BasicZoneProcessorTest_calcRuleOffsetMinutes;
392
403 static const uint8_t kMaxCacheEntries = 5;
404
410 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
411
412 // Disable copy constructor and assignment operator.
413 BasicZoneProcessorTemplate(const BasicZoneProcessorTemplate&) = delete;
414 BasicZoneProcessorTemplate& operator=(const BasicZoneProcessorTemplate&) =
415 delete;
416
417 bool equals(const ZoneProcessor& other) const override {
418 return mZoneInfoBroker.equals(
419 ((const BasicZoneProcessorTemplate&) other).mZoneInfoBroker);
420 }
421
423 const Transition* getTransition(acetime_t epochSeconds) const {
424 bool success = initForEpochSeconds(epochSeconds);
425 return (success) ? findMatch(epochSeconds) : nullptr;
426 }
427
456 bool initForLocalDate(const LocalDate& ld) const {
457 int16_t year = ld.year();
458 if (ld.month() == 1 && ld.day() == 1) {
459 year--;
460 }
461 // Restrict to [1,9999], even though LocalDate should be able to handle
462 // [0,10000].
463 if (year <= LocalDate::kMinYear || LocalDate::kMaxYear <= year) {
464 return false;
465 }
466
467 if (isFilled(year)) return true;
468 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
469 logging::printf("initForLocalDate(): %d (new year %d)\n",
470 ld.year(), year);
471 }
472
473 mYear = year;
474 mEpochYear = Epoch::currentEpochYear();
475 mNumTransitions = 0; // clear cache
476
477 ZEB priorEra = addTransitionPriorToYear(year);
478 ZEB currentEra = addTransitionsForYear(year, priorEra);
479 addTransitionAfterYear(year, currentEra);
480 calcTransitions();
481 calcAbbreviations();
482
483 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
484 log();
485 }
486
487 return true;
488 }
489
495 bool initForEpochSeconds(acetime_t epochSeconds) const {
496 LocalDate ld = LocalDate::forEpochSeconds(epochSeconds);
497 return initForLocalDate(ld);
498 }
499
506 ZEB addTransitionPriorToYear(int16_t year) const {
507 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
508 logging::printf("addTransitionPriorToYear(): %d\n", year);
509 }
510
511 const ZEB era = findZoneEra(mZoneInfoBroker, year - 1);
512
513 // If the prior ZoneEra has a ZonePolicy), then find the latest rule
514 // within the ZoneEra. Otherwise, add a Transition using a rule==nullptr.
515 ZRB latest = findLatestPriorRule(era.zonePolicy(), year);
516 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
517 logging::printf("addTransitionsPriorToYear(): adding latest prior ");
518 if (latest.isNull()) {
519 logging::printf("ZR(null)\n");
520 } else {
521 logging::printf("ZR[%d,%d]\n", latest.fromYear(), latest.toYear());
522 }
523 }
524 addTransition(year - 1, 0 /*month*/, era, latest);
525
526 return era;
527 }
528
534 static ZRB findLatestPriorRule(const ZPB& zonePolicy, int16_t year) {
535 ZRB latest;
536 if (zonePolicy.isNull()) return latest;
537
538 uint8_t numRules = zonePolicy.numRules();
539 for (uint8_t i = 0; i < numRules; i++) {
540 const ZRB rule = zonePolicy.rule(i);
541 // Check if rule is effective prior to the given year
542 if (rule.fromYear() < year) {
543 if ((latest.isNull()) ||
544 compareRulesBeforeYear(year, rule, latest) > 0) {
545 latest = rule;
546 }
547 }
548 }
549
550 return latest;
551 }
552
554 static int8_t compareRulesBeforeYear(
555 int16_t year, const ZRB& a, const ZRB& b) {
556 return basic::compareYearMonth(
557 priorYearOfRule(year, a), a.inMonth(),
558 priorYearOfRule(year, b), b.inMonth());
559 }
560
569 static int16_t priorYearOfRule(int16_t year, const ZRB& rule) {
570 if (rule.toYear() < year) {
571 return rule.toYear();
572 }
573 return year - 1;
574 }
575
580 ZEB addTransitionsForYear(int16_t year, const ZEB& priorEra) const {
581 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
582 logging::printf("addTransitionsForYear(): %d\n", year);
583 }
584
585 const ZEB era = findZoneEra(mZoneInfoBroker, year);
586
587 // If the ZonePolicy has no rules, then add a Transition which takes
588 // effect at the start time of the current year.
589 const ZPB zonePolicy = era.zonePolicy();
590 if (zonePolicy.isNull()) {
591 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
592 logging::printf("addTransitionsForYear(): adding ZE.untilY=%d\n",
593 era.untilYear());
594 }
595 addTransition(year, 0 /*month*/, era, ZRB());
596 return era;
597 }
598
599 if (! era.equals(priorEra)) {
600 // The ZoneEra has changed, so we need to find the Rule in effect at
601 // the start of the current year of the current ZoneEra. This may be a
602 // rule far in the past, but shift the rule forward to {year, 1, 1}.
603 ZRB latestPrior = findLatestPriorRule(era.zonePolicy(), year);
604 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
605 logging::printf(
606 "addTransitionsForYear(): adding latest prior ");
607 if (latestPrior.isNull()) {
608 logging::printf("ZR(null)\n");
609 } else {
610 logging::printf("ZR[%d,%d]\n",
611 latestPrior.fromYear(), latestPrior.toYear());
612 }
613 }
614 addTransition(year, 1 /*month*/, era, latestPrior);
615 }
616
617 // Find all directly matching transitions (i.e. the [from, to] overlap
618 // with the current year) and add them to mTransitions, in sorted order
619 // according to the ZoneRule::inMonth field.
620 uint8_t numRules = zonePolicy.numRules();
621 for (uint8_t i = 0; i < numRules; i++) {
622 const ZRB rule = zonePolicy.rule(i);
623 if ((rule.fromYear() <= year) && (year <= rule.toYear())) {
624 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
625 logging::printf(
626 "addTransitionsForYear(): adding rule ");
627 if (rule.isNull()) {
628 logging::printf("ZR(null)\n");
629 } else {
630 logging::printf("ZR[%d,%d]\n", rule.fromYear(), rule.toYear());
631 }
632 }
633 addTransition(year, 0 /*month*/, era, rule);
634 }
635 }
636
637 return era;
638 }
639
641 void addTransitionAfterYear(int16_t year, const ZEB& currentEra) const {
642 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
643 logging::printf("addTransitionAfterYear(): %d\n", year);
644 }
645
646 const ZEB eraAfter = findZoneEra(mZoneInfoBroker, year + 1);
647
648 // If the current era is the same as the following year, then we'll just
649 // assume that the latest ZoneRule carries over to Jan 1st of the next
650 // year. tzcompiler.py guarantees no ZoneRule occurs on Jan 1st.
651 if (currentEra.equals(eraAfter)) {
652 return;
653 }
654
655 // If the ZoneEra did change, find the latest transition prior to
656 // {year + 1, 1, 1}, then shift that Transition to Jan 1st of the
657 // following year.
658 ZRB latest = findLatestPriorRule(eraAfter.zonePolicy(), year + 1);
659 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
660 logging::printf(
661 "addTransitionsAfterYear(): adding latest prior ");
662 if (latest.isNull()) {
663 logging::printf("ZR(null)\n");
664 } else {
665 logging::printf("ZR[%d,%d]\n", latest.fromYear(), latest.toYear());
666 }
667 }
668 addTransition(year + 1, 1 /*month*/, eraAfter, latest);
669 }
670
694 void addTransition(int16_t year, uint8_t month, const ZEB& era,
695 const ZRB& rule) const {
696
697 // If a zone needs more transitions than kMaxCacheEntries, the check below
698 // will cause the DST transition information to be inaccurate, and it is
699 // highly likely that this situation would be caught in the
700 // AceTimeValidation tests. Since these integration tests pass, I feel
701 // confident that those zones which need more than kMaxCacheEntries are
702 // already filtered out by tzcompiler.py.
703 //
704 // Ideally, the tzcompiler.py script would explicitly remove those zones
705 // which need more than kMaxCacheEntries Transitions. But this would
706 // require a Python version of the BasicZoneProcessor, and unfortunately,
707 // zone_processor.py implements only the ExtendedZoneProcessor algorithm
708 // An early version of zone_processor.py may have implemented something
709 // close to BasicZoneProcessor, and it may be available in the git
710 // history. But it seems like too much work right now to try to dig that
711 // out, just to implement the explicit check for kMaxCacheEntries. It
712 // would mean maintaining another version of zone_processor.py.
713 if (mNumTransitions >= kMaxCacheEntries) return;
714
715 // Insert new element at the end of the list.
716 // NOTE: It is probably tempting to pass a pointer (or reference) to
717 // mTransitions[mNumTransitions] into createTransition(), instead of
718 // returning it by value. However, MemoryBenchmark shows that directly
719 // updating the Transition through the pointer increases flash memory
720 // consumption by ~110 bytes on AVR processors. It seems that creating a
721 // local copy of Transition on the stack, filling it, and then copying it
722 // by value takes fewer instructions.
723 mTransitions[mNumTransitions] = createTransition(year, month, era, rule);
724 mNumTransitions++;
725
726 // perform an insertion sort based on ZoneRule.inMonth()
727 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
728 Transition& left = mTransitions[i - 1];
729 Transition& right = mTransitions[i];
730 // assume only 1 rule per month
731 if (basic::compareYearMonth(left.year, left.month,
732 right.year, right.month) > 0) {
733 Transition tmp = left;
734 left = right;
735 right = tmp;
736 }
737 }
738 }
739
745 static Transition createTransition(int16_t year, uint8_t month,
746 const ZEB& era, const ZRB& rule) {
747
748 Transition transition;
749 int16_t deltaMinutes;
750 uint8_t mon;
751 if (rule.isNull()) {
752 mon = 1; // RULES is either '-' or 'hh:mm' so takes effect in Jan
753 deltaMinutes = era.deltaSeconds() / kSecPerMin;
754 transition.abbrev[0] = '\0';
755 } else {
756 mon = rule.inMonth();
757 deltaMinutes = rule.deltaSeconds() / kSecPerMin;
758 ace_common::strncpy_T(
759 transition.abbrev, rule.letter(), internal::kAbbrevSize - 1);
760 transition.abbrev[internal::kAbbrevSize - 1] = '\0';
761 }
762 // Clobber the month if specified.
763 if (month != 0) {
764 mon = month;
765 }
766 int16_t offsetMinutes = era.offsetSeconds() / kSecPerMin;
767
768 transition.era = era;
769 transition.rule = rule;
770 transition.startEpochSeconds = 0;
771 transition.offsetMinutes = offsetMinutes;
772 transition.deltaMinutes = deltaMinutes;
773 transition.year = year;
774 transition.month = mon;
775 return transition;
776 }
777
782 static ZEB findZoneEra(const ZIB& info, int16_t year) {
783 for (uint8_t i = 0; i < info.numEras(); i++) {
784 const ZEB era = info.era(i);
785 if (year < era.untilYear()) return era;
786 }
787 // Return the last ZoneEra if we run off the end.
788 return info.era(info.numEras() - 1);
789 }
790
798 void calcTransitions() const {
799 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
800 logging::printf("calcTransitions():\n");
801 }
802
803 // Set the initial startEpochSeconds to be -Infinity
804 Transition* prevTransition = &mTransitions[0];
805 prevTransition->startEpochSeconds = kMinEpochSeconds;
806
807 for (uint8_t i = 1; i < mNumTransitions; i++) {
808 Transition& transition = mTransitions[i];
809 const int16_t year = transition.year;
810
811 if (transition.rule.isNull()) {
812 // If the transition is simple (has no named rule), then the
813 // ZoneEra applies for the entire year (since BasicZoneProcessor
814 // supports only whole year in the UNTIL field). The whole year UNTIL
815 // field has an implied 'w' suffix on 00:00, we don't need to call
816 // calcRuleOffsetMinutes() with a 'w', we can just use the previous
817 // transition's offset to calculate the startDateTime of this
818 // transition.
819 //
820 // Also, when transition.rule == nullptr, the mNumTransitions should
821 // be 1, since only a single transition is added by
822 // addTransitionsForYear().
823 const int16_t prevTotalOffsetMinutes = prevTransition->offsetMinutes
824 + prevTransition->deltaMinutes;
825 OffsetDateTime startDateTime = OffsetDateTime::forComponents(
826 year, 1, 1, 0, 0, 0,
827 TimeOffset::forMinutes(prevTotalOffsetMinutes));
828 transition.startEpochSeconds = startDateTime.toEpochSeconds();
829 } else {
830 // In this case, the transition points to a named ZonePolicy, which
831 // means that there could be multiple ZoneRules associated with the
832 // given year. For each transition, determine the startEpochSeconds,
833 // and the effective offset time.
834
835 // Determine the start date of the rule.
836 const internal::MonthDay monthDay = internal::calcStartDayOfMonth(
837 year, transition.month, transition.rule.onDayOfWeek(),
838 transition.rule.onDayOfMonth());
839
840 // Determine the offset of the 'atTimeSuffix'. The 'w' suffix
841 // requires the offset of the previous transition.
842 const int16_t prevTotalOffsetMinutes = calcRuleOffsetMinutes(
843 prevTransition->offsetMinutes + prevTransition->deltaMinutes,
844 transition.era.offsetSeconds() / kSecPerMin,
845 transition.rule.atTimeSuffix());
846
847 // startDateTime
848 const uint16_t minutes = transition.rule.atTimeSeconds() / 60;
849 const uint8_t atHour = minutes / 60;
850 const uint8_t atMinute = minutes % 60;
851 OffsetDateTime startDateTime = OffsetDateTime::forComponents(
852 year, monthDay.month, monthDay.day,
853 atHour, atMinute, 0 /*second*/,
854 TimeOffset::forMinutes(prevTotalOffsetMinutes));
855 transition.startEpochSeconds = startDateTime.toEpochSeconds();
856 }
857
858 prevTransition = &transition;
859 }
860 }
861
868 static int16_t calcRuleOffsetMinutes(int16_t prevTotalOffsetMinutes,
869 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
870 if (atSuffix == basic::ZoneContext::kSuffixW) {
871 return prevTotalOffsetMinutes;
872 } else if (atSuffix == basic::ZoneContext::kSuffixS) {
873 return currentBaseOffsetMinutes;
874 } else { // 'u', 'g' or 'z'
875 return 0;
876 }
877 }
878
880 void calcAbbreviations() const {
881 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
882 logging::printf("calcAbbreviations():\n");
883 }
884
885 for (uint8_t i = 0; i < mNumTransitions; i++) {
886 Transition* transition = &mTransitions[i];
887 internal::createAbbreviation(
888 transition->abbrev,
889 internal::kAbbrevSize,
890 transition->era.format(),
891 transition->offsetMinutes * kSecPerMin,
892 transition->deltaMinutes * kSecPerMin,
893 transition->abbrev);
894 }
895 }
896
898 const Transition* findMatch(acetime_t epochSeconds) const {
899 const Transition* closestMatch = nullptr;
900 for (uint8_t i = 0; i < mNumTransitions; i++) {
901 const Transition* m = &mTransitions[i];
902 if (closestMatch == nullptr || m->startEpochSeconds <= epochSeconds) {
903 closestMatch = m;
904 }
905 }
906 return closestMatch;
907 }
908
909 private:
910 static const int32_t kSecPerMin = 60;
911
912 const ZIS* mZoneInfoStore; // nullable
913 ZIB mZoneInfoBroker;
914
915 mutable uint8_t mNumTransitions = 0;
916 mutable Transition mTransitions[kMaxCacheEntries];
917 };
918
923 class BasicZoneProcessor: public BasicZoneProcessorTemplate<
924 basic::ZoneInfoStore,
925 basic::ZoneInfoBroker,
926 basic::ZoneEraBroker,
927 basic::ZonePolicyBroker,
928 basic::ZoneRuleBroker> {
929
930 public:
932 static const uint8_t kTypeBasic = 3;
933
934 explicit BasicZoneProcessor(const basic::ZoneInfo* zoneInfo = nullptr)
935 : BasicZoneProcessorTemplate<
936 basic::ZoneInfoStore,
937 basic::ZoneInfoBroker,
938 basic::ZoneEraBroker,
939 basic::ZonePolicyBroker,
940 basic::ZoneRuleBroker>(
941 kTypeBasic, &mZoneInfoStore, (uintptr_t) zoneInfo)
942 {}
943
944 private:
945 basic::ZoneInfoStore mZoneInfoStore;
946 };
947
948 } // namespace ace_time
949
950 #endif
ace_time::BasicZoneProcessorTemplate
An implementation of ZoneProcessor that supports a subset of the zones containing in the TZ Database.
Definition: BasicZoneProcessor.h:201
ace_time::BasicZoneProcessorTemplate::isLink
bool isLink() const override
Return true if timezone is a Link entry pointing to a Zone entry.
Definition: BasicZoneProcessor.h:206
ace_time::BasicZoneProcessorTemplate::Transition
basic::TransitionTemplate< ZIB, ZEB, ZPB, ZRB > Transition
Exposed only for testing purposes.
Definition: BasicZoneProcessor.h:204
ace_time::BasicZoneProcessorTemplate::setZoneKey
void setZoneKey(uintptr_t zoneKey) override
Set the opaque zoneKey of this object to a new value, reseting any internally cached information.
Definition: BasicZoneProcessor.h:323
ace_time::BasicZoneProcessorTemplate::printNameTo
void printNameTo(Print &printer) const override
Print a human-readable identifier (e.g.
Definition: BasicZoneProcessor.h:309
ace_time::BasicZoneProcessorTemplate::findByEpochSeconds
FindResult findByEpochSeconds(acetime_t epochSeconds) const override
Return the search results at given epochSeconds.
Definition: BasicZoneProcessor.h:294
ace_time::BasicZoneProcessorTemplate::setZoneInfoStore
void setZoneInfoStore(const ZIS *zoneInfoStore)
Set the zone info store at runtime.
Definition: BasicZoneProcessor.h:356
ace_time::BasicZoneProcessorTemplate::log
void log() const
Used only for debugging.
Definition: BasicZoneProcessor.h:337
ace_time::BasicZoneProcessorTemplate::BasicZoneProcessorTemplate
BasicZoneProcessorTemplate(uint8_t type, const ZIS *zoneInfoStore, uintptr_t zoneKey)
Constructor.
Definition: BasicZoneProcessor.h:372
ace_time::BasicZoneProcessorTemplate::printShortNameTo
void printShortNameTo(Print &printer) const override
Print a short human-readable identifier (e.g.
Definition: BasicZoneProcessor.h:313
ace_time::BasicZoneProcessorTemplate::equalsZoneKey
bool equalsZoneKey(uintptr_t zoneKey) const override
Return true if ZoneProcessor is associated with the given opaque zoneKey.
Definition: BasicZoneProcessor.h:332
ace_time::BasicZoneProcessorTemplate::findByLocalDateTime
FindResult findByLocalDateTime(const LocalDateTime &ldt) const override
Return the search results at given LocalDateTime.
Definition: BasicZoneProcessor.h:242
ace_time::BasicZoneProcessorTemplate::getZoneId
uint32_t getZoneId() const override
Return the unique stable zoneId.
Definition: BasicZoneProcessor.h:210
ace_time::BasicZoneProcessorTemplate::printTargetNameTo
void printTargetNameTo(Print &printer) const override
Print the full identifier (e.g.
Definition: BasicZoneProcessor.h:317
ace_time::BasicZoneProcessor
A specific implementation of BasicZoneProcessorTemplate that uses ZoneXxxBrokers which read from zone...
Definition: BasicZoneProcessor.h:928
ace_time::BasicZoneProcessor::kTypeBasic
static const uint8_t kTypeBasic
Unique TimeZone type identifier for BasicZoneProcessor.
Definition: BasicZoneProcessor.h:932
ace_time::Epoch::currentEpochYear
static int16_t currentEpochYear()
Get the current epoch year.
Definition: Epoch.h:27
ace_time::FindResult
Result of a search for transition at a specific epochSeconds or a specific LocalDateTime.
Definition: ZoneProcessor.h:23
ace_time::FindResult::stdOffsetSeconds
int32_t stdOffsetSeconds
STD offset of the resulting OffsetDateTime.
Definition: ZoneProcessor.h:79
ace_time::FindResult::dstOffsetSeconds
int32_t dstOffsetSeconds
DST offset of the resulting OffsetDateTime.
Definition: ZoneProcessor.h:82
ace_time::FindResult::reqDstOffsetSeconds
int32_t reqDstOffsetSeconds
DST offset of the Transition which matched the epochSeconds requested by findByEpochSeconds(),...
Definition: ZoneProcessor.h:108
ace_time::FindResult::abbrev
const char * abbrev
Pointer to the abbreviation stored in the transient Transition::abbrev variable.
Definition: ZoneProcessor.h:115
ace_time::FindResult::reqStdOffsetSeconds
int32_t reqStdOffsetSeconds
STD offset of the Transition which matched the epochSeconds requested by findByEpochSeconds(),...
Definition: ZoneProcessor.h:95
ace_time::FindResult::type
uint8_t type
Result of the findByEpochSeconds() or findByLocalDateTime() search methods.
Definition: ZoneProcessor.h:65
ace_time::LocalDateTime
Class that holds the date-time as the components (year, month, day, hour, minute, second) without reg...
Definition: LocalDateTime.h:30
ace_time::LocalDateTime::localDate
const LocalDate & localDate() const
Return the LocalDate.
Definition: LocalDateTime.h:201
ace_time::LocalDateTime::toEpochSeconds
acetime_t toEpochSeconds() const
Return seconds since the current AceTime epoch defined by Epoch::currentEpochYear().
Definition: LocalDateTime.h:230
ace_time::LocalDate::kMaxYear
static const int16_t kMaxYear
The largest year that is expected to be handled by LocalDate.
Definition: LocalDate.h:78
ace_time::LocalDate::kMinYear
static const int16_t kMinYear
The smallest year that is expected to be handled by LocalDate.
Definition: LocalDate.h:69
ace_time::LocalDate::forEpochSeconds
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since the current epoch year given by currentEpochYear().
Definition: LocalDate.h:205
ace_time::LocalDate::kInvalidYear
static const int16_t kInvalidYear
Sentinel year which indicates one or more of the following conditions:
Definition: LocalDate.h:58
ace_time::OffsetDateTime::forLocalDateTimeAndOffset
static OffsetDateTime forLocalDateTimeAndOffset(const LocalDateTime &localDateTime, TimeOffset timeOffset)
Factory method from LocalDateTime and TimeOffset.
Definition: OffsetDateTime.h:41
ace_time::OffsetDateTime::forComponents
static OffsetDateTime forComponents(int16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, TimeOffset timeOffset, uint8_t fold=0)
Factory method using separated date, time, and UTC offset fields.
Definition: OffsetDateTime.h:61
ace_time::TimeOffset::forSeconds
static TimeOffset forSeconds(int32_t seconds)
Create TimeOffset from seconds from 00:00.
Definition: TimeOffset.h:96
ace_time::TimeOffset::forMinutes
static TimeOffset forMinutes(int16_t minutes)
Create TimeOffset from minutes from 00:00.
Definition: TimeOffset.h:91
ace_time::ZoneProcessor
Base interface for ZoneProcessor classes.
Definition: ZoneProcessor.h:140
ace_time::ZoneProcessor::mYear
int16_t mYear
Year that was used to calculate the transitions in the current cache.
Definition: ZoneProcessor.h:249
ace_time::ZoneProcessor::isFilled
bool isFilled(int16_t year) const
Check if the Transition cache is filled for the given year and current epochYear.
Definition: ZoneProcessor.h:229
ace_time::ZoneProcessor::mEpochYear
int16_t mEpochYear
Epoch year that was used to calculate the transitions in the current cache.
Definition: ZoneProcessor.h:255
ace_time::zoneinfolow::ZoneInfoStore< ZoneContext, ZoneInfo, ZoneEra, ZonePolicy, ZoneRule >
common.h
Identifiers used by implementation code which need to be publically exported.
ace_time::acetime_t
int32_t acetime_t
Type for the number of seconds from epoch.
Definition: common.h:24
ace_time::basic::TransitionTemplate
Data structure that defines the start of a specific UTC offset as described by the matching ZoneEra a...
Definition: BasicZoneProcessor.h:59
ace_time::basic::TransitionTemplate::month
uint8_t month
Month of the transition.
Definition: BasicZoneProcessor.h:98
ace_time::basic::TransitionTemplate::startEpochSeconds
acetime_t startEpochSeconds
The calculated transition time of the given rule.
Definition: BasicZoneProcessor.h:79
ace_time::basic::TransitionTemplate::rule
ZRB rule
The Zone transition rule that matched for the the given year.
Definition: BasicZoneProcessor.h:76
ace_time::basic::TransitionTemplate::era
ZEB era
The ZoneEra that matched the given year.
Definition: BasicZoneProcessor.h:65
ace_time::basic::TransitionTemplate::log
void log() const
Used only for debugging.
Definition: BasicZoneProcessor.h:110
ace_time::basic::TransitionTemplate::year
int16_t year
Year of the Transition.
Definition: BasicZoneProcessor.h:91
ace_time::basic::TransitionTemplate::offsetMinutes
int16_t offsetMinutes
The standard time offset minutes at the start of transition, not including DST offset.
Definition: BasicZoneProcessor.h:85
ace_time::basic::TransitionTemplate::abbrev
char abbrev[internal::kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
Definition: BasicZoneProcessor.h:107
ace_time::basic::TransitionTemplate::deltaMinutes
int16_t deltaMinutes
The deltaMinutes from "standard time" at the start of transition.
Definition: BasicZoneProcessor.h:88
ace_time::zoneinfolow::ZoneContext::kSuffixS
static const uint8_t kSuffixS
Represents 's' or standard time.
Definition: ZoneInfoLow.h:83
ace_time::zoneinfolow::ZoneContext::kSuffixW
static const uint8_t kSuffixW
Represents 'w' or wall time.
Definition: ZoneInfoLow.h:80
ace_time::zoneinfolow::ZoneInfo
Representation of a given time zone, implemented as an array of ZoneEra records.
Definition: ZoneInfoLow.h:320