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