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

ExtendedZoneProcessor.h
1 /*
2 * MIT License
3 * Copyright (c) 2019 Brian T. Park
4 */
5
6 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
7 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
8
9 #include <stdint.h> // uintptr_t
10 #include <AceCommon.h> // copyReplaceString()
11 #include "../zoneinfo/infos.h"
12 #include "common/common.h" // kAbbrevSize
13 #include "common/logging.h"
14 #include "LocalDate.h"
15 #include "ZoneProcessor.h"
16 #include "Transition.h"
17
18 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
19 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG 0
20 #endif
21
22 class ExtendedZoneProcessorTest_compareEraToYearMonth;
23 class ExtendedZoneProcessorTest_compareEraToYearMonth2;
24 class ExtendedZoneProcessorTest_createMatchingEra;
25 class ExtendedZoneProcessorTest_findMatches_simple;
26 class ExtendedZoneProcessorTest_findMatches_named;
27 class ExtendedZoneProcessorTest_findCandidateTransitions;
28 class ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
29 class ExtendedZoneProcessorTest_getTransitionTime;
30 class ExtendedZoneProcessorTest_createTransitionForYear;
31 class ExtendedZoneProcessorTest_calcInteriorYears;
32 class ExtendedZoneProcessorTest_getMostRecentPriorYear;
33 class ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
34 class ExtendedZoneProcessorTest_compareTransitionToMatch;
35 class ExtendedZoneProcessorTest_processTransitionCompareStatus;
36 class ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
37 class ExtendedZoneProcessorTest_setZoneKey;
38 class ExtendedTransitionValidation;
39 class CompleteTransitionValidation;
40
41 class Print;
42
43 namespace ace_time {
44
45 namespace extended {
46
48 struct YearMonthTuple {
49 int16_t year;
50 uint8_t month;
51 };
52
53 }
54
80 template <typename D>
81 class ExtendedZoneProcessorTemplate: public ZoneProcessor {
82 public:
94 static const uint8_t kMaxTransitions = 8;
95
97 typedef extended::TransitionTemplate<D> Transition;
98
100 typedef extended::TransitionForSecondsTemplate<D>
101 TransitionForSeconds;
102
104 typedef extended::TransitionForDateTimeTemplate<D>
105 TransitionForDateTime;
106
108 typedef extended::MatchingEraTemplate<D> MatchingEra;
109
111 typedef extended::TransitionStorageTemplate<kMaxTransitions, D>
112 TransitionStorage;
113
114 bool isLink() const override {
115 return ! mZoneInfoBroker.targetInfo().isNull();
116 }
117
118 uint32_t getZoneId() const override {
119 return mZoneInfoBroker.zoneId();
120 }
121
122 FindResult findByLocalDateTime(const LocalDateTime& ldt) const override {
123 FindResult result;
124
125 bool success = initForYear(ldt.year());
126 if (! success) {
127 return result;
128 }
129
130 // Find the Transition(s) in the gap or overlap.
131 TransitionForDateTime transitionForDateTime =
132 mTransitionStorage.findTransitionForDateTime(ldt);
133
134 // Extract the target Transition, depending on the requested ldt.fold
135 // and the result.num.
136 const Transition* transition;
137 if (transitionForDateTime.num == 1) {
138 transition = transitionForDateTime.curr;
139 result.type = FindResult::kTypeExact;
140 result.reqStdOffsetSeconds = transition->offsetSeconds;
141 result.reqDstOffsetSeconds = transition->deltaSeconds;
142 } else { // num = 0 or 2
143 if (transitionForDateTime.prev == nullptr
144 || transitionForDateTime.curr == nullptr) {
145 // ldt was far past or far future
146 transition = nullptr;
147 result.type = FindResult::kTypeNotFound;
148 } else { // gap or overlap
149 if (transitionForDateTime.num == 0) { // num==0, Gap
150 result.type = FindResult::kTypeGap;
151 if (ldt.fold() == 0) {
152 // ldt wants to use the 'prev' transition to convert to
153 // epochSeconds.
154 result.reqStdOffsetSeconds =
155 transitionForDateTime.prev->offsetSeconds;
156 result.reqDstOffsetSeconds =
157 transitionForDateTime.prev->deltaSeconds;
158 // But after normalization, it will be shifted into the curr
159 // transition, so select 'curr' as the target transition.
160 transition = transitionForDateTime.curr;
161 } else {
162 // ldt wants to use the 'curr' transition to convert to
163 // epochSeconds.
164 result.reqStdOffsetSeconds =
165 transitionForDateTime.curr->offsetSeconds;
166 result.reqDstOffsetSeconds =
167 transitionForDateTime.curr->deltaSeconds;
168 // But after normalization, it will be shifted into the prev
169 // transition, so select 'prev' as the target transition.
170 transition = transitionForDateTime.prev;
171 }
172 } else { // num==2, Overlap
173 transition = (ldt.fold() == 0)
174 ? transitionForDateTime.prev
175 : transitionForDateTime.curr;
176 result.type = FindResult::kTypeOverlap;
177 result.reqStdOffsetSeconds = transition->offsetSeconds;
178 result.reqDstOffsetSeconds = transition->deltaSeconds;
179 result.fold = ldt.fold();
180 }
181 }
182 }
183
184 if (! transition) {
185 return result;
186 }
187
188 result.stdOffsetSeconds = transition->offsetSeconds;
189 result.dstOffsetSeconds = transition->deltaSeconds;
190 result.abbrev = transition->abbrev;
191
192 return result;
193 }
194
203 FindResult findByEpochSeconds(acetime_t epochSeconds) const override {
204 FindResult result;
205 bool success = initForEpochSeconds(epochSeconds);
206 if (!success) return result;
207
208 TransitionForSeconds transitionForSeconds =
209 mTransitionStorage.findTransitionForSeconds(epochSeconds);
210 const Transition* transition = transitionForSeconds.curr;
211 if (!transition) return result;
212
213 result.stdOffsetSeconds = transition->offsetSeconds;
214 result.dstOffsetSeconds = transition->deltaSeconds;
215 result.reqStdOffsetSeconds = transition->offsetSeconds;
216 result.reqDstOffsetSeconds = transition->deltaSeconds;
217 result.abbrev = transition->abbrev;
218 result.fold = transitionForSeconds.fold;
219 if (transitionForSeconds.num == 2) {
220 result.type = FindResult::kTypeOverlap;
221 } else {
222 result.type = FindResult::kTypeExact;
223 }
224 return result;
225 }
226
227 void printNameTo(Print& printer) const override {
228 mZoneInfoBroker.printNameTo(printer);
229 }
230
231 void printShortNameTo(Print& printer) const override {
232 mZoneInfoBroker.printShortNameTo(printer);
233 }
234
235 void printTargetNameTo(Print& printer) const override {
236 if (isLink()) {
237 mZoneInfoBroker.targetInfo().printNameTo(printer);
238 }
239 }
240
242 void log() const {
243 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
244 logging::printf("ExtendedZoneProcessor:\n");
245 logging::printf(" mEpochYear: %d\n", mEpochYear);
246 logging::printf(" mYear: %d\n", mYear);
247 logging::printf(" mNumMatches: %d\n", mNumMatches);
248 for (int i = 0; i < mNumMatches; i++) {
249 logging::printf(" Match %d: ", i);
250 mMatches[i].log();
251 logging::printf("\n");
252 }
253 mTransitionStorage.log();
254 }
255 }
256
258 void resetTransitionAllocSize() {
259 mTransitionStorage.resetAllocSize();
260 }
261
263 uint8_t getTransitionAllocSize() const {
264 return mTransitionStorage.getAllocSize();
265 }
266
267 void setZoneKey(uintptr_t zoneKey) override {
268 if (! mZoneInfoStore) return;
269 if (mZoneInfoBroker.equals(zoneKey)) return;
270
271 mZoneInfoBroker = mZoneInfoStore->createZoneInfoBroker(zoneKey);
272 mYear = LocalDate::kInvalidYear;
273 mNumMatches = 0;
274 resetTransitionAllocSize(); // clear the alloc size for new zone
275 }
276
277 bool equalsZoneKey(uintptr_t zoneKey) const override {
278 return mZoneInfoBroker.equals(zoneKey);
279 }
280
287 void setZoneInfoStore(const typename D::ZoneInfoStore* zoneInfoStore) {
288 mZoneInfoStore = zoneInfoStore;
289 }
290
296 bool initForEpochSeconds(acetime_t epochSeconds) const {
297 LocalDate ld = LocalDate::forEpochSeconds(epochSeconds);
298 return initForYear(ld.year());
299 }
300
306 bool initForYear(int16_t year) const {
307 // Restrict to [1,9999] even though LocalDate should be able to handle
308 // [0,10000].
309 if (year <= LocalDate::kMinYear || LocalDate::kMaxYear <= year) {
310 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
311 logging::printf(
312 "initForYear(): Year %d outside range [%d, %d]\n",
313 year, LocalDate::kMinYear + 1, LocalDate::kMaxYear - 1);
314 }
315 return false;
316 }
317
318 if (isFilled(year)) return true;
319 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
320 logging::printf("initForYear(): %d\n", year);
321 }
322 mYear = year;
323 mEpochYear = Epoch::currentEpochYear();
324 mNumMatches = 0; // clear cache
325 mTransitionStorage.init();
326
327 // Fill transitions over a 14-month window straddling the given year.
328 extended::YearMonthTuple startYm = { (int16_t) (year - 1), 12 };
329 extended::YearMonthTuple untilYm = { (int16_t) (year + 1), 2 };
330
331 // Step 1. The equivalent steps for the Python version are in the
332 // acetimepy project, under zone_processor.ZoneProcessor.init_for_year().
333 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
334 logging::printf("==== Step 1: findMatches()\n");
335 }
336 mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
337 kMaxMatches);
338 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
339
340 // Step 2
341 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
342 logging::printf("==== Step 2: createTransitions()\n");
343 }
344 createTransitions(mTransitionStorage, mMatches, mNumMatches);
345 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
346
347 // Step 3
348 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
349 logging::printf("==== Step 3: fixTransitionTimes()\n");
350 }
351 Transition** begin = mTransitionStorage.getActivePoolBegin();
352 Transition** end = mTransitionStorage.getActivePoolEnd();
353 fixTransitionTimes(begin, end);
354 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
355
356 // Step 4
357 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
358 logging::printf("==== Step 4: generateStartUntilTimes()\n");
359 }
360 generateStartUntilTimes(begin, end);
361 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
362
363 // Step 5
364 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
365 logging::printf("==== Step 5: calcAbbreviations()\n");
366 }
367 calcAbbreviations(begin, end);
368 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
369
370 return true;
371 }
372
373 protected:
385 explicit ExtendedZoneProcessorTemplate(
386 uint8_t type,
387 const typename D::ZoneInfoStore* zoneInfoStore /*nullable*/,
388 uintptr_t zoneKey
389 ) :
390 ZoneProcessor(type),
391 mZoneInfoStore(zoneInfoStore)
392 {
393 setZoneKey(zoneKey);
394 }
395
396 private:
397 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
398 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
399 friend class ::ExtendedZoneProcessorTest_createMatchingEra;
400 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
401 friend class ::ExtendedZoneProcessorTest_findMatches_named;
402 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
403 friend class ::ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
404 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
405 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
406 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
407 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
408 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
409 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
410 friend class ::ExtendedZoneProcessorTest_processTransitionCompareStatus;
411 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
412 friend class ::ExtendedZoneProcessorTest_setZoneKey;
413 friend class ::ExtendedTransitionValidation;
414 friend class ::CompleteTransitionValidation;
415
416 // Disable copy constructor and assignment operator.
417 ExtendedZoneProcessorTemplate(
418 const ExtendedZoneProcessorTemplate&) = delete;
419 ExtendedZoneProcessorTemplate& operator=(
420 const ExtendedZoneProcessorTemplate&) = delete;
421
426 static const uint8_t kMaxMatches = 4;
427
432 static const uint8_t kMaxInteriorYears = 4;
433
434 bool equals(const ZoneProcessor& other) const override {
435 return mZoneInfoBroker.equals(
436 ((const ExtendedZoneProcessorTemplate&) other).mZoneInfoBroker);
437 }
438
446 static uint8_t findMatches(
447 const typename D::ZoneInfoBroker& zoneInfo,
448 const extended::YearMonthTuple& startYm,
449 const extended::YearMonthTuple& untilYm,
450 MatchingEra* matches,
451 uint8_t maxMatches
452 ) {
453 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
454 logging::printf("findMatches()\n");
455 }
456 uint8_t iMatch = 0;
457 MatchingEra* prevMatch = nullptr;
458 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
459 const typename D::ZoneEraBroker era = zoneInfo.era(iEra);
460 if (eraOverlapsInterval(prevMatch, era, startYm, untilYm)) {
461 if (iMatch < maxMatches) {
462 matches[iMatch] = createMatchingEra(
463 prevMatch, era, startYm, untilYm);
464 prevMatch = &matches[iMatch];
465 iMatch++;
466 }
467 }
468 }
469 return iMatch;
470 }
471
497 static bool eraOverlapsInterval(
498 const MatchingEra* prevMatch,
499 const typename D::ZoneEraBroker& era,
500 const extended::YearMonthTuple& startYm,
501 const extended::YearMonthTuple& untilYm) {
502 return (prevMatch == nullptr || compareEraToYearMonth(
503 prevMatch->era, untilYm.year, untilYm.month) < 0)
504 && compareEraToYearMonth(era, startYm.year, startYm.month) > 0;
505 }
506
508 static int8_t compareEraToYearMonth(const typename D::ZoneEraBroker& era,
509 int16_t year, uint8_t month) {
510 if (era.untilYear() < year) return -1;
511 if (era.untilYear() > year) return 1;
512 if (era.untilMonth() < month) return -1;
513 if (era.untilMonth() > month) return 1;
514 if (era.untilDay() > 1) return 1;
515 //if (era.untilTimeSeconds() < 0) return -1; // never possible
516 if (era.untilTimeSeconds() > 0) return 1;
517 return 0;
518 }
519
526 static MatchingEra createMatchingEra(
527 MatchingEra* prevMatch,
528 const typename D::ZoneEraBroker& era,
529 const extended::YearMonthTuple& startYm,
530 const extended::YearMonthTuple& untilYm) {
531
532 // If prevMatch is null, set startDate to be earlier than all valid
533 // ZoneEra.
534 extended::DateTuple startDate = (prevMatch == nullptr)
535 ? extended::DateTuple{
536 LocalDate::kInvalidYear,
537 1,
538 1,
539 0,
540 extended::Info::ZoneContext::kSuffixW
541 }
542 : extended::DateTuple{
543 prevMatch->era.untilYear(),
544 prevMatch->era.untilMonth(),
545 prevMatch->era.untilDay(),
546 (int32_t) prevMatch->era.untilTimeSeconds(),
547 prevMatch->era.untilTimeSuffix()
548 };
549 extended::DateTuple lowerBound{
550 startYm.year,
551 startYm.month,
552 1,
553 0,
554 extended::Info::ZoneContext::kSuffixW
555 };
556 if (startDate < lowerBound) {
557 startDate = lowerBound;
558 }
559
560 extended::DateTuple untilDate{
561 era.untilYear(),
562 era.untilMonth(),
563 era.untilDay(),
564 (int32_t) era.untilTimeSeconds(),
565 era.untilTimeSuffix()
566 };
567 extended::DateTuple upperBound{
568 untilYm.year,
569 untilYm.month,
570 1,
571 0,
572 extended::Info::ZoneContext::kSuffixW
573 };
574 if (upperBound < untilDate) {
575 untilDate = upperBound;
576 }
577
578 return {startDate, untilDate, era, prevMatch, 0, 0};
579 }
580
585 static void createTransitions(
586 TransitionStorage& transitionStorage,
587 MatchingEra* matches,
588 uint8_t numMatches) {
589 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
590 logging::printf("createTransitions()\n");
591 }
592
593 for (uint8_t i = 0; i < numMatches; i++) {
594 createTransitionsForMatch(transitionStorage, &matches[i]);
595 }
596 }
597
599 static void createTransitionsForMatch(
600 TransitionStorage& transitionStorage,
601 MatchingEra* match) {
602 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
603 logging::printf("== createTransitionsForMatch()\n");
604 }
605 const typename D::ZonePolicyBroker policy = match->era.zonePolicy();
606 if (policy.isNull()) {
607 createTransitionsFromSimpleMatch(transitionStorage, match);
608 } else {
609 createTransitionsFromNamedMatch(transitionStorage, match);
610 }
611 }
612
613 // Step 2A
614 static void createTransitionsFromSimpleMatch(
615 TransitionStorage& transitionStorage,
616 MatchingEra* match) {
617 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
618 logging::printf("== createTransitionsFromSimpleMatch()\n");
619 }
620
621 Transition* freeTransition = transitionStorage.getFreeAgent();
622 createTransitionForYear(freeTransition, 0 /*not used*/,
623 typename D::ZoneRuleBroker() /*rule*/, match);
624 freeTransition->compareStatus = extended::CompareStatus::kExactMatch;
625 match->lastOffsetSeconds = freeTransition->offsetSeconds;
626 match->lastDeltaSeconds = freeTransition->deltaSeconds;
627 transitionStorage.addFreeAgentToActivePool();
628 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
629 transitionStorage.log();
630 }
631 }
632
633 // Step 2B
634 static void createTransitionsFromNamedMatch(
635 TransitionStorage& transitionStorage,
636 MatchingEra* match) {
637 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
638 logging::printf("== createTransitionsFromNamedMatch()\n");
639 }
640
641 transitionStorage.resetCandidatePool();
642 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
643 match->log(); logging::printf("\n");
644 }
645
646 // Pass 1: Find candidate transitions using whole years.
647 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
648 logging::printf("---- Pass 1: findCandidateTransitions()\n");
649 }
650 findCandidateTransitions(transitionStorage, match);
651 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
652 transitionStorage.log();
653 }
654
655 // Pass 2: Fix the transitions times, converting 's' and 'u' into 'w'
656 // uniformly.
657 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
658 logging::printf("---- Pass 2: fixTransitionTimes()\n");
659 }
660 fixTransitionTimes(
661 transitionStorage.getCandidatePoolBegin(),
662 transitionStorage.getCandidatePoolEnd());
663
664 // Pass 3: Select only those Transitions which overlap with the actual
665 // start and until times of the MatchingEra.
666 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
667 logging::printf("---- Pass 3: selectActiveTransitions()\n");
668 }
669 selectActiveTransitions(
670 transitionStorage.getCandidatePoolBegin(),
671 transitionStorage.getCandidatePoolEnd());
672 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
673 transitionStorage.log();
674 }
675 Transition* lastTransition =
676 transitionStorage.addActiveCandidatesToActivePool();
677 match->lastOffsetSeconds = lastTransition->offsetSeconds;
678 match->lastDeltaSeconds = lastTransition->deltaSeconds;
679 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
680 transitionStorage.log();
681 }
682 }
683
684 // Step 2B: Pass 1
685 static void findCandidateTransitions(
686 TransitionStorage& transitionStorage,
687 const MatchingEra* match) {
688 using extended::CompareStatus;
689
690 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
691 logging::printf("findCandidateTransitions(): \n");
692 match->log();
693 logging::printf("\n");
694 }
695 const typename D::ZonePolicyBroker policy = match->era.zonePolicy();
696 uint8_t numRules = policy.numRules();
697 int16_t startY = match->startDateTime.year;
698 int16_t endY = match->untilDateTime.year;
699
700 // The prior is referenced through a handle (i.e. pointer to pointer)
701 // because the actual pointer to the prior could change through the
702 // transitionStorage.setFreeAgentAsPriorIfValid() method.
703 Transition** prior = transitionStorage.reservePrior();
704 (*prior)->isValidPrior = false; // indicates "no prior transition"
705 for (uint8_t r = 0; r < numRules; r++) {
706 const typename D::ZoneRuleBroker rule = policy.rule(r);
707
708 // Add Transitions for interior years
709 int16_t interiorYears[kMaxInteriorYears];
710 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
711 rule.fromYear(), rule.toYear(), startY, endY);
712 for (uint8_t y = 0; y < numYears; y++) {
713 int16_t year = interiorYears[y];
714 Transition* t = transitionStorage.getFreeAgent();
715 createTransitionForYear(t, year, rule, match);
716 CompareStatus status = compareTransitionToMatchFuzzy(t, match);
717 if (status == CompareStatus::kPrior) {
718 transitionStorage.setFreeAgentAsPriorIfValid();
719 } else if (status == CompareStatus::kWithinMatch) {
720 transitionStorage.addFreeAgentToCandidatePool();
721 } else {
722 // Must be kFarFuture.
723 // Do nothing, allowing the free agent to be reused.
724 }
725 }
726
727 // Add Transition for prior year
728 int16_t priorYear = getMostRecentPriorYear(
729 rule.fromYear(), rule.toYear(), startY, endY);
730 if (priorYear != LocalDate::kInvalidYear) {
731 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
732 logging::printf(
733 "findCandidateTransitions(): priorYear: %d\n", priorYear);
734 }
735 Transition* t = transitionStorage.getFreeAgent();
736 createTransitionForYear(t, priorYear, rule, match);
737 transitionStorage.setFreeAgentAsPriorIfValid();
738 }
739 }
740
741 // Add the reserved prior into the Candidate pool only if 'isValidPrior'
742 // is true.
743 if ((*prior)->isValidPrior) {
744 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
745 logging::printf(
746 "findCandidateTransitions(): adding prior to Candidate pool\n");
747 logging::printf(" ");
748 (*prior)->log();
749 logging::printf("\n");
750 }
751 transitionStorage.addPriorToCandidatePool();
752 }
753 }
754
774 static uint8_t calcInteriorYears(
775 int16_t* interiorYears,
776 uint8_t maxInteriorYears,
777 int16_t fromYear, int16_t toYear,
778 int16_t startYear, int16_t endYear) {
779 uint8_t i = 0;
780 for (int16_t year = startYear; year <= endYear; year++) {
781 if (fromYear <= year && year <= toYear) {
782 interiorYears[i] = year;
783 i++;
784 if (i >= maxInteriorYears) break;
785 }
786 }
787 return i;
788 }
789
795 static void createTransitionForYear(
796 Transition* t,
797 int16_t year,
798 const typename D::ZoneRuleBroker& rule,
799 const MatchingEra* match) {
800 t->match = match;
801 t->offsetSeconds = match->era.offsetSeconds();
802 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
803 t->rule = rule;
804 #endif
805
806 if (rule.isNull()) {
807 // Create a Transition using the MatchingEra for the transitionTime.
808 // Used for simple MatchingEra.
809 t->transitionTime = match->startDateTime;
810 t->deltaSeconds = match->era.deltaSeconds();
811 t->abbrev[0] = '\0';
812 } else {
813 t->transitionTime = getTransitionTime(year, rule);
814 t->deltaSeconds = rule.deltaSeconds();
815 ace_common::strncpy_T(
816 t->abbrev, rule.letter(), kAbbrevSize - 1);
817 t->abbrev[kAbbrevSize - 1] = '\0';
818 }
819 }
820
833 static int16_t getMostRecentPriorYear(
834 int16_t fromYear, int16_t toYear,
835 int16_t startYear, int16_t /*endYear*/) {
836
837 if (fromYear < startYear) {
838 if (toYear < startYear) {
839 return toYear;
840 } else {
841 return startYear - 1;
842 }
843 } else {
844 return LocalDate::kInvalidYear;
845 }
846 }
847
852 static extended::DateTuple getTransitionTime(
853 int16_t year, const typename D::ZoneRuleBroker& rule) {
854
855 MonthDay monthDay = calcStartDayOfMonth(
856 year,
857 rule.inMonth(),
858 rule.onDayOfWeek(),
859 rule.onDayOfMonth());
860 return {
861 year,
862 monthDay.month,
863 monthDay.day,
864 (int32_t) rule.atTimeSeconds(),
865 rule.atTimeSuffix()
866 };
867 }
868
879 static extended::CompareStatus compareTransitionToMatchFuzzy(
880 const Transition* t, const MatchingEra* match) {
881 return compareDateTupleFuzzy(
882 t->transitionTime,
883 match->startDateTime,
884 match->untilDateTime);
885 }
886
895 static void fixTransitionTimes(Transition** begin, Transition** end) {
896 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
897 logging::printf("fixTransitionTimes(): START; #transitions=%d\n",
898 (int) (end - begin));
899 Transition::printTransitions(" ", begin, end);
900 }
901
902 // extend first Transition to -infinity
903 Transition* prev = *begin;
904
905 for (Transition** iter = begin; iter != end; ++iter) {
906 Transition* curr = *iter;
907 expandDateTuple(
908 &curr->transitionTime,
909 prev->offsetSeconds,
910 prev->deltaSeconds,
911 &curr->transitionTime,
912 &curr->transitionTimeS,
913 &curr->transitionTimeU);
914 prev = curr;
915 }
916 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
917 logging::printf("fixTransitionTimes(): FIXED\n");
918 Transition::printTransitions(" ", begin, end);
919 logging::printf("fixTransitionTimes(): END\n");
920 }
921 }
922
927 static void selectActiveTransitions(Transition** begin, Transition** end) {
928 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
929 logging::printf("selectActiveTransitions(): #candidates: %d\n",
930 (int) (end - begin));
931 }
932
933 Transition* prior = nullptr;
934 for (Transition** iter = begin; iter != end; ++iter) {
935 Transition* transition = *iter;
936 processTransitionCompareStatus(transition, &prior);
937 }
938
939 // If the latest prior transition is found, shift it to start at the
940 // startDateTime of the current match.
941 if (prior) {
942 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
943 logging::printf(
944 "selectActiveTransitions(): found latest prior\n");
945 }
946 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
947 prior->originalTransitionTime = prior->transitionTime;
948 #endif
949 prior->transitionTime = prior->match->startDateTime;
950 }
951 }
952
959 static void processTransitionCompareStatus(
960 Transition* transition,
961 Transition** prior) {
962 using extended::CompareStatus;
963
964 CompareStatus status = compareTransitionToMatch(
965 transition, transition->match);
966 transition->compareStatus = status;
967
968 if (status == CompareStatus::kExactMatch) {
969 if (*prior) {
970 (*prior)->compareStatus = CompareStatus::kFarPast;
971 }
972 (*prior) = transition;
973 } else if (status == CompareStatus::kPrior) {
974 if (*prior) {
975 if ((*prior)->transitionTimeU <= transition->transitionTimeU) {
976 (*prior)->compareStatus = CompareStatus::kFarPast;
977 (*prior) = transition;
978 } else {
979 transition->compareStatus = CompareStatus::kFarPast;
980 }
981 } else {
982 (*prior) = transition;
983 }
984 }
985 }
986
995 static extended::CompareStatus compareTransitionToMatch(
996 const Transition* transition,
997 const MatchingEra* match) {
998
999 // Find the previous Match offsets.
1000 int32_t prevMatchOffsetSeconds;
1001 int32_t prevMatchDeltaSeconds;
1002 if (match->prevMatch) {
1003 prevMatchOffsetSeconds = match->prevMatch->lastOffsetSeconds;
1004 prevMatchDeltaSeconds = match->prevMatch->lastDeltaSeconds;
1005 } else {
1006 prevMatchOffsetSeconds = match->era.offsetSeconds();
1007 prevMatchDeltaSeconds = 0;
1008 }
1009
1010 // Expand start times.
1011 extended::DateTuple stw;
1012 extended::DateTuple sts;
1013 extended::DateTuple stu;
1014 expandDateTuple(
1015 &match->startDateTime,
1016 prevMatchOffsetSeconds,
1017 prevMatchDeltaSeconds,
1018 &stw,
1019 &sts,
1020 &stu);
1021
1022 // Transition times.
1023 const extended::DateTuple& ttw = transition->transitionTime;
1024 const extended::DateTuple& tts = transition->transitionTimeS;
1025 const extended::DateTuple& ttu = transition->transitionTimeU;
1026
1027 // Compare Transition to Match, where equality is assumed if *any* of the
1028 // 'w', 's', or 'u' versions of the DateTuple are equal. This prevents
1029 // duplicate Transition instances from being created in a few cases.
1030 if (ttw == stw || tts == sts || ttu == stu) {
1031 return extended::CompareStatus::kExactMatch;
1032 }
1033
1034 if (ttu < stu) {
1035 return extended::CompareStatus::kPrior;
1036 }
1037
1038 // Now check if the transition occurs after the given match. The
1039 // untilDateTime of the current match uses the same UTC offsets as the
1040 // transitionTime of the current transition, so no complicated adjustments
1041 // are needed. We just make sure we compare 'w' with 'w', 's' with 's',
1042 // and 'u' with 'u'.
1043 const extended::DateTuple& matchUntil = match->untilDateTime;
1044 const extended::DateTuple* transitionTime;
1045 if (matchUntil.suffix == extended::Info::ZoneContext::kSuffixS) {
1046 transitionTime = &tts;
1047 } else if (matchUntil.suffix == extended::Info::ZoneContext::kSuffixU) {
1048 transitionTime = &ttu;
1049 } else { // assume 'w'
1050 transitionTime = &ttw;
1051 }
1052 if (*transitionTime < matchUntil) {
1053 return extended::CompareStatus::kWithinMatch;
1054 }
1055 return extended::CompareStatus::kFarFuture;
1056 }
1057
1063 static void generateStartUntilTimes(Transition** begin, Transition** end) {
1064 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1065 logging::printf(
1066 "generateStartUntilTimes(): #transitions=%d\n",
1067 (int) (end - begin));
1068 }
1069
1070 // It is possible that there are no matching transitions. This can happen
1071 // if the zonedbx is corrupted and ZoneInfo contains invalid fields.
1072 if (begin == end) return;
1073
1074 Transition* prev = *begin;
1075 bool isAfterFirst = false;
1076
1077 for (Transition** iter = begin; iter != end; ++iter) {
1078 Transition* const t = *iter;
1079
1080 // 1) Update the untilDateTime of the previous Transition
1081 const extended::DateTuple& tt = t->transitionTime;
1082 if (isAfterFirst) {
1083 prev->untilDateTime = tt;
1084 }
1085
1086 // 2) Calculate the current startDateTime by shifting the
1087 // transitionTime (represented in the UTC offset of the previous
1088 // transition) into the UTC offset of the *current* transition.
1089 int32_t seconds = tt.seconds + (
1090 - prev->offsetSeconds - prev->deltaSeconds
1091 + t->offsetSeconds + t->deltaSeconds);
1092 t->startDateTime = {tt.year, tt.month, tt.day, seconds, tt.suffix};
1093 extended::normalizeDateTuple(&t->startDateTime);
1094
1095 // 3) The epochSecond of the 'transitionTime' is determined by the
1096 // UTC offset of the *previous* Transition. However, the
1097 // transitionTime can be represented by an illegal time (e.g. 24:00).
1098 // So, it is better to use the properly normalized startDateTime
1099 // (calculated above) with the *current* UTC offset.
1100 //
1101 // NOTE: We should also be able to calculate this directly from
1102 // 'transitionTimeU' which should still be a valid field, because it
1103 // hasn't been clobbered by 'untilDateTime' yet. Not sure if this saves
1104 // any CPU time though, since we still need to mutiply by 900.
1105 const extended::DateTuple& st = t->startDateTime;
1106 const acetime_t offsetSeconds =
1107 st.seconds - (t->offsetSeconds + t->deltaSeconds);
1108 LocalDate ld = LocalDate::forComponents(st.year, st.month, st.day);
1109 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1110
1111 prev = t;
1112 isAfterFirst = true;
1113 }
1114
1115 // The last Transition's until time is the until time of the MatchingEra.
1116 extended::DateTuple untilTimeW;
1117 extended::DateTuple untilTimeS;
1118 extended::DateTuple untilTimeU;
1119 expandDateTuple(
1120 &prev->match->untilDateTime,
1121 prev->offsetSeconds,
1122 prev->deltaSeconds,
1123 &untilTimeW,
1124 &untilTimeS,
1125 &untilTimeU);
1126 prev->untilDateTime = untilTimeW;
1127 }
1128
1132 static void calcAbbreviations(Transition** begin, Transition** end) {
1133 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1134 logging::printf("calcAbbreviations(): #transitions: %d\n",
1135 (int) (end - begin));
1136 }
1137 for (Transition** iter = begin; iter != end; ++iter) {
1138 Transition* const t = *iter;
1139 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1140 logging::printf(
1141 "calcAbbreviations(): format:%s, deltaSeconds:%d, letter:%s\n",
1142 t->format(), t->deltaSeconds, t->abbrev);
1143 }
1144 createAbbreviation(
1145 t->abbrev,
1146 kAbbrevSize,
1147 t->format(),
1148 t->offsetSeconds,
1149 t->deltaSeconds,
1150 t->abbrev);
1151 }
1152 }
1153
1154 private:
1155 const typename D::ZoneInfoStore* mZoneInfoStore; // nullable
1156 typename D::ZoneInfoBroker mZoneInfoBroker;
1157
1158 // NOTE: Maybe move mNumMatches and mMatches into a MatchStorage object.
1159 mutable uint8_t mNumMatches = 0; // actual number of matches
1160 mutable MatchingEra mMatches[kMaxMatches];
1161 mutable TransitionStorage mTransitionStorage;
1162 };
1163
1169 class ExtendedZoneProcessor :
1170 public ExtendedZoneProcessorTemplate<extended::Info> {
1171
1172 public:
1174 static const uint8_t kTypeExtended = 4;
1175
1176 explicit ExtendedZoneProcessor(
1177 const extended::Info::ZoneInfo* zoneInfo = nullptr)
1178 : ExtendedZoneProcessorTemplate<extended::Info>(
1179 kTypeExtended, &mZoneInfoStore, (uintptr_t) zoneInfo)
1180 {}
1181
1182 private:
1183 extended::Info::ZoneInfoStore mZoneInfoStore;
1184 };
1185
1186 } // namespace ace_time
1187
1188 #endif
ace_time::Epoch::currentEpochYear
static int16_t currentEpochYear()
Get the current epoch year.
Definition: Epoch.h:27
ace_time::ExtendedZoneProcessorTemplate
An implementation of ZoneProcessor that supports for all zones defined by the TZ Database.
Definition: ExtendedZoneProcessor.h:81
ace_time::ExtendedZoneProcessorTemplate::printTargetNameTo
void printTargetNameTo(Print &printer) const override
Print the full identifier (e.g.
Definition: ExtendedZoneProcessor.h:235
ace_time::ExtendedZoneProcessorTemplate::kMaxTransitions
static const uint8_t kMaxTransitions
Max number of Transitions required for all Zones supported by this class.
Definition: ExtendedZoneProcessor.h:94
ace_time::ExtendedZoneProcessorTemplate::findByEpochSeconds
FindResult findByEpochSeconds(acetime_t epochSeconds) const override
Definition: ExtendedZoneProcessor.h:203
ace_time::ExtendedZoneProcessorTemplate::getTransitionAllocSize
uint8_t getTransitionAllocSize() const
Get the largest allocation size of TransitionStorage.
Definition: ExtendedZoneProcessor.h:263
ace_time::ExtendedZoneProcessorTemplate::TransitionForDateTime
extended::TransitionForDateTimeTemplate< D > TransitionForDateTime
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:105
ace_time::ExtendedZoneProcessorTemplate::resetTransitionAllocSize
void resetTransitionAllocSize()
Reset the TransitionStorage high water mark.
Definition: ExtendedZoneProcessor.h:258
ace_time::ExtendedZoneProcessorTemplate::printShortNameTo
void printShortNameTo(Print &printer) const override
Print a short human-readable identifier (e.g.
Definition: ExtendedZoneProcessor.h:231
ace_time::ExtendedZoneProcessorTemplate::Transition
extended::TransitionTemplate< D > Transition
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:97
ace_time::ExtendedZoneProcessorTemplate::ExtendedZoneProcessorTemplate
ExtendedZoneProcessorTemplate(uint8_t type, const typename D::ZoneInfoStore *zoneInfoStore, uintptr_t zoneKey)
Constructor.
Definition: ExtendedZoneProcessor.h:385
ace_time::ExtendedZoneProcessorTemplate::equalsZoneKey
bool equalsZoneKey(uintptr_t zoneKey) const override
Return true if ZoneProcessor is associated with the given opaque zoneKey.
Definition: ExtendedZoneProcessor.h:277
ace_time::ExtendedZoneProcessorTemplate::MatchingEra
extended::MatchingEraTemplate< D > MatchingEra
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:108
ace_time::ExtendedZoneProcessorTemplate::initForEpochSeconds
bool initForEpochSeconds(acetime_t epochSeconds) const
Initialize using the epochSeconds.
Definition: ExtendedZoneProcessor.h:296
ace_time::ExtendedZoneProcessorTemplate::setZoneKey
void setZoneKey(uintptr_t zoneKey) override
Set the opaque zoneKey of this object to a new value, reseting any internally cached information.
Definition: ExtendedZoneProcessor.h:267
ace_time::ExtendedZoneProcessorTemplate::getZoneId
uint32_t getZoneId() const override
Return the unique stable zoneId.
Definition: ExtendedZoneProcessor.h:118
ace_time::ExtendedZoneProcessorTemplate::printNameTo
void printNameTo(Print &printer) const override
Print a human-readable identifier (e.g.
Definition: ExtendedZoneProcessor.h:227
ace_time::ExtendedZoneProcessorTemplate::log
void log() const
Used only for debugging.
Definition: ExtendedZoneProcessor.h:242
ace_time::ExtendedZoneProcessorTemplate::TransitionForSeconds
extended::TransitionForSecondsTemplate< D > TransitionForSeconds
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:101
ace_time::ExtendedZoneProcessorTemplate::TransitionStorage
extended::TransitionStorageTemplate< kMaxTransitions, D > TransitionStorage
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:112
ace_time::ExtendedZoneProcessorTemplate::findByLocalDateTime
FindResult findByLocalDateTime(const LocalDateTime &ldt) const override
Return the search results at given LocalDateTime.
Definition: ExtendedZoneProcessor.h:122
ace_time::ExtendedZoneProcessorTemplate::setZoneInfoStore
void setZoneInfoStore(const typename D::ZoneInfoStore *zoneInfoStore)
Set the zone info store at runtime.
Definition: ExtendedZoneProcessor.h:287
ace_time::ExtendedZoneProcessorTemplate::initForYear
bool initForYear(int16_t year) const
Initialize the zone rules cache, keyed by the "current" year.
Definition: ExtendedZoneProcessor.h:306
ace_time::ExtendedZoneProcessorTemplate::isLink
bool isLink() const override
Return true if timezone is a Link entry pointing to a Zone entry.
Definition: ExtendedZoneProcessor.h:114
ace_time::ExtendedZoneProcessor
A specific implementation of ExtendedZoneProcessorTemplate that uses the extended::Info::ZoneXxxBroke...
Definition: ExtendedZoneProcessor.h:1170
ace_time::ExtendedZoneProcessor::kTypeExtended
static const uint8_t kTypeExtended
Unique TimeZone type identifier for ExtendedZoneProcessor.
Definition: ExtendedZoneProcessor.h:1174
ace_time::FindResult
Result of a search for transition at a specific epochSeconds or a specific LocalDateTime.
Definition: ZoneProcessor.h:23
ace_time::FindResult::fold
uint8_t fold
For findByLocalDateTime(), when type==kTypeOverlap, this is a copy of the requested LocalDateTime::fo...
Definition: ZoneProcessor.h:76
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::fold
uint8_t fold() const
Return the fold.
Definition: LocalDateTime.h:192
ace_time::LocalDateTime::year
int16_t year() const
Return the year.
Definition: LocalDateTime.h:156
ace_time::LocalDate
The date (year, month, day) representing the date without regards to time zone.
Definition: LocalDate.h:46
ace_time::LocalDate::forComponents
static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day)
Factory method using separated year, month and day fields.
Definition: LocalDate.h:153
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::year
int16_t year() const
Return the year.
Definition: LocalDate.h:301
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::ZoneInfoLow::ZoneInfoStore
A storage object that creates an ZoneInfoBroker from a key that identifies the ZoneInfo.
Definition: ZoneInfoLow.h:807
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::extended::TransitionStorageTemplate< kMaxTransitions, D >
ace_time::extended::TransitionStorageTemplate::getAllocSize
uint8_t getAllocSize() const
Return the maximum number of transitions which was allocated.
Definition: Transition.h:766
ace_time::extended::TransitionStorageTemplate::resetAllocSize
void resetAllocSize()
Reset the current allocation size.
Definition: Transition.h:759
ace_time::extended::TransitionStorageTemplate::findTransitionForDateTime
TransitionForDateTime findTransitionForDateTime(const LocalDateTime &ldt) const
Return the candidate Transitions matching the given dateTime.
Definition: Transition.h:677
ace_time::extended::TransitionStorageTemplate::findTransitionForSeconds
TransitionForSeconds findTransitionForSeconds(acetime_t epochSeconds) const
Return the Transition matching the given epochSeconds.
Definition: Transition.h:561
ace_time::extended::TransitionStorageTemplate::init
void init()
Initialize all pools to 0 size, usually when a new year is initialized.
Definition: Transition.h:382
ace_time::extended::TransitionStorageTemplate::log
void log() const
Verify that the indexes are valid.
Definition: Transition.h:733
common.h
Identifiers used by implementation code which need to be publically exported.
ace_time::kAbbrevSize
const uint8_t kAbbrevSize
Size of the c-string buffer needed to hold a time zone abbreviation.
Definition: common.h:44
ace_time::acetime_t
int32_t acetime_t
Type for the number of seconds from epoch.
Definition: common.h:24
ace_time::ZoneInfoLow::ZoneContext::kSuffixW
static const uint8_t kSuffixW
Represents 'w' or wall time.
Definition: ZoneInfoLow.h:88
ace_time::ZoneInfoLow::ZoneContext::kSuffixS
static const uint8_t kSuffixS
Represents 's' or standard time.
Definition: ZoneInfoLow.h:91
ace_time::ZoneInfoLow::ZoneContext::kSuffixU
static const uint8_t kSuffixU
Represents 'u' or UTC time.
Definition: ZoneInfoLow.h:94
ace_time::ZoneInfoLow::ZoneInfo
Representation of a given time zone, implemented as an array of ZoneEra records.
Definition: ZoneInfoLow.h:324
ace_time::extended::MatchingEraTemplate
Data structure that captures the matching ZoneEra and its ZoneRule transitions for a given year.
Definition: Transition.h:47
ace_time::extended::TransitionForDateTimeTemplate
The result of the findTransitionForDateTime(const LocalDatetime& ldt) method which can return 0,...
Definition: Transition.h:309
ace_time::extended::TransitionForDateTimeTemplate::curr
const TransitionTemplate< D > * curr
The matching transition, or null if not found or in gap.
Definition: Transition.h:315
ace_time::extended::TransitionForDateTimeTemplate::prev
const TransitionTemplate< D > * prev
The previous transition.
Definition: Transition.h:312
ace_time::extended::TransitionForDateTimeTemplate::num
uint8_t num
Number of matches: 0, 1, 2.
Definition: Transition.h:318
ace_time::extended::TransitionForSecondsTemplate
Tuple of a matching Transition and its 'fold'.
Definition: Transition.h:279
ace_time::extended::TransitionForSecondsTemplate::curr
const TransitionTemplate< D > * curr
The matching transition, or null if not found.
Definition: Transition.h:281
ace_time::extended::TransitionForSecondsTemplate::num
uint8_t num
Number of occurrences of the resulting LocalDateTime: 0, 1, or 2.
Definition: Transition.h:292
ace_time::extended::TransitionForSecondsTemplate::fold
uint8_t fold
1 if corresponding datetime occurred the second time
Definition: Transition.h:284
ace_time::extended::TransitionTemplate
Represents an interval of time where the time zone obeyed a certain UTC offset and DST delta.
Definition: Transition.h:112
ace_time::extended::TransitionTemplate::deltaSeconds
int32_t deltaSeconds
The DST delta seconds.
Definition: Transition.h:180
ace_time::extended::TransitionTemplate::abbrev
char abbrev[kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
Definition: Transition.h:188
ace_time::extended::TransitionTemplate::printTransitions
static void printTransitions(const char *prefix, const TransitionTemplate *const *begin, const TransitionTemplate *const *end)
Print an iterable of Transitions from 'begin' to 'end'.
Definition: Transition.h:258
ace_time::extended::TransitionTemplate::offsetSeconds
int32_t offsetSeconds
The standard time offset seconds, not the total offset.
Definition: Transition.h:177
ace_time::extended::YearMonthTuple
A simple tuple to represent a year/month pair.
Definition: ExtendedZoneProcessor.h:48