AceTime  1.11.2
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 <string.h> // memcpy()
10 #include <stdint.h> // uintptr_t
11 #include <AceCommon.h> // copyReplaceString()
12 #include "common/compat.h"
13 #include "internal/ZonePolicy.h"
14 #include "internal/ZoneInfo.h"
16 #include "common/logging.h"
17 #include "TimeOffset.h"
18 #include "LocalDate.h"
19 #include "OffsetDateTime.h"
20 #include "ZoneProcessor.h"
21 #include "local_date_mutation.h"
22 
23 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
24 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG 0
25 #endif
26 
27 class ExtendedZoneProcessorTest_compareEraToYearMonth;
28 class ExtendedZoneProcessorTest_compareEraToYearMonth2;
29 class ExtendedZoneProcessorTest_createMatch;
30 class ExtendedZoneProcessorTest_findMatches_simple;
31 class ExtendedZoneProcessorTest_findMatches_named;
32 class ExtendedZoneProcessorTest_findCandidateTransitions;
33 class ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
34 class ExtendedZoneProcessorTest_getTransitionTime;
35 class ExtendedZoneProcessorTest_createTransitionForYear;
36 class ExtendedZoneProcessorTest_normalizeDateTuple;
37 class ExtendedZoneProcessorTest_expandDateTuple;
38 class ExtendedZoneProcessorTest_calcInteriorYears;
39 class ExtendedZoneProcessorTest_getMostRecentPriorYear;
40 class ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
41 class ExtendedZoneProcessorTest_compareTransitionToMatch;
42 class ExtendedZoneProcessorTest_processTransitionMatchStatus;
43 class ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
44 class ExtendedZoneProcessorTest_createAbbreviation;
45 class ExtendedZoneProcessorTest_setZoneKey;
46 class TransitionStorageTest_getFreeAgent;
47 class TransitionStorageTest_getFreeAgent2;
48 class TransitionStorageTest_addFreeAgentToActivePool;
49 class TransitionStorageTest_reservePrior;
50 class TransitionStorageTest_addFreeAgentToCandidatePool;
51 class TransitionStorageTest_setFreeAgentAsPriorIfValid;
52 class TransitionStorageTest_addActiveCandidatesToActivePool;
53 class TransitionStorageTest_findTransitionForDateTime;
54 class TransitionStorageTest_resetCandidatePool;
55 class TransitionValidation;
56 
57 class Print;
58 
59 namespace ace_time {
60 namespace extended {
61 
62 //---------------------------------------------------------------------------
63 
68 struct DateTuple {
69  DateTuple() = default;
70 
71  DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
72  yearTiny(y), month(mon), day(d), suffix(mod), minutes(min) {}
73 
74  int8_t yearTiny; // [-127, 126], 127 will cause bugs
75  uint8_t month; // [1-12]
76  uint8_t day; // [1-31]
77  uint8_t suffix; // kSuffixS, kSuffixW, kSuffixU
78  int16_t minutes; // negative values allowed
79 
81  void log() const {
82  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
83  int hour = minutes / 60;
84  int minute = minutes - hour * 60;
85  char c = "wsu"[(suffix>>4)];
86  logging::printf("%04d-%02u-%02uT%02d:%02d%c",
87  yearTiny+LocalDate::kEpochYear, month, day, hour, minute, c);
88  }
89  }
90 };
91 
93 inline bool operator<(const DateTuple& a, const DateTuple& b) {
94  if (a.yearTiny < b.yearTiny) return true;
95  if (a.yearTiny > b.yearTiny) return false;
96  if (a.month < b.month) return true;
97  if (a.month > b.month) return false;
98  if (a.day < b.day) return true;
99  if (a.day > b.day) return false;
100  if (a.minutes < b.minutes) return true;
101  if (a.minutes > b.minutes) return false;
102  return false;
103 }
104 
105 inline bool operator>=(const DateTuple& a, const DateTuple& b) {
106  return ! (a < b);
107 }
108 
109 inline bool operator<=(const DateTuple& a, const DateTuple& b) {
110  return ! (b < a);
111 }
112 
113 inline bool operator>(const DateTuple& a, const DateTuple& b) {
114  return (b < a);
115 }
116 
118 inline bool operator==(const DateTuple& a, const DateTuple& b) {
119  return a.yearTiny == b.yearTiny
120  && a.month == b.month
121  && a.day == b.day
122  && a.minutes == b.minutes
123  && a.suffix == b.suffix;
124 }
125 
127 inline void normalizeDateTuple(DateTuple* dt) {
128  const int16_t kOneDayAsMinutes = 60 * 24;
129  if (dt->minutes <= -kOneDayAsMinutes) {
130  LocalDate ld = LocalDate::forTinyComponents(
131  dt->yearTiny, dt->month, dt->day);
132  local_date_mutation::decrementOneDay(ld);
133  dt->yearTiny = ld.yearTiny();
134  dt->month = ld.month();
135  dt->day = ld.day();
136  dt->minutes += kOneDayAsMinutes;
137  } else if (kOneDayAsMinutes <= dt->minutes) {
138  LocalDate ld = LocalDate::forTinyComponents(
139  dt->yearTiny, dt->month, dt->day);
140  local_date_mutation::incrementOneDay(ld);
141  dt->yearTiny = ld.yearTiny();
142  dt->month = ld.month();
143  dt->day = ld.day();
144  dt->minutes -= kOneDayAsMinutes;
145  } else {
146  // do nothing
147  }
148 }
149 
151 inline acetime_t subtractDateTuple(const DateTuple& a, const DateTuple& b) {
152  int32_t epochDaysA = LocalDate::forTinyComponents(
153  a.yearTiny, a.month, a.day).toEpochDays();
154  int32_t epochSecondsA = epochDaysA * 86400 + a.minutes * 60;
155 
156  int32_t epochDaysB = LocalDate::forTinyComponents(
157  b.yearTiny, b.month, b.day).toEpochDays();
158  int32_t epochSecondsB = epochDaysB * 86400 + b.minutes * 60;
159 
160  return epochSecondsA - epochSecondsB;
161 }
162 
163 //---------------------------------------------------------------------------
164 
167  int8_t yearTiny;
168  uint8_t month;
169 };
170 
177 template<typename ZEB>
184 
187 
189  ZEB era;
190 
193 
196 
199 
200  void log() const {
201  logging::printf("MatchingEra(");
202  logging::printf("start="); startDateTime.log();
203  logging::printf("; until="); untilDateTime.log();
204  logging::printf("; era=%c", (era.isNull()) ? '-' : '*');
205  logging::printf("; prevMatch=%c", (prevMatch) ? '*' : '-');
206  logging::printf(")");
207  }
208 };
209 
211 template <typename T>
212 void swap(T& a, T& b) {
213  T tmp = a;
214  a = b;
215  b = tmp;
216 }
217 
222 enum class MatchStatus : uint8_t {
223  kFarPast, // 0
224  kPrior, // 1
225  kExactMatch, // 2
226  kWithinMatch, // 3
227  kFarFuture, // 4
228 };
229 
230 inline bool isMatchStatusActive(MatchStatus status) {
231  return status == MatchStatus::kExactMatch
232  || status == MatchStatus::kWithinMatch
233  || status == MatchStatus::kPrior;
234 }
235 
236 //---------------------------------------------------------------------------
237 
270 template <typename ZEB, typename ZPB, typename ZRB>
272 
275 
281  ZRB rule;
282 
290 
291  union {
298 
304  };
305 
306  union {
313 
319  };
320 
321 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
322 
326  DateTuple originalTransitionTime;
327 #endif
328 
330  acetime_t startEpochSeconds;
331 
339  int16_t offsetMinutes;
340 
342  int16_t deltaMinutes;
343 
345  char abbrev[internal::kAbbrevSize];
346 
348  char letterBuf[2];
349 
350  union {
358 
363  MatchStatus matchStatus;
364  };
365 
366  //-------------------------------------------------------------------------
367 
368  const char* format() const {
369  return match->era.format();
370  }
371 
377  const char* letter() const {
378  // RULES column is '-' or hh:mm, so return nullptr to indicate this.
379  if (rule.isNull()) {
380  return nullptr;
381  }
382 
383  // RULES point to a named rule, and LETTER is a single, printable
384  // character.
385  char letter = rule.letter();
386  if (letter >= 32) {
387  return letterBuf;
388  }
389 
390  // RULES points to a named rule, and the LETTER is a string. The
391  // rule->letter is a non-printable number < 32, which is an index into
392  // a list of strings given by match->era->zonePolicy->letters[].
393  const ZPB policy = match->era.zonePolicy();
394  uint8_t numLetters = policy.numLetters();
395  if (letter >= numLetters) {
396  // This should never happen unless there is a programming error. If it
397  // does, return an empty string. (createTransitionForYear() sets
398  // letterBuf to a NUL terminated empty string if rule->letter < 32)
399  return letterBuf;
400  }
401 
402  // Return the string at index 'rule->letter'.
403  return policy.letter(letter);
404  }
405 
407  void log() const {
408  logging::printf("Transition(");
409  if (sizeof(acetime_t) <= sizeof(int)) {
410  logging::printf("start=%d", startEpochSeconds);
411  } else {
412  logging::printf("start=%ld", startEpochSeconds);
413  }
414  logging::printf("; status=%d", matchStatus);
415  logging::printf("; UTC");
418  logging::printf("; tt="); transitionTime.log();
419  logging::printf("; tts="); transitionTimeS.log();
420  logging::printf("; ttu="); transitionTimeU.log();
421  if (rule.isNull()) {
422  logging::printf("; rule=-");
423  } else {
424  logging::printf("; rule=");
425  logging::printf("[%d,%d]",
426  rule.fromYearTiny() + LocalDate::kEpochYear,
427  rule.toYearTiny() + LocalDate::kEpochYear);
428  }
429  }
430 
432  static void logHourMinute(int16_t minutes) {
433  char sign;
434  if (minutes < 0) {
435  sign = '-';
436  minutes = -minutes;
437  } else {
438  sign = '+';
439  }
440  uint8_t hour = minutes / 60;
441  uint8_t minute = minutes - hour * 60;
442  logging::printf("%c%02u:%02u", sign, (unsigned) hour, (unsigned) minute);
443  }
444 };
445 
451 template <typename ZEB, typename ZPB, typename ZRB>
453  const TransitionTemplate<ZEB, ZPB, ZRB>* transition;
454  uint8_t fold; // 1 if in the overlap, otherwise 0
455 };
456 
462 template <typename ZEB, typename ZPB, typename ZRB>
464  static constexpr int8_t kStatusGap = 0; // no exact match
465  static constexpr int8_t kStatusExact = 1; // one exact match
466  static constexpr int8_t kStatusOverlap = 2; // two matches
467 
468  const TransitionTemplate<ZEB, ZPB, ZRB>* transition0; // fold==0
469  const TransitionTemplate<ZEB, ZPB, ZRB>* transition1; // fold==1
470  int8_t searchStatus;
471 };
472 
504 template<uint8_t SIZE, typename ZEB, typename ZPB, typename ZRB>
506  public:
512 
519 
526 
529 
536  void init() {
537  for (uint8_t i = 0; i < SIZE; i++) {
538  mTransitions[i] = &mPool[i];
539  }
540  mIndexPrior = 0;
541  mIndexCandidates = 0;
542  mIndexFree = 0;
543  }
544 
547  return mTransitions[mIndexPrior];
548  }
549 
559  mIndexCandidates = mIndexPrior;
560  mIndexFree = mIndexPrior;
561  }
562 
563  Transition** getCandidatePoolBegin() {
564  return &mTransitions[mIndexCandidates];
565  }
566  Transition** getCandidatePoolEnd() {
567  return &mTransitions[mIndexFree];
568  }
569 
570  Transition** getActivePoolBegin() {
571  return &mTransitions[0];
572  }
573  Transition** getActivePoolEnd() {
574  return &mTransitions[mIndexFree];
575  }
576 
583  if (mIndexFree < SIZE) {
584  // Allocate a free transition.
585  if (mIndexFree >= mAllocSize) {
586  mAllocSize = mIndexFree + 1;
587  }
588  return mTransitions[mIndexFree];
589  } else {
590  // No more transition available in the buffer, so just return the last
591  // one. This will probably cause a bug in the timezone calculations, but
592  // I think this is better than triggering undefined behavior by running
593  // off the end of the mTransitions buffer.
594  return mTransitions[SIZE - 1];
595  }
596  }
597 
606  if (mIndexFree >= SIZE) return;
607  mIndexFree++;
608  mIndexPrior = mIndexFree;
609  mIndexCandidates = mIndexFree;
610  }
611 
621  getFreeAgent(); // allocate a new Transition
622 
623  mIndexCandidates++;
624  mIndexFree++;
625  return &mTransitions[mIndexPrior];
626  }
627 
630  Transition* ft = mTransitions[mIndexFree];
631  Transition* prior = mTransitions[mIndexPrior];
632  if ((prior->isValidPrior && prior->transitionTime < ft->transitionTime)
633  || !prior->isValidPrior) {
634  ft->isValidPrior = true;
635  prior->isValidPrior = false;
636  swap(mTransitions[mIndexPrior], mTransitions[mIndexFree]);
637  }
638  }
639 
646  mIndexCandidates--;
647  }
648 
656  if (mIndexFree >= SIZE) return;
657 
658  // This implementation makes pair-wise swaps to shift the current
659  // Transition leftwards into its correctly sorted position. At first
660  // glance, this seem inefficient compared to the alternative
661  // implementation where we save the current Transition, then slide all the
662  // elements to the left by one position rightwards. However,
663  // MemoryBenchmark shows that this implementation is 46 bytes smaller on
664  // an AVR processor.
665  for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
666  Transition* curr = mTransitions[i];
667  Transition* prev = mTransitions[i - 1];
668  if (curr->transitionTime >= prev->transitionTime) break;
669  mTransitions[i] = prev;
670  mTransitions[i - 1] = curr;
671  }
672  mIndexFree++;
673  }
674 
682  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
683  logging::printf("addActiveCandidatesToActivePool()\n");
684  }
685 
686  // Shift active candidates to the left into the Active pool.
687  uint8_t iActive = mIndexPrior;
688  uint8_t iCandidate = mIndexCandidates;
689  for (; iCandidate < mIndexFree; iCandidate++) {
690  if (isMatchStatusActive(mTransitions[iCandidate]->matchStatus)) {
691  if (iActive != iCandidate) {
692  // Must use swap(), because we are moving pointers instead of the
693  // actual Transition objects.
694  swap(mTransitions[iActive], mTransitions[iCandidate]);
695  }
696  ++iActive;
697  }
698  }
699 
700  mIndexPrior = iActive;
701  mIndexCandidates = iActive;
702  mIndexFree = iActive;
703 
704  return mTransitions[iActive - 1];
705  }
706 
716  const {
717  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
718  logging::printf(
719  "findTransitionForSeconds(): mIndexFree: %d\n", mIndexFree);
720  }
721 
722  const Transition* prevMatch = nullptr;
723  const Transition* match = nullptr;
724  for (uint8_t i = 0; i < mIndexFree; i++) {
725  const Transition* candidate = mTransitions[i];
726  if (candidate->startEpochSeconds > epochSeconds) break;
727  prevMatch = match;
728  match = candidate;
729  }
730  uint8_t fold = calculateFold(epochSeconds, match, prevMatch);
731  return MatchingTransition{ match, fold };
732  }
733 
734  static uint8_t calculateFold(
735  acetime_t epochSeconds,
736  const Transition* match,
737  const Transition* prevMatch) {
738 
739  if (match == nullptr) return 0;
740  if (prevMatch == nullptr) return 0;
741 
742  // Check if epochSeconds occurs during a "fall back" DST transition.
743  acetime_t overlapSeconds = subtractDateTuple(
744  prevMatch->untilDateTime, match->startDateTime);
745  if (overlapSeconds <= 0) return 0;
746  acetime_t secondsFromTransitionStart =
747  epochSeconds - match->startEpochSeconds;
748  if (secondsFromTransitionStart >= overlapSeconds) return 0;
749 
750  // EpochSeconds occurs within the "fall back" overlap.
751  return 1;
752  }
753 
760  const {
761  // Convert LocalDateTime to DateTuple.
762  DateTuple localDate{
763  ldt.yearTiny(),
764  ldt.month(),
765  ldt.day(),
766  (int16_t) (ldt.hour() * 60 + ldt.minute()),
768  };
769 
770  // Examine adjacent pairs of Transitions, looking for an exact match, gap,
771  // or overlap.
772  const Transition* prevCandidate = nullptr;
773  const Transition* candidate = nullptr;
774  int8_t searchStatus = TransitionResult::kStatusGap;
775  for (uint8_t i = 0; i < mIndexFree; i++) {
776  candidate = mTransitions[i];
777 
778  DateTuple startDateTime = candidate->startDateTime;
779  DateTuple untilDateTime = candidate->untilDateTime;
780  bool isExactMatch = (startDateTime <= localDate)
781  && (localDate < untilDateTime);
782 
783  if (isExactMatch) {
784  // Check for a previous exact match to detect an overlap.
785  if (searchStatus == TransitionResult::kStatusExact) {
786  searchStatus = TransitionResult::kStatusOverlap;
787  break;
788  }
789 
790  // Loop again to detect an overlap.
791  searchStatus = TransitionResult::kStatusExact;
792 
793  } else if (startDateTime > localDate) {
794  // Exit loop since no more candidate transition.
795  break;
796  }
797 
798  prevCandidate = candidate;
799 
800  // Set nullptr so that if the loop runs off the end of the list of
801  // Transitions, the candidate is marked as nullptr.
802  candidate = nullptr;
803  }
804 
805  // Check if the prev was an exact match, and clear the current to
806  // avoid confusion.
807  if (searchStatus == TransitionResult::kStatusExact) {
808  candidate = nullptr;
809  }
810 
811  // This should get optimized by RVO.
812  return TransitionResult{
813  prevCandidate,
814  candidate,
815  searchStatus,
816  };
817  }
818 
820  void log() const {
821  logging::printf("TransitionStorage: ");
822  logging::printf("nActives=%d", mIndexPrior);
823  logging::printf(", nPrior=%d", mIndexCandidates - mIndexPrior);
824  logging::printf(", nCandidates=%d", mIndexFree - mIndexCandidates);
825  logging::printf(", nFree=%d", SIZE - mIndexFree);
826  logging::printf("\n");
827 
828  if (mIndexPrior != 0) {
829  logging::printf(" Actives:\n");
830  for (uint8_t i = 0; i < mIndexPrior; i++) {
831  logging::printf(" ");
832  mTransitions[i]->log();
833  logging::printf("\n");
834  }
835  }
836  if (mIndexPrior != mIndexCandidates) {
837  logging::printf(" Prior: \n");
838  logging::printf(" ");
839  mTransitions[mIndexPrior]->log();
840  logging::printf("\n");
841  }
842  if (mIndexCandidates != mIndexFree) {
843  logging::printf(" Candidates:\n");
844  for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
845  logging::printf(" ");
846  mTransitions[i]->log();
847  logging::printf("\n");
848  }
849  }
850  }
851 
853  void resetAllocSize() { mAllocSize = 0; }
854 
860  uint8_t getAllocSize() const { return mAllocSize; }
861 
862  private:
863  friend class ::TransitionStorageTest_getFreeAgent;
864  friend class ::TransitionStorageTest_getFreeAgent2;
865  friend class ::TransitionStorageTest_addFreeAgentToActivePool;
866  friend class ::TransitionStorageTest_reservePrior;
867  friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
868  friend class ::TransitionStorageTest_setFreeAgentAsPriorIfValid;
869  friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
870  friend class ::TransitionStorageTest_findTransitionForDateTime;
871  friend class ::TransitionStorageTest_resetCandidatePool;
872 
874  Transition* getTransition(uint8_t i) {
875  return mTransitions[i];
876  }
877 
878  Transition mPool[SIZE];
879  Transition* mTransitions[SIZE];
880  uint8_t mIndexPrior;
881  uint8_t mIndexCandidates;
882  uint8_t mIndexFree;
883 
885  uint8_t mAllocSize = 0;
886 };
887 
888 } // namespace extended
889 
921 template <typename BF, typename ZIB, typename ZEB, typename ZPB, typename ZRB>
923  public:
934  static const uint8_t kMaxTransitions = 8;
935 
938 
942 
946 
949 
953 
954  bool isLink() const override { return mZoneInfoBroker.isLink(); }
955 
956  uint32_t getZoneId(bool followLink = false) const override {
957  ZIB zib = (isLink() && followLink)
958  ? mZoneInfoBroker.targetZoneInfo()
959  : mZoneInfoBroker;
960  return zib.zoneId();
961  }
962 
963  TimeOffset getUtcOffset(acetime_t epochSeconds) const override {
964  bool success = initForEpochSeconds(epochSeconds);
965  if (!success) return TimeOffset::forError();
966  MatchingTransition matchingTransition =
967  mTransitionStorage.findTransitionForSeconds(epochSeconds);
968  const Transition* transition = matchingTransition.transition;
969  return (transition)
971  transition->offsetMinutes + transition->deltaMinutes)
973  }
974 
975  TimeOffset getDeltaOffset(acetime_t epochSeconds) const override {
976  bool success = initForEpochSeconds(epochSeconds);
977  if (!success) return TimeOffset::forError();
978  MatchingTransition matchingTransition =
979  mTransitionStorage.findTransitionForSeconds(epochSeconds);
980  const Transition* transition = matchingTransition.transition;
981  return (transition)
982  ? TimeOffset::forMinutes(transition->deltaMinutes)
984  }
985 
986  const char* getAbbrev(acetime_t epochSeconds) const override {
987  bool success = initForEpochSeconds(epochSeconds);
988  if (!success) return "";
989  MatchingTransition matchingTransition =
990  mTransitionStorage.findTransitionForSeconds(epochSeconds);
991  const Transition* transition = matchingTransition.transition;
992  return (transition) ? transition->abbrev : "";
993  }
994 
1014  OffsetDateTime getOffsetDateTime(const LocalDateTime& ldt) const override {
1015  bool success = initForYear(ldt.year());
1016  if (! success) {
1017  return OffsetDateTime::forError();
1018  }
1019 
1020  // Find the Transition(s) in the gap or overlap.
1021  TransitionResult result =
1022  mTransitionStorage.findTransitionForDateTime(ldt);
1023 
1024  // Extract the appropriate Transition, depending on the request ldt.fold
1025  // and the result.searchStatus.
1026  bool needsNormalization = false;
1027  const Transition* transition;
1028  if (result.searchStatus == TransitionResult::kStatusExact) {
1029  transition = result.transition0;
1030  } else {
1031  if (result.transition0 == nullptr || result.transition1 == nullptr) {
1032  // ldt was far past or far future, and didn't match anything.
1033  transition = nullptr;
1034  } else {
1035  needsNormalization =
1036  (result.searchStatus == TransitionResult::kStatusGap);
1037  transition = (ldt.fold() == 0)
1038  ? result.transition0
1039  : result.transition1;
1040  }
1041  }
1042 
1043  if (! transition) {
1044  return OffsetDateTime::forError();
1045  }
1046 
1048  transition->offsetMinutes + transition->deltaMinutes);
1049  auto odt = OffsetDateTime::forLocalDateTimeAndOffset(ldt, offset);
1050 
1051  if (needsNormalization) {
1052  acetime_t epochSeconds = odt.toEpochSeconds();
1053 
1054  // If in the gap, normalization means that we convert to epochSeconds
1055  // then perform another search through the Transitions, then use
1056  // that new Transition to convert the epochSeconds to OffsetDateTime. It
1057  // turns out that this process identical to just using the other
1058  // Transition returned in TransitionResult above.
1059  const Transition* otherTransition = (ldt.fold() == 0)
1060  ? result.transition1
1061  : result.transition0;
1062  TimeOffset otherOffset = TimeOffset::forMinutes(
1063  otherTransition->offsetMinutes + otherTransition->deltaMinutes);
1064  odt = OffsetDateTime::forEpochSeconds(epochSeconds, otherOffset);
1065 
1066  // Invert the fold.
1067  // 1) The normalization process causes the LocalDateTime to jump to the
1068  // other transition.
1069  // 2) Provides a user-accessible indicator that a gap normalization was
1070  // performed.
1071  odt.fold(1 - ldt.fold());
1072  }
1073 
1074  return odt;
1075  }
1076 
1085  OffsetDateTime getOffsetDateTime(acetime_t epochSeconds) const override {
1086  bool success = initForEpochSeconds(epochSeconds);
1087  if (!success) return OffsetDateTime::forError();
1088 
1089  MatchingTransition matchingTransition =
1090  mTransitionStorage.findTransitionForSeconds(epochSeconds);
1091  const Transition* transition = matchingTransition.transition;
1092  TimeOffset timeOffset = (transition)
1094  transition->offsetMinutes + transition->deltaMinutes)
1097  epochSeconds, timeOffset, matchingTransition.fold);
1098  }
1099 
1100  void printNameTo(Print& printer, bool followLink = false) const override {
1101  ZIB zib = (isLink() && followLink)
1102  ? mZoneInfoBroker.targetZoneInfo()
1103  : mZoneInfoBroker;
1104  zib.printNameTo(printer);
1105  }
1106 
1107  void printShortNameTo(Print& printer, bool followLink = false)
1108  const override {
1109  ZIB zib = (isLink() && followLink)
1110  ? mZoneInfoBroker.targetZoneInfo()
1111  : mZoneInfoBroker;
1112  zib.printShortNameTo(printer);
1113  }
1114 
1116  void log() const {
1117  logging::printf("ExtendedZoneProcessor\n");
1118  logging::printf(" mYear: %d\n", mYear);
1119  logging::printf(" mNumMatches: %d\n", mNumMatches);
1120  for (int i = 0; i < mNumMatches; i++) {
1121  logging::printf(" Match %d: ", i);
1122  mMatches[i].log();
1123  logging::printf("\n");
1124  }
1125  mTransitionStorage.log();
1126  }
1127 
1130  mTransitionStorage.resetAllocSize();
1131  }
1132 
1134  uint8_t getTransitionAllocSize() const {
1135  return mTransitionStorage.getAllocSize();
1136  }
1137 
1138  void setZoneKey(uintptr_t zoneKey) override {
1139  if (! mBrokerFactory) return;
1140  if (mZoneInfoBroker.equals(zoneKey)) return;
1141 
1142  mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
1143  mYear = 0;
1144  mIsFilled = false;
1145  mNumMatches = 0;
1146  resetTransitionAllocSize(); // clear the alloc size for new zone
1147  }
1148 
1149  bool equalsZoneKey(uintptr_t zoneKey) const override {
1150  return mZoneInfoBroker.equals(zoneKey);
1151  }
1152 
1159  void setBrokerFactory(const BF* brokerFactory) {
1160  mBrokerFactory = brokerFactory;
1161  }
1162 
1168  bool initForEpochSeconds(acetime_t epochSeconds) const {
1169  LocalDate ld = LocalDate::forEpochSeconds(epochSeconds);
1170  return initForYear(ld.year());
1171  }
1172 
1178  bool initForYear(int16_t year) const {
1179  if (isFilled(year)) return true;
1180  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1181  logging::printf("initForYear(): %d\n", year);
1182  }
1183 
1184  mYear = year;
1185  mNumMatches = 0; // clear cache
1186  mTransitionStorage.init();
1187 
1188  if (year < mZoneInfoBroker.zoneContext()->startYear - 1
1189  || mZoneInfoBroker.zoneContext()->untilYear < year) {
1190  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1191  logging::printf(
1192  "initForYear(): Year %d out of valid range [%d, %d)\n",
1193  year,
1194  mZoneInfoBroker.zoneContext()->startYear,
1195  mZoneInfoBroker.zoneContext()->untilYear);
1196  }
1197  return false;
1198  }
1199 
1200  extended::YearMonthTuple startYm = {
1201  (int8_t) (year - LocalDate::kEpochYear - 1), 12 };
1202  extended::YearMonthTuple untilYm = {
1203  (int8_t) (year - LocalDate::kEpochYear + 1), 2 };
1204 
1205  // Step 1. The equivalent steps for the Python version are in the
1206  // AceTimePython project, under
1207  // zone_processor.ZoneProcessor.init_for_year().
1208  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1209  logging::printf("==== Step 1: findMatches()\n");
1210  }
1211  mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
1212  kMaxMatches);
1213  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1214 
1215  // Step 2
1216  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1217  logging::printf("==== Step 2: createTransitions()\n");
1218  }
1219  createTransitions(mTransitionStorage, mMatches, mNumMatches);
1220  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1221 
1222  // Step 3
1223  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1224  logging::printf("==== Step 3: fixTransitionTimes()\n");
1225  }
1226  Transition** begin = mTransitionStorage.getActivePoolBegin();
1227  Transition** end = mTransitionStorage.getActivePoolEnd();
1228  fixTransitionTimes(begin, end);
1229  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1230 
1231  // Step 4
1232  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1233  logging::printf("==== Step 4: generateStartUntilTimes()\n");
1234  }
1235  generateStartUntilTimes(begin, end);
1236  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1237 
1238  // Step 5
1239  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1240  logging::printf("==== Step 5: calcAbbreviations()\n");
1241  }
1242  calcAbbreviations(begin, end);
1243  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1244 
1245  mIsFilled = true;
1246  return true;
1247  }
1248 
1249  protected:
1260  uint8_t type,
1261  const BF* brokerFactory /*nullable*/,
1262  uintptr_t zoneKey
1263  ) :
1264  ZoneProcessor(type),
1265  mBrokerFactory(brokerFactory)
1266  {
1267  setZoneKey(zoneKey);
1268  }
1269 
1270  private:
1271  friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
1272  friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
1273  friend class ::ExtendedZoneProcessorTest_createMatch;
1274  friend class ::ExtendedZoneProcessorTest_findMatches_simple;
1275  friend class ::ExtendedZoneProcessorTest_findMatches_named;
1276  friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
1277  friend class ::ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
1278  friend class ::ExtendedZoneProcessorTest_getTransitionTime;
1279  friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
1280  friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
1281  friend class ::ExtendedZoneProcessorTest_expandDateTuple;
1282  friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
1283  friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
1284  friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
1285  friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
1286  friend class ::ExtendedZoneProcessorTest_processTransitionMatchStatus;
1287  friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
1288  friend class ::ExtendedZoneProcessorTest_createAbbreviation;
1289  friend class ::ExtendedZoneProcessorTest_setZoneKey;
1290  friend class ::TransitionStorageTest_findTransitionForDateTime;
1291  friend class ::TransitionValidation;
1292 
1293  // Disable copy constructor and assignment operator.
1295  const ExtendedZoneProcessorTemplate&) = delete;
1296  ExtendedZoneProcessorTemplate& operator=(
1297  const ExtendedZoneProcessorTemplate&) = delete;
1298 
1303  static const uint8_t kMaxMatches = 4;
1304 
1309  static const uint8_t kMaxInteriorYears = 4;
1310 
1311  bool equals(const ZoneProcessor& other) const override {
1312  return mZoneInfoBroker.equals(
1313  ((const ExtendedZoneProcessorTemplate&) other).mZoneInfoBroker);
1314  }
1315 
1317  bool isFilled(int16_t year) const {
1318  return mIsFilled && (year == mYear);
1319  }
1320 
1328  static uint8_t findMatches(
1329  const ZIB& zoneInfo,
1330  const extended::YearMonthTuple& startYm,
1331  const extended::YearMonthTuple& untilYm,
1332  MatchingEra* matches,
1333  uint8_t maxMatches
1334  ) {
1335  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1336  logging::printf("findMatches()\n");
1337  }
1338  uint8_t iMatch = 0;
1339  MatchingEra* prevMatch = nullptr;
1340  for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
1341  const ZEB era = zoneInfo.era(iEra);
1342  if (eraOverlapsInterval(prevMatch, era, startYm, untilYm)) {
1343  if (iMatch < maxMatches) {
1344  matches[iMatch] = createMatch(prevMatch, era, startYm, untilYm);
1345  prevMatch = &matches[iMatch];
1346  iMatch++;
1347  }
1348  }
1349  }
1350  return iMatch;
1351  }
1352 
1364  static bool eraOverlapsInterval(
1365  const MatchingEra* prevMatch,
1366  const ZEB& era,
1367  const extended::YearMonthTuple& startYm,
1368  const extended::YearMonthTuple& untilYm) {
1369  return (prevMatch == nullptr || compareEraToYearMonth(
1370  prevMatch->era, untilYm.yearTiny, untilYm.month) < 0)
1371  && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1372  }
1373 
1375  static int8_t compareEraToYearMonth(const ZEB& era,
1376  int8_t yearTiny, uint8_t month) {
1377  if (era.untilYearTiny() < yearTiny) return -1;
1378  if (era.untilYearTiny() > yearTiny) return 1;
1379  if (era.untilMonth() < month) return -1;
1380  if (era.untilMonth() > month) return 1;
1381  if (era.untilDay() > 1) return 1;
1382  //if (era.untilTimeMinutes() < 0) return -1; // never possible
1383  if (era.untilTimeMinutes() > 0) return 1;
1384  return 0;
1385  }
1386 
1393  static MatchingEra createMatch(
1394  MatchingEra* prevMatch,
1395  const ZEB& era,
1396  const extended::YearMonthTuple& startYm,
1397  const extended::YearMonthTuple& untilYm) {
1398 
1399  // If prevMatch is null, set startDate to be earlier than all valid
1400  // ZoneEra.
1401  extended::DateTuple startDate = (prevMatch == nullptr)
1402  ? extended::DateTuple{
1404  1,
1405  1,
1406  0,
1408  }
1409  : extended::DateTuple{
1410  prevMatch->era.untilYearTiny(),
1411  prevMatch->era.untilMonth(),
1412  prevMatch->era.untilDay(),
1413  (int16_t) prevMatch->era.untilTimeMinutes(),
1414  prevMatch->era.untilTimeSuffix()
1415  };
1416  extended::DateTuple lowerBound{
1417  startYm.yearTiny,
1418  startYm.month,
1419  1,
1420  0,
1422  };
1423  if (startDate < lowerBound) {
1424  startDate = lowerBound;
1425  }
1426 
1427  extended::DateTuple untilDate{
1428  era.untilYearTiny(),
1429  era.untilMonth(),
1430  era.untilDay(),
1431  (int16_t) era.untilTimeMinutes(),
1432  era.untilTimeSuffix()
1433  };
1434  extended::DateTuple upperBound{
1435  untilYm.yearTiny,
1436  untilYm.month,
1437  1,
1438  0,
1440  };
1441  if (upperBound < untilDate) {
1442  untilDate = upperBound;
1443  }
1444 
1445  return {startDate, untilDate, era, prevMatch, 0, 0};
1446  }
1447 
1452  static void createTransitions(
1453  TransitionStorage& transitionStorage,
1454  MatchingEra* matches,
1455  uint8_t numMatches) {
1456  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1457  logging::printf("createTransitions()\n");
1458  }
1459 
1460  for (uint8_t i = 0; i < numMatches; i++) {
1461  createTransitionsForMatch(transitionStorage, &matches[i]);
1462  }
1463  }
1464 
1466  static void createTransitionsForMatch(
1467  TransitionStorage& transitionStorage,
1468  MatchingEra* match) {
1469  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1470  logging::printf("== createTransitionsForMatch()\n");
1471  }
1472  const ZPB policy = match->era.zonePolicy();
1473  if (policy.isNull()) {
1474  createTransitionsFromSimpleMatch(transitionStorage, match);
1475  } else {
1476  createTransitionsFromNamedMatch(transitionStorage, match);
1477  }
1478  }
1479 
1480  static void createTransitionsFromSimpleMatch(
1481  TransitionStorage& transitionStorage,
1482  MatchingEra* match) {
1483  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1484  logging::printf("== createTransitionsFromSimpleMatch()\n");
1485  }
1486 
1487  Transition* freeTransition = transitionStorage.getFreeAgent();
1488  createTransitionForYear(freeTransition, 0 /*not used*/,
1489  ZRB() /*rule*/, match);
1490  freeTransition->matchStatus = extended::MatchStatus::kExactMatch;
1491  match->lastOffsetMinutes = freeTransition->offsetMinutes;
1492  match->lastDeltaMinutes = freeTransition->deltaMinutes;
1493  transitionStorage.addFreeAgentToActivePool();
1494  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1495  freeTransition->log();
1496  logging::printf("\n");
1497  }
1498  }
1499 
1500  static void createTransitionsFromNamedMatch(
1501  TransitionStorage& transitionStorage,
1502  MatchingEra* match) {
1503  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1504  logging::printf("== createTransitionsFromNamedMatch()\n");
1505  }
1506 
1507  transitionStorage.resetCandidatePool();
1508  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1509  match->log(); logging::printf("\n");
1510  }
1511 
1512  // Pass 1: Find candidate transitions using whole years.
1513  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1514  logging::printf("---- Pass 1: findCandidateTransitions()\n");
1515  }
1516  findCandidateTransitions(transitionStorage, match);
1517  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1518  transitionStorage.log();
1519  }
1520 
1521  // Pass 2: Fix the transitions times, converting 's' and 'u' into 'w'
1522  // uniformly.
1523  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1524  logging::printf("---- Pass 2: fixTransitionTimes()\n");
1525  }
1526  fixTransitionTimes(
1527  transitionStorage.getCandidatePoolBegin(),
1528  transitionStorage.getCandidatePoolEnd());
1529 
1530  // Pass 3: Select only those Transitions which overlap with the actual
1531  // start and until times of the MatchingEra.
1532  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1533  logging::printf("---- Pass 3: selectActiveTransitions()\n");
1534  }
1535  selectActiveTransitions(
1536  transitionStorage.getCandidatePoolBegin(),
1537  transitionStorage.getCandidatePoolEnd());
1538  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1539  transitionStorage.log();
1540  }
1541  Transition* lastTransition =
1542  transitionStorage.addActiveCandidatesToActivePool();
1543  match->lastOffsetMinutes = lastTransition->offsetMinutes;
1544  match->lastDeltaMinutes = lastTransition->deltaMinutes;
1545  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1546  transitionStorage.log();
1547  }
1548  }
1549 
1550  static void findCandidateTransitions(
1551  TransitionStorage& transitionStorage,
1552  const MatchingEra* match) {
1553  using extended::MatchStatus;
1554 
1555  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1556  logging::printf("findCandidateTransitions(): \n");
1557  match->log();
1558  logging::printf("\n");
1559  }
1560  const ZPB policy = match->era.zonePolicy();
1561  uint8_t numRules = policy.numRules();
1562  int8_t startY = match->startDateTime.yearTiny;
1563  int8_t endY = match->untilDateTime.yearTiny;
1564 
1565  // The prior is referenced through a handle (i.e. pointer to pointer)
1566  // because the actual pointer to the prior could change through the
1567  // transitionStorage.setFreeAgentAsPriorIfValid() method.
1568  Transition** prior = transitionStorage.reservePrior();
1569  (*prior)->isValidPrior = false; // indicates "no prior transition"
1570  for (uint8_t r = 0; r < numRules; r++) {
1571  const ZRB rule = policy.rule(r);
1572 
1573  // Add Transitions for interior years
1574  int8_t interiorYears[kMaxInteriorYears];
1575  uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1576  rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1577  for (uint8_t y = 0; y < numYears; y++) {
1578  int8_t year = interiorYears[y];
1579  Transition* t = transitionStorage.getFreeAgent();
1580  createTransitionForYear(t, year, rule, match);
1581  MatchStatus status = compareTransitionToMatchFuzzy(t, match);
1582  if (status == MatchStatus::kPrior) {
1583  transitionStorage.setFreeAgentAsPriorIfValid();
1584  } else if (status == MatchStatus::kWithinMatch) {
1585  transitionStorage.addFreeAgentToCandidatePool();
1586  } else {
1587  // Must be kFarFuture.
1588  // Do nothing, allowing the free agent to be reused.
1589  }
1590  }
1591 
1592  // Add Transition for prior year
1593  int8_t priorYear = getMostRecentPriorYear(
1594  rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1595  if (priorYear != LocalDate::kInvalidYearTiny) {
1596  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1597  logging::printf(
1598  "findCandidateTransitions(): priorYear: %d\n",
1599  priorYear + LocalDate::kEpochYear);
1600  }
1601  Transition* t = transitionStorage.getFreeAgent();
1602  createTransitionForYear(t, priorYear, rule, match);
1603  transitionStorage.setFreeAgentAsPriorIfValid();
1604  }
1605  }
1606 
1607  // Add the reserved prior into the Candidate pool only if 'isValidPrior'
1608  // is true.
1609  if ((*prior)->isValidPrior) {
1610  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1611  logging::printf(
1612  "findCandidateTransitions(): adding prior to Candidate pool\n");
1613  (*prior)->log();
1614  logging::printf("\n");
1615  }
1616  transitionStorage.addPriorToCandidatePool();
1617  }
1618  }
1619 
1639  static uint8_t calcInteriorYears(
1640  int8_t* interiorYears,
1641  uint8_t maxInteriorYears,
1642  int8_t fromYear, int8_t toYear,
1643  int8_t startYear, int8_t endYear) {
1644  uint8_t i = 0;
1645  for (int8_t year = startYear; year <= endYear; year++) {
1646  if (fromYear <= year && year <= toYear) {
1647  interiorYears[i] = year;
1648  i++;
1649  if (i >= maxInteriorYears) break;
1650  }
1651  }
1652  return i;
1653  }
1654 
1661  static void createTransitionForYear(
1662  Transition* t,
1663  int8_t year,
1664  const ZRB& rule,
1665  const MatchingEra* match) {
1666  t->match = match;
1667  t->rule = rule;
1668  t->offsetMinutes = match->era.offsetMinutes();
1669  t->letterBuf[0] = '\0';
1670 
1671  if (! rule.isNull()) {
1672  t->transitionTime = getTransitionTime(year, rule);
1673  t->deltaMinutes = rule.deltaMinutes();
1674 
1675  char letter = rule.letter();
1676  if (letter >= 32) {
1677  // If LETTER is a '-', treat it the same as an empty string.
1678  if (letter != '-') {
1679  t->letterBuf[0] = letter;
1680  t->letterBuf[1] = '\0';
1681  }
1682  } else {
1683  // rule->letter is a long string, so is referenced as an offset index
1684  // into the ZonePolicy.letters array. The string cannot fit in
1685  // letterBuf, so will be retrieved by the letter() method below.
1686  }
1687  } else {
1688  // Create a Transition using the MatchingEra for the transitionTime.
1689  // Used for simple MatchingEra.
1690  t->transitionTime = match->startDateTime;
1691  t->deltaMinutes = match->era.deltaMinutes();
1692  }
1693  }
1694 
1707  static int8_t getMostRecentPriorYear(
1708  int8_t fromYear, int8_t toYear,
1709  int8_t startYear, int8_t endYear) {
1710 
1711  (void) endYear; // disable compiler warnings
1712 
1713  if (fromYear < startYear) {
1714  if (toYear < startYear) {
1715  return toYear;
1716  } else {
1717  return startYear - 1;
1718  }
1719  } else {
1721  }
1722  }
1723 
1728  static extended::DateTuple getTransitionTime(
1729  int8_t yearTiny, const ZRB& rule) {
1730 
1731  internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1732  yearTiny + LocalDate::kEpochYear,
1733  rule.inMonth(),
1734  rule.onDayOfWeek(),
1735  rule.onDayOfMonth());
1736  return {
1737  yearTiny,
1738  monthDay.month,
1739  monthDay.day,
1740  (int16_t) rule.atTimeMinutes(),
1741  rule.atTimeSuffix()
1742  };
1743  }
1744 
1755  static extended::MatchStatus compareTransitionToMatchFuzzy(
1756  const Transition* t, const MatchingEra* match) {
1757  using extended::MatchStatus;
1758 
1759  int16_t ttMonths = t->transitionTime.yearTiny * 12
1760  + t->transitionTime.month;
1761 
1762  int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1763  + match->startDateTime.month;
1764  if (ttMonths < matchStartMonths - 1) return MatchStatus::kPrior;
1765 
1766  int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1767  + match->untilDateTime.month;
1768  if (matchUntilMonths + 2 <= ttMonths) return MatchStatus::kFarFuture;
1769 
1770  return MatchStatus::kWithinMatch;
1771  }
1772 
1781  static void fixTransitionTimes(Transition** begin, Transition** end) {
1782  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1783  logging::printf("fixTransitionTimes(): START; #transitions=%d\n",
1784  (int) (end - begin));
1785  printTransitions(begin, end);
1786  }
1787 
1788  // extend first Transition to -infinity
1789  Transition* prev = *begin;
1790 
1791  for (Transition** iter = begin; iter != end; ++iter) {
1792  Transition* curr = *iter;
1793  expandDateTuple(
1794  &curr->transitionTime,
1795  prev->offsetMinutes,
1796  prev->deltaMinutes,
1797  &curr->transitionTime,
1798  &curr->transitionTimeS,
1799  &curr->transitionTimeU);
1800  prev = curr;
1801  }
1802  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1803  logging::printf("fixTransitionTimes(): FIXED\n");
1804  printTransitions(begin, end);
1805  logging::printf("fixTransitionTimes(): END\n");
1806  }
1807  }
1808 
1809  #ifdef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1810  static void printTransitions(Transition** begin, Transition** end) {
1811  for (Transition** iter = begin; iter != end; ++iter) {
1812  (*iter)->log();
1813  logging::printf("\n");
1814  }
1815  }
1816  #endif
1817 
1823  static void expandDateTuple(
1824  const extended::DateTuple* tt,
1825  int16_t offsetMinutes,
1826  int16_t deltaMinutes,
1827  extended::DateTuple* ttw,
1828  extended::DateTuple* tts,
1829  extended::DateTuple* ttu) {
1830 
1831  if (tt->suffix == internal::ZoneContext::kSuffixS) {
1832  *tts = *tt;
1833  *ttu = {tt->yearTiny, tt->month, tt->day,
1834  (int16_t) (tt->minutes - offsetMinutes),
1836  *ttw = {tt->yearTiny, tt->month, tt->day,
1837  (int16_t) (tt->minutes + deltaMinutes),
1839  } else if (tt->suffix == internal::ZoneContext::kSuffixU) {
1840  *ttu = *tt;
1841  *tts = {tt->yearTiny, tt->month, tt->day,
1842  (int16_t) (tt->minutes + offsetMinutes),
1844  *ttw = {tt->yearTiny, tt->month, tt->day,
1845  (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1847  } else {
1848  // Explicit set the suffix to 'w' in case it was something else.
1849  *ttw = *tt;
1850  ttw->suffix = internal::ZoneContext::kSuffixW;
1851  *tts = {tt->yearTiny, tt->month, tt->day,
1852  (int16_t) (tt->minutes - deltaMinutes),
1854  *ttu = {tt->yearTiny, tt->month, tt->day,
1855  (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1857  }
1858 
1859  extended::normalizeDateTuple(ttw);
1860  extended::normalizeDateTuple(tts);
1861  extended::normalizeDateTuple(ttu);
1862  }
1863 
1868  static void selectActiveTransitions(Transition** begin, Transition** end) {
1869  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1870  logging::printf("selectActiveTransitions(): #candidates: %d\n",
1871  (int) (end - begin));
1872  }
1873 
1874  Transition* prior = nullptr;
1875  for (Transition** iter = begin; iter != end; ++iter) {
1876  Transition* transition = *iter;
1877  processTransitionMatchStatus(transition, &prior);
1878  }
1879 
1880  // If the latest prior transition is found, shift it to start at the
1881  // startDateTime of the current match.
1882  if (prior) {
1883  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1884  logging::printf(
1885  "selectActiveTransitions(): found latest prior\n");
1886  }
1887  #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1888  prior->originalTransitionTime = prior->transitionTime;
1889  #endif
1890  prior->transitionTime = prior->match->startDateTime;
1891  }
1892  }
1893 
1900  static void processTransitionMatchStatus(
1901  Transition* transition,
1902  Transition** prior) {
1903  using extended::MatchStatus;
1904 
1905  MatchStatus status = compareTransitionToMatch(
1906  transition, transition->match);
1907  transition->matchStatus = status;
1908 
1909  if (status == MatchStatus::kExactMatch) {
1910  if (*prior) {
1911  (*prior)->matchStatus = MatchStatus::kFarPast;
1912  }
1913  (*prior) = transition;
1914  } else if (status == MatchStatus::kPrior) {
1915  if (*prior) {
1916  if ((*prior)->transitionTimeU <= transition->transitionTimeU) {
1917  (*prior)->matchStatus = MatchStatus::kFarPast;
1918  (*prior) = transition;
1919  } else {
1920  transition->matchStatus = MatchStatus::kFarPast;
1921  }
1922  } else {
1923  (*prior) = transition;
1924  }
1925  }
1926  }
1927 
1936  static extended::MatchStatus compareTransitionToMatch(
1937  const Transition* transition,
1938  const MatchingEra* match) {
1939 
1940  // Find the previous Match offsets.
1941  int16_t prevMatchOffsetMinutes;
1942  int16_t prevMatchDeltaMinutes;
1943  if (match->prevMatch) {
1944  prevMatchOffsetMinutes = match->prevMatch->lastOffsetMinutes;
1945  prevMatchDeltaMinutes = match->prevMatch->lastDeltaMinutes;
1946  } else {
1947  prevMatchOffsetMinutes = match->era.offsetMinutes();
1948  prevMatchDeltaMinutes = 0;
1949  }
1950 
1951  // Expand start times.
1952  extended::DateTuple stw;
1953  extended::DateTuple sts;
1954  extended::DateTuple stu;
1955  expandDateTuple(
1956  &match->startDateTime,
1957  prevMatchOffsetMinutes,
1958  prevMatchDeltaMinutes,
1959  &stw,
1960  &sts,
1961  &stu);
1962 
1963  // Transition times.
1964  const extended::DateTuple& ttw = transition->transitionTime;
1965  const extended::DateTuple& tts = transition->transitionTimeS;
1966  const extended::DateTuple& ttu = transition->transitionTimeU;
1967 
1968  // Compare Transition to Match, where equality is assumed if *any* of the
1969  // 'w', 's', or 'u' versions of the DateTuple are equal. This prevents
1970  // duplicate Transition instances from being created in a few cases.
1971  if (ttw == stw || tts == sts || ttu == stu) {
1972  return extended::MatchStatus::kExactMatch;
1973  }
1974 
1975  if (ttu < stu) {
1976  return extended::MatchStatus::kPrior;
1977  }
1978 
1979  // Now check if the transition occurs after the given match. The
1980  // untilDateTime of the current match uses the same UTC offsets as the
1981  // transitionTime of the current transition, so no complicated adjustments
1982  // are needed. We just make sure we compare 'w' with 'w', 's' with 's',
1983  // and 'u' with 'u'.
1984  const extended::DateTuple& matchUntil = match->untilDateTime;
1985  const extended::DateTuple* transitionTime;
1986  if (matchUntil.suffix == internal::ZoneContext::kSuffixS) {
1987  transitionTime = &tts;
1988  } else if (matchUntil.suffix == internal::ZoneContext::kSuffixU) {
1989  transitionTime = &ttu;
1990  } else { // assume 'w'
1991  transitionTime = &ttw;
1992  }
1993  if (*transitionTime < matchUntil) {
1994  return extended::MatchStatus::kWithinMatch;
1995  }
1996  return extended::MatchStatus::kFarFuture;
1997  }
1998 
2004  static void generateStartUntilTimes(Transition** begin, Transition** end) {
2005  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2006  logging::printf(
2007  "generateStartUntilTimes(): #transitions=%d\n",
2008  (int) (end - begin));
2009  }
2010 
2011  Transition* prev = *begin;
2012  bool isAfterFirst = false;
2013 
2014  for (Transition** iter = begin; iter != end; ++iter) {
2015  Transition* const t = *iter;
2016 
2017  // 1) Update the untilDateTime of the previous Transition
2018  const extended::DateTuple& tt = t->transitionTime;
2019  if (isAfterFirst) {
2020  prev->untilDateTime = tt;
2021  }
2022 
2023  // 2) Calculate the current startDateTime by shifting the
2024  // transitionTime (represented in the UTC offset of the previous
2025  // transition) into the UTC offset of the *current* transition.
2026  int16_t minutes = tt.minutes + (
2027  - prev->offsetMinutes - prev->deltaMinutes
2028  + t->offsetMinutes + t->deltaMinutes);
2029  t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
2030  tt.suffix};
2031  extended::normalizeDateTuple(&t->startDateTime);
2032 
2033  // 3) The epochSecond of the 'transitionTime' is determined by the
2034  // UTC offset of the *previous* Transition. However, the
2035  // transitionTime can be represented by an illegal time (e.g. 24:00).
2036  // So, it is better to use the properly normalized startDateTime
2037  // (calculated above) with the *current* UTC offset.
2038  //
2039  // NOTE: We should also be able to calculate this directly from
2040  // 'transitionTimeU' which should still be a valid field, because it
2041  // hasn't been clobbered by 'untilDateTime' yet. Not sure if this saves
2042  // any CPU time though, since we still need to mutiply by 900.
2043  const extended::DateTuple& st = t->startDateTime;
2044  const acetime_t offsetSeconds = (acetime_t) 60
2045  * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
2046  LocalDate ld = LocalDate::forTinyComponents(
2047  st.yearTiny, st.month, st.day);
2048  t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
2049 
2050  prev = t;
2051  isAfterFirst = true;
2052  }
2053 
2054  // The last Transition's until time is the until time of the MatchingEra.
2055  extended::DateTuple untilTimeW;
2056  extended::DateTuple untilTimeS;
2057  extended::DateTuple untilTimeU;
2058  expandDateTuple(
2059  &prev->match->untilDateTime,
2060  prev->offsetMinutes,
2061  prev->deltaMinutes,
2062  &untilTimeW,
2063  &untilTimeS,
2064  &untilTimeU);
2065  prev->untilDateTime = untilTimeW;
2066  }
2067 
2071  static void calcAbbreviations(Transition** begin, Transition** end) {
2072  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2073  logging::printf("calcAbbreviations(): #transitions: %d\n",
2074  (int) (end - begin));
2075  }
2076  for (Transition** iter = begin; iter != end; ++iter) {
2077  Transition* const t = *iter;
2078  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2079  logging::printf(
2080  "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
2081  t->format(), t->deltaMinutes, t->letter());
2082  }
2083  createAbbreviation(
2084  t->abbrev,
2085  internal::kAbbrevSize,
2086  t->format(),
2087  t->deltaMinutes,
2088  t->letter());
2089  }
2090  }
2091 
2100  static void createAbbreviation(
2101  char* dest,
2102  uint8_t destSize,
2103  const char* format,
2104  uint16_t deltaMinutes,
2105  const char* letterString) {
2106 
2107  // Check if FORMAT contains a '%'.
2108  if (strchr(format, '%') != nullptr) {
2109  // Check if RULES column empty, therefore no 'letter'
2110  if (letterString == nullptr) {
2111  strncpy(dest, format, destSize - 1);
2112  dest[destSize - 1] = '\0';
2113  } else {
2114  ace_common::copyReplaceString(
2115  dest, destSize, format, '%', letterString);
2116  }
2117  } else {
2118  // Check if FORMAT contains a '/'.
2119  const char* slashPos = strchr(format, '/');
2120  if (slashPos != nullptr) {
2121  if (deltaMinutes == 0) {
2122  uint8_t headLength = (slashPos - format);
2123  if (headLength >= destSize) headLength = destSize - 1;
2124  memcpy(dest, format, headLength);
2125  dest[headLength] = '\0';
2126  } else {
2127  uint8_t tailLength = strlen(slashPos+1);
2128  if (tailLength >= destSize) tailLength = destSize - 1;
2129  memcpy(dest, slashPos+1, tailLength);
2130  dest[tailLength] = '\0';
2131  }
2132  } else {
2133  // Just copy the FORMAT disregarding deltaMinutes and letterString.
2134  strncpy(dest, format, destSize);
2135  dest[destSize - 1] = '\0';
2136  }
2137  }
2138  }
2139 
2140  const BF* mBrokerFactory; // nullable
2141  ZIB mZoneInfoBroker;
2142 
2143  mutable int16_t mYear = 0; // maybe create LocalDate::kInvalidYear?
2144  mutable bool mIsFilled = false;
2145  // NOTE: Maybe move mNumMatches and mMatches into a MatchStorage object.
2146  mutable uint8_t mNumMatches = 0; // actual number of matches
2147  mutable MatchingEra mMatches[kMaxMatches];
2148  mutable TransitionStorage mTransitionStorage;
2149 };
2150 
2151 
2157  extended::BrokerFactory,
2158  extended::ZoneInfoBroker,
2159  extended::ZoneEraBroker,
2160  extended::ZonePolicyBroker,
2161  extended::ZoneRuleBroker> {
2162 
2163  public:
2165  static const uint8_t kTypeExtended = 4;
2166 
2167  explicit ExtendedZoneProcessor(const extended::ZoneInfo* zoneInfo = nullptr)
2169  extended::BrokerFactory,
2170  extended::ZoneInfoBroker,
2171  extended::ZoneEraBroker,
2172  extended::ZonePolicyBroker,
2173  extended::ZoneRuleBroker>(
2174  kTypeExtended, &mBrokerFactory, (uintptr_t) zoneInfo)
2175  {}
2176 
2177  private:
2178  extended::BrokerFactory mBrokerFactory;
2179 };
2180 
2181 } // namespace ace_time
2182 
2183 #endif
ace_time::ExtendedZoneProcessorTemplate::printShortNameTo
void printShortNameTo(Print &printer, bool followLink=false) const override
Print a short human-readable identifier (e.g.
Definition: ExtendedZoneProcessor.h:1107
ace_time::extended::TransitionTemplate::match
const MatchingEraTemplate< ZEB > * match
The match which generated this Transition.
Definition: ExtendedZoneProcessor.h:274
ace_time::extended::TransitionTemplate::untilDateTime
DateTuple untilDateTime
Until time expressed using the UTC offset of the current Transition.
Definition: ExtendedZoneProcessor.h:318
ace_time::ExtendedZoneProcessorTemplate::initForYear
bool initForYear(int16_t year) const
Initialize the zone rules cache, keyed by the "current" year.
Definition: ExtendedZoneProcessor.h:1178
ace_time::extended::TransitionStorageTemplate::findTransitionForSeconds
MatchingTransition findTransitionForSeconds(acetime_t epochSeconds) const
Return the Transition matching the given epochSeconds.
Definition: ExtendedZoneProcessor.h:715
ace_time::extended::TransitionStorageTemplate::init
void init()
Initialize all pools to 0 size, usually when a new year is initialized.
Definition: ExtendedZoneProcessor.h:536
ace_time::extended::TransitionStorageTemplate::getPrior
Transition * getPrior()
Return the current prior transition.
Definition: ExtendedZoneProcessor.h:546
ace_time::extended::TransitionStorageTemplate::reservePrior
Transition ** reservePrior()
Allocate a free Transition then add it to the Prior pool.
Definition: ExtendedZoneProcessor.h:620
ace_time::extended::TransitionTemplate::isValidPrior
bool isValidPrior
During findCandidateTransitions(), this flag indicates whether the current transition is a valid "pri...
Definition: ExtendedZoneProcessor.h:357
ace_time::extended::TransitionTemplate::letterBuf
char letterBuf[2]
Storage for the single letter 'letter' field if 'rule' is not null.
Definition: ExtendedZoneProcessor.h:348
ace_time::LocalDateTime::hour
uint8_t hour() const
Return the hour.
Definition: LocalDateTime.h:223
ace_time::LocalDateTime
Class that holds the date-time as the components (year, month, day, hour, minute, second) without reg...
Definition: LocalDateTime.h:31
ace_time::TimeOffset::forError
static TimeOffset forError()
Return an error indicator.
Definition: TimeOffset.h:105
ace_time::extended::TransitionTemplate::rule
ZRB rule
The Zone transition rule that matched for the the given year.
Definition: ExtendedZoneProcessor.h:281
ace_time::extended::TransitionStorageTemplate::addPriorToCandidatePool
void addPriorToCandidatePool()
Add the current prior into the Candidates pool.
Definition: ExtendedZoneProcessor.h:645
ace_time::extended::TransitionTemplate::offsetMinutes
int16_t offsetMinutes
The base offset minutes, not the total effective UTC offset.
Definition: ExtendedZoneProcessor.h:339
ace_time::extended::TransitionStorageTemplate::getFreeAgent
Transition * getFreeAgent()
Return a pointer to the first Transition in the free pool.
Definition: ExtendedZoneProcessor.h:582
ace_time::extended::TransitionStorageTemplate::TransitionStorageTemplate
TransitionStorageTemplate()
Constructor.
Definition: ExtendedZoneProcessor.h:528
ace_time::extended::TransitionTemplate::letter
const char * letter() const
Return the letter string.
Definition: ExtendedZoneProcessor.h:377
ace_time::TimeOffset
A thin wrapper that represents a time offset from a reference point, usually 00:00 at UTC,...
Definition: TimeOffset.h:56
ace_time::ExtendedZoneProcessorTemplate::isLink
bool isLink() const override
Return true if timezone is a Link entry pointing to a Zone entry.
Definition: ExtendedZoneProcessor.h:954
ace_time::ExtendedZoneProcessorTemplate::TransitionResult
extended::TransitionResultTemplate< ZEB, ZPB, ZRB > TransitionResult
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:945
ace_time::extended::MatchingEraTemplate::lastDeltaMinutes
uint16_t lastDeltaMinutes
The DST offset of the last Transition in this MatchingEra.
Definition: ExtendedZoneProcessor.h:198
ace_time::ExtendedZoneProcessorTemplate::MatchingTransition
extended::MatchingTransitionTemplate< ZEB, ZPB, ZRB > MatchingTransition
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:941
ace_time::extended::MatchingTransitionTemplate
Tuple of a matching Transition and its 'fold'.
Definition: ExtendedZoneProcessor.h:452
ace_time::internal::ZoneContext::kSuffixS
static const uint8_t kSuffixS
Represents 's' or standard time.
Definition: ZoneContext.h:21
ace_time::LocalDate
The date (year, month, day) representing the date without regards to time zone.
Definition: LocalDate.h:36
ace_time::ExtendedZoneProcessor::kTypeExtended
static const uint8_t kTypeExtended
Unique TimeZone type identifier for ExtendedZoneProcessor.
Definition: ExtendedZoneProcessor.h:2165
ace_time::extended::TransitionTemplate::transitionTimeU
DateTuple transitionTimeU
Version of transitionTime in 'u' mode, using the UTC offset of the previous transition.
Definition: ExtendedZoneProcessor.h:312
ace_time::ExtendedZoneProcessorTemplate::getTransitionAllocSize
uint8_t getTransitionAllocSize() const
Get the largest allocation size of TransitionStorage.
Definition: ExtendedZoneProcessor.h:1134
ace_time::LocalDate::forEpochSeconds
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since AceTime epoch of 2000-01-01.
Definition: LocalDate.h:175
ace_time::ExtendedZoneProcessor
A specific implementation of ExtendedZoneProcessorTemplate that uses ZoneXxxBrokers which read from z...
Definition: ExtendedZoneProcessor.h:2156
ace_time::extended::TransitionStorageTemplate::resetCandidatePool
void resetCandidatePool()
Empty the Candidate pool by resetting the various indexes.
Definition: ExtendedZoneProcessor.h:558
ace_time::ExtendedZoneProcessorTemplate::TransitionStorage
extended::TransitionStorageTemplate< kMaxTransitions, ZEB, ZPB, ZRB > TransitionStorage
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:952
ace_time::extended::MatchingEraTemplate::lastOffsetMinutes
uint16_t lastOffsetMinutes
The STD offset of the last Transition in this MatchingEra.
Definition: ExtendedZoneProcessor.h:195
ace_time::ExtendedZoneProcessorTemplate::getAbbrev
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
Definition: ExtendedZoneProcessor.h:986
ace_time::extended::TransitionStorageTemplate::MatchingTransition
MatchingTransitionTemplate< ZEB, ZPB, ZRB > MatchingTransition
Template instantiation of MatchingTransitiontemplate used by this class.
Definition: ExtendedZoneProcessor.h:518
ace_time::ExtendedZoneProcessorTemplate::MatchingEra
extended::MatchingEraTemplate< ZEB > MatchingEra
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:948
ace_time::LocalDateTime::month
uint8_t month() const
Return the month with January=1, December=12.
Definition: LocalDateTime.h:211
ace_time::OffsetDateTime
The date (year, month, day), time (hour, minute, second) and offset from UTC (timeOffset).
Definition: OffsetDateTime.h:33
ace_time::ExtendedZoneProcessorTemplate::ExtendedZoneProcessorTemplate
ExtendedZoneProcessorTemplate(uint8_t type, const BF *brokerFactory, uintptr_t zoneKey)
Constructor.
Definition: ExtendedZoneProcessor.h:1259
compat.h
ace_time::ZoneProcessor
Base interface for ZoneProcessor classes.
Definition: ZoneProcessor.h:41
ace_time::LocalDate::kInvalidYearTiny
static const int8_t kInvalidYearTiny
Sentinel yearTiny which indicates an error condition or sometimes a year that 'does not exist'.
Definition: LocalDate.h:51
ace_time::ExtendedZoneProcessorTemplate::getUtcOffset
TimeOffset getUtcOffset(acetime_t epochSeconds) const override
Return the total UTC offset at epochSeconds, including DST offset.
Definition: ExtendedZoneProcessor.h:963
ace_time::OffsetDateTime::forError
static OffsetDateTime forError()
Factory method that returns an instance whose isError() is true.
Definition: OffsetDateTime.h:179
ace_time::extended::TransitionTemplate::matchStatus
MatchStatus matchStatus
During processTransitionMatchStatus(), this flag indicates how the transition falls within the time i...
Definition: ExtendedZoneProcessor.h:363
ace_time::extended::TransitionTemplate::abbrev
char abbrev[internal::kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
Definition: ExtendedZoneProcessor.h:345
ace_time::extended::TransitionTemplate::startDateTime
DateTuple startDateTime
Start time expressed using the UTC offset of the current Transition.
Definition: ExtendedZoneProcessor.h:303
ace_time::extended::DateTuple
A tuple that represents a date and time.
Definition: ExtendedZoneProcessor.h:68
ace_time::ExtendedZoneProcessorTemplate::kMaxTransitions
static const uint8_t kMaxTransitions
Max number of Transitions required for all Zones supported by this class.
Definition: ExtendedZoneProcessor.h:934
ace_time::extended::MatchingEraTemplate::prevMatch
MatchingEraTemplate * prevMatch
The previous MatchingEra, needed to interpret startDateTime.
Definition: ExtendedZoneProcessor.h:192
ace_time::extended::TransitionStorageTemplate::findTransitionForDateTime
TransitionResult findTransitionForDateTime(const LocalDateTime &ldt) const
Return the candidate Transitions matching the given dateTime.
Definition: ExtendedZoneProcessor.h:759
ace_time::ExtendedZoneProcessorTemplate::getOffsetDateTime
OffsetDateTime getOffsetDateTime(const LocalDateTime &ldt) const override
Definition: ExtendedZoneProcessor.h:1014
ace_time::LocalDateTime::minute
uint8_t minute() const
Return the minute.
Definition: LocalDateTime.h:229
ace_time::extended::BrokerFactory
A factory that creates a basic::ZoneInfoBroker.
Definition: ExtendedBrokers.h:501
ace_time::TimeOffset::forMinutes
static TimeOffset forMinutes(int16_t minutes)
Create TimeOffset from minutes from 00:00.
Definition: TimeOffset.h:83
ace_time::extended::TransitionStorageTemplate
A heap manager which is specialized and tuned to manage a collection of Transitions,...
Definition: ExtendedZoneProcessor.h:505
ace_time::LocalDate::forTinyComponents
static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month, uint8_t day)
Factory method using components with an int8_t yearTiny.
Definition: LocalDate.h:131
ace_time::extended::TransitionStorageTemplate::TransitionResult
TransitionResultTemplate< ZEB, ZPB, ZRB > TransitionResult
Template instantiation of TransitionResultTemplate used by this class.
Definition: ExtendedZoneProcessor.h:525
ace_time::ExtendedZoneProcessorTemplate::setBrokerFactory
void setBrokerFactory(const BF *brokerFactory)
Set the broker factory at runtime.
Definition: ExtendedZoneProcessor.h:1159
ace_time::extended::TransitionStorageTemplate::addActiveCandidatesToActivePool
Transition * addActiveCandidatesToActivePool()
Add active candidates into the Active pool, and collapse the Candidate pool.
Definition: ExtendedZoneProcessor.h:681
ace_time::ExtendedZoneProcessorTemplate::initForEpochSeconds
bool initForEpochSeconds(acetime_t epochSeconds) const
Initialize using the epochSeconds.
Definition: ExtendedZoneProcessor.h:1168
ace_time::LocalDate::year
int16_t year() const
Return the full year instead of just the last 2 digits.
Definition: LocalDate.h:269
ace_time::extended::TransitionStorageTemplate::resetAllocSize
void resetAllocSize()
Reset the current allocation size.
Definition: ExtendedZoneProcessor.h:853
ace_time::LocalDate::kEpochYear
static const int16_t kEpochYear
Base year of epoch.
Definition: LocalDate.h:39
ace_time::LocalDateTime::year
int16_t year() const
Return the year.
Definition: LocalDateTime.h:191
ace_time::extended::TransitionTemplate::deltaMinutes
int16_t deltaMinutes
The DST delta minutes.
Definition: ExtendedZoneProcessor.h:342
ace_time::extended::TransitionTemplate::transitionTimeS
DateTuple transitionTimeS
Version of transitionTime in 's' mode, using the UTC offset of the previous Transition.
Definition: ExtendedZoneProcessor.h:297
ace_time::extended::TransitionStorageTemplate::setFreeAgentAsPriorIfValid
void setFreeAgentAsPriorIfValid()
Set the free agent transition as the most recent prior.
Definition: ExtendedZoneProcessor.h:629
ace_time::extended::MatchingEraTemplate::untilDateTime
DateTuple untilDateTime
The effective until time of the matching ZoneEra.
Definition: ExtendedZoneProcessor.h:186
ace_time::extended::MatchingEraTemplate
Data structure that captures the matching ZoneEra and its ZoneRule transitions for a given year.
Definition: ExtendedZoneProcessor.h:178
ace_time::LocalDateTime::yearTiny
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
Definition: LocalDateTime.h:201
ace_time::extended::MatchingEraTemplate::startDateTime
DateTuple startDateTime
The effective start time of the matching ZoneEra, which uses the UTC offsets of the previous matching...
Definition: ExtendedZoneProcessor.h:183
ace_time::extended::TransitionResultTemplate
The result of the findTransitionForDateTime(const LocalDatetime&) method which can return 2 possible ...
Definition: ExtendedZoneProcessor.h:463
ExtendedBrokers.h
ace_time::ExtendedZoneProcessorTemplate::getZoneId
uint32_t getZoneId(bool followLink=false) const override
Return the unique stable zoneId.
Definition: ExtendedZoneProcessor.h:956
ace_time::internal::ZoneContext::kSuffixU
static const uint8_t kSuffixU
Represents 'u' or UTC time.
Definition: ZoneContext.h:24
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:1138
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:1149
ace_time::ExtendedZoneProcessorTemplate::printNameTo
void printNameTo(Print &printer, bool followLink=false) const override
Print a human-readable identifier (e.g.
Definition: ExtendedZoneProcessor.h:1100
ace_time::extended::TransitionStorageTemplate::Transition
TransitionTemplate< ZEB, ZPB, ZRB > Transition
Template instantiation of TransitionTemplate used by this class.
Definition: ExtendedZoneProcessor.h:511
ace_time::extended::TransitionTemplate::startEpochSeconds
acetime_t startEpochSeconds
The calculated transition time of the given rule.
Definition: ExtendedZoneProcessor.h:330
ace_time::ExtendedZoneProcessorTemplate::log
void log() const
Used only for debugging.
Definition: ExtendedZoneProcessor.h:1116
ace_time::extended::TransitionStorageTemplate::addFreeAgentToActivePool
void addFreeAgentToActivePool()
Immediately add the free agent Transition at index mIndexFree to the Active pool.
Definition: ExtendedZoneProcessor.h:605
ace_time::LocalDateTime::day
uint8_t day() const
Return the day of the month.
Definition: LocalDateTime.h:217
ace_time::extended::TransitionTemplate
Represents an interval of time where the time zone obeyed a certain UTC offset and DST delta.
Definition: ExtendedZoneProcessor.h:271
ace_time::ExtendedZoneProcessorTemplate::getOffsetDateTime
OffsetDateTime getOffsetDateTime(acetime_t epochSeconds) const override
Definition: ExtendedZoneProcessor.h:1085
ace_time::OffsetDateTime::forLocalDateTimeAndOffset
static OffsetDateTime forLocalDateTimeAndOffset(const LocalDateTime &localDateTime, TimeOffset timeOffset)
Factory method from LocalDateTime and TimeOffset.
Definition: OffsetDateTime.h:37
ace_time::extended::YearMonthTuple
A simple tuple to represent a year/month pair.
Definition: ExtendedZoneProcessor.h:166
ace_time::ExtendedZoneProcessorTemplate::getDeltaOffset
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
Definition: ExtendedZoneProcessor.h:975
ace_time::extended::DateTuple::log
void log() const
Used only for debugging.
Definition: ExtendedZoneProcessor.h:81
ace_time::extended::MatchingEraTemplate::era
ZEB era
The ZoneEra that matched the given year.
Definition: ExtendedZoneProcessor.h:189
ace_time::extended::TransitionTemplate::transitionTime
DateTuple transitionTime
The original transition time, usually 'w' but sometimes 's' or 'u'.
Definition: ExtendedZoneProcessor.h:289
ace_time::internal::ZoneContext::kSuffixW
static const uint8_t kSuffixW
Represents 'w' or wall time.
Definition: ZoneContext.h:18
ace_time::extended::TransitionStorageTemplate::getAllocSize
uint8_t getAllocSize() const
Return the maximum number of transitions which was allocated.
Definition: ExtendedZoneProcessor.h:860
ace_time::LocalDateTime::fold
uint8_t fold() const
Return the fold.
Definition: LocalDateTime.h:241
ace_time::ExtendedZoneProcessorTemplate::Transition
extended::TransitionTemplate< ZEB, ZPB, ZRB > Transition
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:937
ace_time::extended::TransitionStorageTemplate::addFreeAgentToCandidatePool
void addFreeAgentToCandidatePool()
Add the free agent Transition at index mIndexFree to the Candidate pool, sorted by transitionTime.
Definition: ExtendedZoneProcessor.h:655
ace_time::extended::TransitionTemplate::log
void log() const
Used only for debugging.
Definition: ExtendedZoneProcessor.h:407
ace_time::ExtendedZoneProcessorTemplate
An implementation of ZoneProcessor that supports for all zones defined by the TZ Database.
Definition: ExtendedZoneProcessor.h:922
ace_time::extended::TransitionTemplate::logHourMinute
static void logHourMinute(int16_t minutes)
Print minutes as [+/-]hh:mm.
Definition: ExtendedZoneProcessor.h:432
ace_time::LocalDate::toEpochDays
int32_t toEpochDays() const
Return number of days since AceTime epoch (2000-01-01 00:00:00Z).
Definition: LocalDate.h:338
ace_time::ExtendedZoneProcessorTemplate::resetTransitionAllocSize
void resetTransitionAllocSize()
Reset the TransitionStorage high water mark.
Definition: ExtendedZoneProcessor.h:1129
ace_time::extended::TransitionStorageTemplate::log
void log() const
Verify that the indexes are valid.
Definition: ExtendedZoneProcessor.h:820
ace_time::OffsetDateTime::forEpochSeconds
static OffsetDateTime forEpochSeconds(acetime_t epochSeconds, TimeOffset timeOffset, uint8_t fold=0)
Factory method.
Definition: OffsetDateTime.h:75