AceTime  1.8.0
Date and time classes for Arduino that support timezones from the TZ Database, and a system clock that can synchronize from an NTP server or an RTC chip.
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 
66 struct DateTuple {
67  DateTuple() = default;
68 
69  DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
70  yearTiny(y), month(mon), day(d), suffix(mod), minutes(min) {}
71 
72  int8_t yearTiny; // [-127, 126], 127 will cause bugs
73  uint8_t month; // [1-12]
74  uint8_t day; // [1-31]
75  uint8_t suffix; // kSuffixS, kSuffixW, kSuffixU
76  int16_t minutes; // negative values allowed
77 
79  void log() const {
80  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
81  int hour = minutes / 60;
82  int minute = minutes - hour * 60;
83  char c = "wsu"[(suffix>>4)];
84  logging::printf("%04d-%02u-%02uT%02d:%02d%c",
85  yearTiny+LocalDate::kEpochYear, month, day, hour, minute, c);
86  }
87  }
88 };
89 
91 inline bool operator<(const DateTuple& a, const DateTuple& b) {
92  if (a.yearTiny < b.yearTiny) return true;
93  if (a.yearTiny > b.yearTiny) return false;
94  if (a.month < b.month) return true;
95  if (a.month > b.month) return false;
96  if (a.day < b.day) return true;
97  if (a.day > b.day) return false;
98  if (a.minutes < b.minutes) return true;
99  if (a.minutes > b.minutes) return false;
100  return false;
101 }
102 
103 inline bool operator>=(const DateTuple& a, const DateTuple& b) {
104  return ! (a < b);
105 }
106 
107 inline bool operator<=(const DateTuple& a, const DateTuple& b) {
108  return ! (b < a);
109 }
110 
111 inline bool operator>(const DateTuple& a, const DateTuple& b) {
112  return (b < a);
113 }
114 
116 inline bool operator==(const DateTuple& a, const DateTuple& b) {
117  return a.yearTiny == b.yearTiny
118  && a.month == b.month
119  && a.day == b.day
120  && a.minutes == b.minutes
121  && a.suffix == b.suffix;
122 }
123 
126  int8_t yearTiny;
127  uint8_t month;
128 };
129 
136 template<typename ZEB>
143 
146 
148  ZEB era;
149 
152 
155 
158 
159  void log() const {
160  logging::printf("MatchingEra(");
161  logging::printf("start="); startDateTime.log();
162  logging::printf("; until="); untilDateTime.log();
163  logging::printf("; era=%c", (era.isNull()) ? '-' : '*');
164  logging::printf("; prevMatch=%c", (prevMatch) ? '*' : '-');
165  logging::printf(")");
166  }
167 };
168 
170 template <typename T>
171 void swap(T& a, T& b) {
172  T tmp = a;
173  a = b;
174  b = tmp;
175 }
176 
181 enum class MatchStatus : uint8_t {
182  kFarPast, // 0
183  kPrior, // 1
184  kExactMatch, // 2
185  kWithinMatch, // 3
186  kFarFuture, // 4
187 };
188 
189 inline bool isMatchStatusActive(MatchStatus status) {
190  return status == MatchStatus::kExactMatch
191  || status == MatchStatus::kWithinMatch
192  || status == MatchStatus::kPrior;
193 }
194 
224 template <typename ZEB, typename ZPB, typename ZRB>
228 
234  ZRB rule;
235 
243 
244  union {
251 
257  };
258 
259  union {
266 
272  };
273 
274 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
275 
279  DateTuple originalTransitionTime;
280 #endif
281 
283  acetime_t startEpochSeconds;
284 
292  int16_t offsetMinutes;
293 
295  int16_t deltaMinutes;
296 
298  char abbrev[internal::kAbbrevSize];
299 
301  char letterBuf[2];
302 
303  union {
311 
316  MatchStatus matchStatus;
317  };
318 
319  //-------------------------------------------------------------------------
320 
321  const char* format() const {
322  return match->era.format();
323  }
324 
330  const char* letter() const {
331  // RULES column is '-' or hh:mm, so return nullptr to indicate this.
332  if (rule.isNull()) {
333  return nullptr;
334  }
335 
336  // RULES point to a named rule, and LETTER is a single, printable
337  // character.
338  char letter = rule.letter();
339  if (letter >= 32) {
340  return letterBuf;
341  }
342 
343  // RULES points to a named rule, and the LETTER is a string. The
344  // rule->letter is a non-printable number < 32, which is an index into
345  // a list of strings given by match->era->zonePolicy->letters[].
346  const ZPB policy = match->era.zonePolicy();
347  uint8_t numLetters = policy.numLetters();
348  if (letter >= numLetters) {
349  // This should never happen unless there is a programming error. If it
350  // does, return an empty string. (createTransitionForYear() sets
351  // letterBuf to a NUL terminated empty string if rule->letter < 32)
352  return letterBuf;
353  }
354 
355  // Return the string at index 'rule->letter'.
356  return policy.letter(letter);
357  }
358 
360  void log() const {
361  logging::printf("Transition(");
362  if (sizeof(acetime_t) <= sizeof(int)) {
363  logging::printf("start=%d", startEpochSeconds);
364  } else {
365  logging::printf("start=%ld", startEpochSeconds);
366  }
367  logging::printf("; status=%d", matchStatus);
368  logging::printf("; UTC");
371  logging::printf("; tt="); transitionTime.log();
372  logging::printf("; tts="); transitionTimeS.log();
373  logging::printf("; ttu="); transitionTimeU.log();
374  if (rule.isNull()) {
375  logging::printf("; rule=-");
376  } else {
377  logging::printf("; rule=");
378  logging::printf("[%d,%d]",
379  rule.fromYearTiny() + LocalDate::kEpochYear,
380  rule.toYearTiny() + LocalDate::kEpochYear);
381  }
382  }
383 
385  static void logHourMinute(int16_t minutes) {
386  char sign;
387  if (minutes < 0) {
388  sign = '-';
389  minutes = -minutes;
390  } else {
391  sign = '+';
392  }
393  uint8_t hour = minutes / 60;
394  uint8_t minute = minutes - hour * 60;
395  logging::printf("%c%02u:%02u", sign, (unsigned) hour, (unsigned) minute);
396  }
397 };
398 
430 template<uint8_t SIZE, typename ZEB, typename ZPB, typename ZRB>
432  public:
438 
441 
443  void init() {
444  for (uint8_t i = 0; i < SIZE; i++) {
445  mTransitions[i] = &mPool[i];
446  }
447  mIndexPrior = 0;
448  mIndexCandidates = 0;
449  mIndexFree = 0;
450  }
451 
454  return mTransitions[mIndexPrior];
455  }
456 
466  mIndexCandidates = mIndexPrior;
467  mIndexFree = mIndexPrior;
468  }
469 
470  Transition** getCandidatePoolBegin() {
471  return &mTransitions[mIndexCandidates];
472  }
473  Transition** getCandidatePoolEnd() {
474  return &mTransitions[mIndexFree];
475  }
476 
477  Transition** getActivePoolBegin() {
478  return &mTransitions[0];
479  }
480  Transition** getActivePoolEnd() {
481  return &mTransitions[mIndexFree];
482  }
483 
490  // Set the internal high water mark. If that index becomes SIZE,
491  // then we know we have an overflow.
492  if (mIndexFree > mHighWater) {
493  mHighWater = mIndexFree;
494  }
495 
496  if (mIndexFree < SIZE) {
497  return mTransitions[mIndexFree];
498  } else {
499  return mTransitions[SIZE - 1];
500  }
501  }
502 
511  if (mIndexFree >= SIZE) return;
512  mIndexFree++;
513  mIndexPrior = mIndexFree;
514  mIndexCandidates = mIndexFree;
515  }
516 
523  getFreeAgent(); // update high water mark
524 
525  mIndexCandidates++;
526  mIndexFree++;
527  return &mTransitions[mIndexPrior];
528  }
529 
532  Transition* ft = getFreeAgent();
533  Transition* prior = getPrior();
534  if ((prior->isValidPrior && prior->transitionTime < ft->transitionTime)
535  || !prior->isValidPrior) {
536  ft->isValidPrior = true;
537  prior->isValidPrior = false;
538  swap(mTransitions[mIndexPrior], mTransitions[mIndexFree]);
539  }
540  }
541 
548  mIndexCandidates--;
549  }
550 
558  if (mIndexFree >= SIZE) return;
559 
560  // This implementation makes pair-wise swaps to shift the current
561  // Transition leftwards into its correctly sorted position. At first
562  // glance, this seem inefficient compared to the alternative
563  // implementation where we save the current Transition, then slide all the
564  // elements to the left by one position rightwards. However,
565  // MemoryBenchmark shows that this implementation is 46 bytes smaller on
566  // an AVR processor.
567  for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
568  Transition* curr = mTransitions[i];
569  Transition* prev = mTransitions[i - 1];
570  if (curr->transitionTime >= prev->transitionTime) break;
571  mTransitions[i] = prev;
572  mTransitions[i - 1] = curr;
573  }
574  mIndexFree++;
575  }
576 
584  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
585  logging::printf("addActiveCandidatesToActivePool()\n");
586  }
587 
588  // Shift active candidates to the left into the Active pool.
589  uint8_t iActive = mIndexPrior;
590  uint8_t iCandidate = mIndexCandidates;
591  for (; iCandidate < mIndexFree; iCandidate++) {
592  if (isMatchStatusActive(mTransitions[iCandidate]->matchStatus)) {
593  if (iActive != iCandidate) {
594  // Must use swap(), because we are moving pointers instead of the
595  // actual Transition objects.
596  swap(mTransitions[iActive], mTransitions[iCandidate]);
597  }
598  ++iActive;
599  }
600  }
601 
602  mIndexPrior = iActive;
603  mIndexCandidates = iActive;
604  mIndexFree = iActive;
605 
606  return mTransitions[iActive - 1];
607  }
608 
617  const Transition* findTransition(acetime_t epochSeconds) const {
618  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
619  logging::printf( "findTransition(): mIndexFree: %d\n", mIndexFree);
620  }
621 
622  const Transition* match = nullptr;
623  for (uint8_t i = 0; i < mIndexFree; i++) {
624  const Transition* candidate = mTransitions[i];
625  if (candidate->startEpochSeconds > epochSeconds) break;
626  match = candidate;
627  }
628  return match;
629  }
630 
655  const {
656  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
657  logging::printf(
658  "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
659  }
660 
661  // Convert LocalDateTime to DateTuple.
662  DateTuple localDate{
663  ldt.yearTiny(),
664  ldt.month(),
665  ldt.day(),
666  (int16_t) (ldt.hour() * 60 + ldt.minute()),
668  };
669  const Transition* match = nullptr;
670 
671  // Find the last Transition that matches
672  for (uint8_t i = 0; i < mIndexFree; i++) {
673  const Transition* candidate = mTransitions[i];
674  if (candidate->startDateTime > localDate) break;
675  match = candidate;
676  }
677  return match;
678  }
679 
681  void log() const {
682  logging::printf("TransitionStorage: ");
683  logging::printf("nActives=%d", mIndexPrior);
684  logging::printf(", nPrior=%d", mIndexCandidates - mIndexPrior);
685  logging::printf(", nCandidates=%d", mIndexFree - mIndexCandidates);
686  logging::printf(", nFree=%d", SIZE - mIndexFree);
687  logging::printf("\n");
688 
689  if (mIndexPrior != 0) {
690  logging::printf(" Actives:\n");
691  for (uint8_t i = 0; i < mIndexPrior; i++) {
692  logging::printf(" ");
693  mTransitions[i]->log();
694  logging::printf("\n");
695  }
696  }
697  if (mIndexPrior != mIndexCandidates) {
698  logging::printf(" Prior: \n");
699  logging::printf(" ");
700  mTransitions[mIndexPrior]->log();
701  logging::printf("\n");
702  }
703  if (mIndexCandidates != mIndexFree) {
704  logging::printf(" Candidates:\n");
705  for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
706  logging::printf(" ");
707  mTransitions[i]->log();
708  logging::printf("\n");
709  }
710  }
711  }
712 
714  void resetHighWater() { mHighWater = 0; }
715 
721  uint8_t getHighWater() const { return mHighWater; }
722 
723  private:
724  friend class ::TransitionStorageTest_getFreeAgent;
725  friend class ::TransitionStorageTest_getFreeAgent2;
726  friend class ::TransitionStorageTest_addFreeAgentToActivePool;
727  friend class ::TransitionStorageTest_reservePrior;
728  friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
729  friend class ::TransitionStorageTest_setFreeAgentAsPriorIfValid;
730  friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
731  friend class ::TransitionStorageTest_findTransitionForDateTime;
732  friend class ::TransitionStorageTest_resetCandidatePool;
733 
735  Transition* getTransition(uint8_t i) {
736  return mTransitions[i];
737  }
738 
739  Transition mPool[SIZE];
740  Transition* mTransitions[SIZE];
741  uint8_t mIndexPrior;
742  uint8_t mIndexCandidates;
743  uint8_t mIndexFree;
744 
746  uint8_t mHighWater = 0;
747 };
748 
749 } // namespace extended
750 
782 template <typename BF, typename ZIB, typename ZEB, typename ZPB, typename ZRB>
784  public:
793  static const uint8_t kMaxTransitions = 8;
794 
797 
800 
804 
805  uint32_t getZoneId() const override { return mZoneInfoBroker.zoneId(); }
806 
807  TimeOffset getUtcOffset(acetime_t epochSeconds) const override {
808  bool success = initForEpochSeconds(epochSeconds);
809  if (!success) return TimeOffset::forError();
810  const Transition* transition = findTransition(epochSeconds);
811  return (transition)
813  transition->offsetMinutes + transition->deltaMinutes)
815  }
816 
817  TimeOffset getDeltaOffset(acetime_t epochSeconds) const override {
818  bool success = initForEpochSeconds(epochSeconds);
819  if (!success) return TimeOffset::forError();
820  const Transition* transition = findTransition(epochSeconds);
821  return TimeOffset::forMinutes(transition->deltaMinutes);
822  }
823 
824  const char* getAbbrev(acetime_t epochSeconds) const override {
825  bool success = initForEpochSeconds(epochSeconds);
826  if (!success) return "";
827  const Transition* transition = findTransition(epochSeconds);
828  return transition->abbrev;
829  }
830 
831  OffsetDateTime getOffsetDateTime(const LocalDateTime& ldt) const override {
832  TimeOffset offset;
833  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
834  logging::printf("getOffsetDateTime(): ldt=");
835  ldt.printTo(SERIAL_PORT_MONITOR);
836  SERIAL_PORT_MONITOR.println();
837  }
838  bool success = initForYear(ldt.year());
839 
840  // Find the Transition to get the DST offset
841  if (success) {
842  const Transition* transition =
843  mTransitionStorage.findTransitionForDateTime(ldt);
844  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
845  logging::printf("getOffsetDateTime(): match transition=");
846  transition->log();
847  logging::printf("\n");
848  }
849  offset = (transition)
851  transition->offsetMinutes + transition->deltaMinutes)
853  } else {
854  offset = TimeOffset::forError();
855  }
856 
857  auto odt = OffsetDateTime::forLocalDateTimeAndOffset(ldt, offset);
858  if (offset.isError()) {
859  return odt;
860  }
861  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
862  logging::printf("getOffsetDateTime(): odt=");
863  odt.printTo(SERIAL_PORT_MONITOR);
864  SERIAL_PORT_MONITOR.println();
865  }
866 
867  // Normalize the OffsetDateTime, causing LocalDateTime in the DST
868  // transtion gap to be shifted forward one hour. For LocalDateTime in an
869  // overlap (DST->STD transition), the earlier UTC offset is selected// by
870  // findTransitionForDateTime(). Use that to calculate the epochSeconds,
871  // then recalculate the offset. Use this final offset to determine the
872  // effective OffsetDateTime that will survive a round-trip unchanged.
873  acetime_t epochSeconds = odt.toEpochSeconds();
874  const Transition* transition =
875  mTransitionStorage.findTransition(epochSeconds);
876  offset = (transition)
878  transition->offsetMinutes + transition->deltaMinutes)
880  odt = OffsetDateTime::forEpochSeconds(epochSeconds, offset);
881  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
882  logging::printf("getOffsetDateTime(): normalized(odt)=");
883  odt.printTo(SERIAL_PORT_MONITOR);
884  SERIAL_PORT_MONITOR.println();
885  }
886  return odt;
887  }
888 
889  void printNameTo(Print& printer) const override {
890  mZoneInfoBroker.printNameTo(printer);
891  }
892 
893  void printShortNameTo(Print& printer) const override {
894  mZoneInfoBroker.printShortNameTo(printer);
895  }
896 
898  void log() const {
899  logging::printf("ExtendedZoneProcessor\n");
900  logging::printf(" mYear: %d\n", mYear);
901  logging::printf(" mNumMatches: %d\n", mNumMatches);
902  for (int i = 0; i < mNumMatches; i++) {
903  logging::printf(" Match %d: ", i);
904  mMatches[i].log();
905  logging::printf("\n");
906  }
907  mTransitionStorage.log();
908  }
909 
912  mTransitionStorage.resetHighWater();
913  }
914 
916  uint8_t getTransitionHighWater() const {
917  return mTransitionStorage.getHighWater();
918  }
919 
920  void setZoneKey(uintptr_t zoneKey) override {
921  if (mZoneInfoBroker.equals(zoneKey)) return;
922 
923  mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
924  mYear = 0;
925  mIsFilled = false;
926  mNumMatches = 0;
928  }
929 
930  bool equalsZoneKey(uintptr_t zoneKey) const override {
931  return mZoneInfoBroker.equals(zoneKey);
932  }
933 
940  void setBrokerFactory(const BF* brokerFactory) {
941  mBrokerFactory = brokerFactory;
942  }
943 
949  bool initForEpochSeconds(acetime_t epochSeconds) const {
950  LocalDate ld = LocalDate::forEpochSeconds(epochSeconds);
951  return initForYear(ld.year());
952  }
953 
959  bool initForYear(int16_t year) const {
960  if (isFilled(year)) return true;
961  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
962  logging::printf("initForYear(): %d\n", year);
963  }
964 
965  mYear = year;
966  mNumMatches = 0; // clear cache
967  mTransitionStorage.init();
968 
969  if (year < mZoneInfoBroker.zoneContext()->startYear - 1
970  || mZoneInfoBroker.zoneContext()->untilYear < year) {
971  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
972  logging::printf("init(): Year %d out of valid range [%d, %d)\n",
973  year,
974  mZoneInfoBroker.zoneContext()->startYear,
975  mZoneInfoBroker.zoneContext()->untilYear);
976  }
977  return false;
978  }
979 
980  extended::YearMonthTuple startYm = {
981  (int8_t) (year - LocalDate::kEpochYear - 1), 12 };
982  extended::YearMonthTuple untilYm = {
983  (int8_t) (year - LocalDate::kEpochYear + 1), 2 };
984 
985  // Step 1 (See equivalent steps in
986  // zone_processor.ZoneSpecifier.init_for_year())
987  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
988  logging::printf("==== Step 1: findMatches()\n");
989  }
990  mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
991  kMaxMatches);
992  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
993 
994  // Step 2
995  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
996  logging::printf("==== Step 2: createTransitions()\n");
997  }
998  createTransitions(mTransitionStorage, mMatches, mNumMatches);
999  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1000 
1001  // Step 3
1002  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1003  logging::printf("==== Step 3: fixTransitionTimes()\n");
1004  }
1005  Transition** begin = mTransitionStorage.getActivePoolBegin();
1006  Transition** end = mTransitionStorage.getActivePoolEnd();
1007  fixTransitionTimes(begin, end);
1008  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1009 
1010  // Step 4
1011  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1012  logging::printf("==== Step 4: generateStartUntilTimes()\n");
1013  }
1014  generateStartUntilTimes(begin, end);
1015  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1016 
1017  // Step 5
1018  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1019  logging::printf("==== Step 5: calcAbbreviations()\n");
1020  }
1021  calcAbbreviations(begin, end);
1022  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1023 
1024  mIsFilled = true;
1025  return true;
1026  }
1027 
1028  protected:
1036  uint8_t type,
1037  const BF* brokerFactory,
1038  uintptr_t zoneKey
1039  ) :
1040  ZoneProcessor(type),
1041  mBrokerFactory(brokerFactory)
1042  {
1043  setZoneKey(zoneKey);
1044  }
1045 
1046  private:
1047  friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
1048  friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
1049  friend class ::ExtendedZoneProcessorTest_createMatch;
1050  friend class ::ExtendedZoneProcessorTest_findMatches_simple;
1051  friend class ::ExtendedZoneProcessorTest_findMatches_named;
1052  friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
1053  friend class ::ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
1054  friend class ::ExtendedZoneProcessorTest_getTransitionTime;
1055  friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
1056  friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
1057  friend class ::ExtendedZoneProcessorTest_expandDateTuple;
1058  friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
1059  friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
1060  friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
1061  friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
1062  friend class ::ExtendedZoneProcessorTest_processTransitionMatchStatus;
1063  friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
1064  friend class ::ExtendedZoneProcessorTest_createAbbreviation;
1065  friend class ::ExtendedZoneProcessorTest_setZoneKey;
1066  friend class ::TransitionStorageTest_findTransitionForDateTime;
1067  friend class ::TransitionValidation;
1068 
1069  // Disable copy constructor and assignment operator.
1071  const ExtendedZoneProcessorTemplate&) = delete;
1072  ExtendedZoneProcessorTemplate& operator=(
1073  const ExtendedZoneProcessorTemplate&) = delete;
1074 
1079  static const uint8_t kMaxMatches = 4;
1080 
1085  static const uint8_t kMaxInteriorYears = 4;
1086 
1087  bool equals(const ZoneProcessor& other) const override {
1088  return mZoneInfoBroker.equals(
1089  ((const ExtendedZoneProcessorTemplate&) other).mZoneInfoBroker);
1090  }
1091 
1096  const Transition* findTransition(acetime_t epochSeconds) const {
1097  return mTransitionStorage.findTransition(epochSeconds);
1098  }
1099 
1101  bool isFilled(int16_t year) const {
1102  return mIsFilled && (year == mYear);
1103  }
1104 
1112  static uint8_t findMatches(
1113  const ZIB& zoneInfo,
1114  const extended::YearMonthTuple& startYm,
1115  const extended::YearMonthTuple& untilYm,
1116  MatchingEra* matches,
1117  uint8_t maxMatches
1118  ) {
1119  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1120  logging::printf("findMatches()\n");
1121  }
1122  uint8_t iMatch = 0;
1123  MatchingEra* prevMatch = nullptr;
1124  for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
1125  const ZEB era = zoneInfo.era(iEra);
1126  if (eraOverlapsInterval(prevMatch, era, startYm, untilYm)) {
1127  if (iMatch < maxMatches) {
1128  matches[iMatch] = createMatch(prevMatch, era, startYm, untilYm);
1129  prevMatch = &matches[iMatch];
1130  iMatch++;
1131  }
1132  }
1133  }
1134  return iMatch;
1135  }
1136 
1148  static bool eraOverlapsInterval(
1149  const MatchingEra* prevMatch,
1150  const ZEB& era,
1151  const extended::YearMonthTuple& startYm,
1152  const extended::YearMonthTuple& untilYm) {
1153  return (prevMatch == nullptr || compareEraToYearMonth(
1154  prevMatch->era, untilYm.yearTiny, untilYm.month) < 0)
1155  && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1156  }
1157 
1159  static int8_t compareEraToYearMonth(const ZEB& era,
1160  int8_t yearTiny, uint8_t month) {
1161  if (era.untilYearTiny() < yearTiny) return -1;
1162  if (era.untilYearTiny() > yearTiny) return 1;
1163  if (era.untilMonth() < month) return -1;
1164  if (era.untilMonth() > month) return 1;
1165  if (era.untilDay() > 1) return 1;
1166  //if (era.untilTimeMinutes() < 0) return -1; // never possible
1167  if (era.untilTimeMinutes() > 0) return 1;
1168  return 0;
1169  }
1170 
1177  static MatchingEra createMatch(
1178  MatchingEra* prevMatch,
1179  const ZEB& era,
1180  const extended::YearMonthTuple& startYm,
1181  const extended::YearMonthTuple& untilYm) {
1182 
1183  // If prevMatch is null, set startDate to be earlier than all valid
1184  // ZoneEra.
1185  extended::DateTuple startDate = (prevMatch == nullptr)
1186  ? extended::DateTuple{
1188  1,
1189  1,
1190  0,
1192  }
1193  : extended::DateTuple{
1194  prevMatch->era.untilYearTiny(),
1195  prevMatch->era.untilMonth(),
1196  prevMatch->era.untilDay(),
1197  (int16_t) prevMatch->era.untilTimeMinutes(),
1198  prevMatch->era.untilTimeSuffix()
1199  };
1200  extended::DateTuple lowerBound{
1201  startYm.yearTiny,
1202  startYm.month,
1203  1,
1204  0,
1206  };
1207  if (startDate < lowerBound) {
1208  startDate = lowerBound;
1209  }
1210 
1211  extended::DateTuple untilDate{
1212  era.untilYearTiny(),
1213  era.untilMonth(),
1214  era.untilDay(),
1215  (int16_t) era.untilTimeMinutes(),
1216  era.untilTimeSuffix()
1217  };
1218  extended::DateTuple upperBound{
1219  untilYm.yearTiny,
1220  untilYm.month,
1221  1,
1222  0,
1224  };
1225  if (upperBound < untilDate) {
1226  untilDate = upperBound;
1227  }
1228 
1229  return {startDate, untilDate, era, prevMatch, 0, 0};
1230  }
1231 
1236  static void createTransitions(
1237  TransitionStorage& transitionStorage,
1238  MatchingEra* matches,
1239  uint8_t numMatches) {
1240  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1241  logging::printf("createTransitions()\n");
1242  }
1243 
1244  for (uint8_t i = 0; i < numMatches; i++) {
1245  createTransitionsForMatch(transitionStorage, &matches[i]);
1246  }
1247  }
1248 
1250  static void createTransitionsForMatch(
1251  TransitionStorage& transitionStorage,
1252  MatchingEra* match) {
1253  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1254  logging::printf("== createTransitionsForMatch()\n");
1255  }
1256  const ZPB policy = match->era.zonePolicy();
1257  if (policy.isNull()) {
1258  createTransitionsFromSimpleMatch(transitionStorage, match);
1259  } else {
1260  createTransitionsFromNamedMatch(transitionStorage, match);
1261  }
1262  }
1263 
1264  static void createTransitionsFromSimpleMatch(
1265  TransitionStorage& transitionStorage,
1266  MatchingEra* match) {
1267  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1268  logging::printf("== createTransitionsFromSimpleMatch()\n");
1269  }
1270 
1271  Transition* freeTransition = transitionStorage.getFreeAgent();
1272  createTransitionForYear(freeTransition, 0 /*not used*/,
1273  ZRB() /*rule*/, match);
1274  freeTransition->matchStatus = extended::MatchStatus::kExactMatch;
1275  match->lastOffsetMinutes = freeTransition->offsetMinutes;
1276  match->lastDeltaMinutes = freeTransition->deltaMinutes;
1277  transitionStorage.addFreeAgentToActivePool();
1278  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1279  freeTransition->log();
1280  logging::printf("\n");
1281  }
1282  }
1283 
1284  static void createTransitionsFromNamedMatch(
1285  TransitionStorage& transitionStorage,
1286  MatchingEra* match) {
1287  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1288  logging::printf("== createTransitionsFromNamedMatch()\n");
1289  }
1290 
1291  transitionStorage.resetCandidatePool();
1292  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1293  match->log(); logging::printf("\n");
1294  }
1295 
1296  // Pass 1: Find candidate transitions using whole years.
1297  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1298  logging::printf("---- Pass 1: findCandidateTransitions()\n");
1299  }
1300  findCandidateTransitions(transitionStorage, match);
1301  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1302  transitionStorage.log();
1303  }
1304 
1305  // Pass 2: Fix the transitions times, converting 's' and 'u' into 'w'
1306  // uniformly.
1307  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1308  logging::printf("---- Pass 2: fixTransitionTimes()\n");
1309  }
1310  fixTransitionTimes(
1311  transitionStorage.getCandidatePoolBegin(),
1312  transitionStorage.getCandidatePoolEnd());
1313 
1314  // Pass 3: Select only those Transitions which overlap with the actual
1315  // start and until times of the MatchingEra.
1316  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1317  logging::printf("---- Pass 3: selectActiveTransitions()\n");
1318  }
1319  selectActiveTransitions(
1320  transitionStorage.getCandidatePoolBegin(),
1321  transitionStorage.getCandidatePoolEnd());
1322  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1323  transitionStorage.log();
1324  }
1325  Transition* lastTransition =
1326  transitionStorage.addActiveCandidatesToActivePool();
1327  match->lastOffsetMinutes = lastTransition->offsetMinutes;
1328  match->lastDeltaMinutes = lastTransition->deltaMinutes;
1329  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1330  transitionStorage.log();
1331  }
1332  }
1333 
1334  static void findCandidateTransitions(
1335  TransitionStorage& transitionStorage,
1336  const MatchingEra* match) {
1337  using extended::MatchStatus;
1338 
1339  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1340  logging::printf("findCandidateTransitions(): \n");
1341  match->log();
1342  logging::printf("\n");
1343  }
1344  const ZPB policy = match->era.zonePolicy();
1345  uint8_t numRules = policy.numRules();
1346  int8_t startY = match->startDateTime.yearTiny;
1347  int8_t endY = match->untilDateTime.yearTiny;
1348 
1349  Transition** prior = transitionStorage.reservePrior();
1350  (*prior)->isValidPrior = false; // indicates "no prior transition"
1351  for (uint8_t r = 0; r < numRules; r++) {
1352  const ZRB rule = policy.rule(r);
1353 
1354  // Add Transitions for interior years
1355  int8_t interiorYears[kMaxInteriorYears];
1356  uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1357  rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1358  for (uint8_t y = 0; y < numYears; y++) {
1359  int8_t year = interiorYears[y];
1360  Transition* t = transitionStorage.getFreeAgent();
1361  createTransitionForYear(t, year, rule, match);
1362  MatchStatus status = compareTransitionToMatchFuzzy(t, match);
1363  if (status == MatchStatus::kPrior) {
1364  transitionStorage.setFreeAgentAsPriorIfValid();
1365  } else if (status == MatchStatus::kWithinMatch) {
1366  transitionStorage.addFreeAgentToCandidatePool();
1367  } else {
1368  // Must be kFarFuture.
1369  // Do nothing, allowing the free agent to be reused.
1370  }
1371  }
1372 
1373  // Add Transition for prior year
1374  int8_t priorYear = getMostRecentPriorYear(
1375  rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1376  if (priorYear != LocalDate::kInvalidYearTiny) {
1377  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1378  logging::printf(
1379  "findCandidateTransitions(): priorYear: %d\n",
1380  priorYear + LocalDate::kEpochYear);
1381  }
1382  Transition* t = transitionStorage.getFreeAgent();
1383  createTransitionForYear(t, priorYear, rule, match);
1384  transitionStorage.setFreeAgentAsPriorIfValid();
1385  }
1386  }
1387 
1388  // Add the reserved prior into the Candidate pool only if 'isValidPrior'
1389  // is true.
1390  if ((*prior)->isValidPrior) {
1391  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1392  logging::printf(
1393  "findCandidateTransitions(): adding prior to Candidate pool\n");
1394  (*prior)->log();
1395  logging::printf("\n");
1396  }
1397  transitionStorage.addPriorToCandidatePool();
1398  }
1399  }
1400 
1420  static uint8_t calcInteriorYears(
1421  int8_t* interiorYears,
1422  uint8_t maxInteriorYears,
1423  int8_t fromYear, int8_t toYear,
1424  int8_t startYear, int8_t endYear) {
1425  uint8_t i = 0;
1426  for (int8_t year = startYear; year <= endYear; year++) {
1427  if (fromYear <= year && year <= toYear) {
1428  interiorYears[i] = year;
1429  i++;
1430  if (i >= maxInteriorYears) break;
1431  }
1432  }
1433  return i;
1434  }
1435 
1442  static void createTransitionForYear(
1443  Transition* t,
1444  int8_t year,
1445  const ZRB& rule,
1446  const MatchingEra* match) {
1447  t->match = match;
1448  t->rule = rule;
1449  t->offsetMinutes = match->era.offsetMinutes();
1450  t->letterBuf[0] = '\0';
1451 
1452  if (! rule.isNull()) {
1453  t->transitionTime = getTransitionTime(year, rule);
1454  t->deltaMinutes = rule.deltaMinutes();
1455 
1456  char letter = rule.letter();
1457  if (letter >= 32) {
1458  // If LETTER is a '-', treat it the same as an empty string.
1459  if (letter != '-') {
1460  t->letterBuf[0] = letter;
1461  t->letterBuf[1] = '\0';
1462  }
1463  } else {
1464  // rule->letter is a long string, so is referenced as an offset index
1465  // into the ZonePolicy.letters array. The string cannot fit in
1466  // letterBuf, so will be retrieved by the letter() method below.
1467  }
1468  } else {
1469  // Create a Transition using the MatchingEra for the transitionTime.
1470  // Used for simple MatchingEra.
1471  t->transitionTime = match->startDateTime;
1472  t->deltaMinutes = match->era.deltaMinutes();
1473  }
1474  }
1475 
1488  static int8_t getMostRecentPriorYear(
1489  int8_t fromYear, int8_t toYear,
1490  int8_t startYear, int8_t endYear) {
1491 
1492  (void) endYear; // disable compiler warnings
1493 
1494  if (fromYear < startYear) {
1495  if (toYear < startYear) {
1496  return toYear;
1497  } else {
1498  return startYear - 1;
1499  }
1500  } else {
1502  }
1503  }
1504 
1509  static extended::DateTuple getTransitionTime(
1510  int8_t yearTiny, const ZRB& rule) {
1511 
1512  internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1513  yearTiny + LocalDate::kEpochYear,
1514  rule.inMonth(),
1515  rule.onDayOfWeek(),
1516  rule.onDayOfMonth());
1517  return {
1518  yearTiny,
1519  monthDay.month,
1520  monthDay.day,
1521  (int16_t) rule.atTimeMinutes(),
1522  rule.atTimeSuffix()
1523  };
1524  }
1525 
1536  static extended::MatchStatus compareTransitionToMatchFuzzy(
1537  const Transition* t, const MatchingEra* match) {
1538  using extended::MatchStatus;
1539 
1540  int16_t ttMonths = t->transitionTime.yearTiny * 12
1541  + t->transitionTime.month;
1542 
1543  int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1544  + match->startDateTime.month;
1545  if (ttMonths < matchStartMonths - 1) return MatchStatus::kPrior;
1546 
1547  int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1548  + match->untilDateTime.month;
1549  if (matchUntilMonths + 2 <= ttMonths) return MatchStatus::kFarFuture;
1550 
1551  return MatchStatus::kWithinMatch;
1552  }
1553 
1562  static void fixTransitionTimes(Transition** begin, Transition** end) {
1563  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1564  logging::printf("fixTransitionTimes(): START; #transitions=%d\n",
1565  (int) (end - begin));
1566  printTransitions(begin, end);
1567  }
1568 
1569  // extend first Transition to -infinity
1570  Transition* prev = *begin;
1571 
1572  for (Transition** iter = begin; iter != end; ++iter) {
1573  Transition* curr = *iter;
1574  expandDateTuple(
1575  &curr->transitionTime,
1576  prev->offsetMinutes,
1577  prev->deltaMinutes,
1578  &curr->transitionTime,
1579  &curr->transitionTimeS,
1580  &curr->transitionTimeU);
1581  prev = curr;
1582  }
1583  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1584  logging::printf("fixTransitionTimes(): FIXED\n");
1585  printTransitions(begin, end);
1586  logging::printf("fixTransitionTimes(): END\n");
1587  }
1588  }
1589 
1590  #ifdef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1591  static void printTransitions(Transition** begin, Transition** end) {
1592  for (Transition** iter = begin; iter != end; ++iter) {
1593  (*iter)->log();
1594  logging::printf("\n");
1595  }
1596  }
1597  #endif
1598 
1604  static void expandDateTuple(
1605  const extended::DateTuple* tt,
1606  int16_t offsetMinutes,
1607  int16_t deltaMinutes,
1608  extended::DateTuple* ttw,
1609  extended::DateTuple* tts,
1610  extended::DateTuple* ttu) {
1611 
1612  if (tt->suffix == internal::ZoneContext::kSuffixS) {
1613  *tts = *tt;
1614  *ttu = {tt->yearTiny, tt->month, tt->day,
1615  (int16_t) (tt->minutes - offsetMinutes),
1617  *ttw = {tt->yearTiny, tt->month, tt->day,
1618  (int16_t) (tt->minutes + deltaMinutes),
1620  } else if (tt->suffix == internal::ZoneContext::kSuffixU) {
1621  *ttu = *tt;
1622  *tts = {tt->yearTiny, tt->month, tt->day,
1623  (int16_t) (tt->minutes + offsetMinutes),
1625  *ttw = {tt->yearTiny, tt->month, tt->day,
1626  (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1628  } else {
1629  // Explicit set the suffix to 'w' in case it was something else.
1630  *ttw = *tt;
1631  ttw->suffix = internal::ZoneContext::kSuffixW;
1632  *tts = {tt->yearTiny, tt->month, tt->day,
1633  (int16_t) (tt->minutes - deltaMinutes),
1635  *ttu = {tt->yearTiny, tt->month, tt->day,
1636  (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1638  }
1639 
1640  normalizeDateTuple(ttw);
1641  normalizeDateTuple(tts);
1642  normalizeDateTuple(ttu);
1643  }
1644 
1649  static void normalizeDateTuple(extended::DateTuple* dt) {
1650  const int16_t kOneDayAsMinutes = 60 * 24;
1651  if (dt->minutes <= -kOneDayAsMinutes) {
1652  LocalDate ld = LocalDate::forTinyComponents(
1653  dt->yearTiny, dt->month, dt->day);
1654  local_date_mutation::decrementOneDay(ld);
1655  dt->yearTiny = ld.yearTiny();
1656  dt->month = ld.month();
1657  dt->day = ld.day();
1658  dt->minutes += kOneDayAsMinutes;
1659  } else if (kOneDayAsMinutes <= dt->minutes) {
1660  LocalDate ld = LocalDate::forTinyComponents(
1661  dt->yearTiny, dt->month, dt->day);
1662  local_date_mutation::incrementOneDay(ld);
1663  dt->yearTiny = ld.yearTiny();
1664  dt->month = ld.month();
1665  dt->day = ld.day();
1666  dt->minutes -= kOneDayAsMinutes;
1667  } else {
1668  // do nothing
1669  }
1670  }
1671 
1676  static void selectActiveTransitions(Transition** begin, Transition** end) {
1677  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1678  logging::printf("selectActiveTransitions(): #candidates: %d\n",
1679  (int) (end - begin));
1680  }
1681 
1682  Transition* prior = nullptr;
1683  for (Transition** iter = begin; iter != end; ++iter) {
1684  Transition* transition = *iter;
1685  processTransitionMatchStatus(transition, &prior);
1686  }
1687 
1688  // If the latest prior transition is found, shift it to start at the
1689  // startDateTime of the current match.
1690  if (prior) {
1691  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1692  logging::printf(
1693  "selectActiveTransitions(): found latest prior\n");
1694  }
1695  #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1696  prior->originalTransitionTime = prior->transitionTime;
1697  #endif
1698  prior->transitionTime = prior->match->startDateTime;
1699  }
1700  }
1701 
1708  static void processTransitionMatchStatus(
1709  Transition* transition,
1710  Transition** prior) {
1711  using extended::MatchStatus;
1712 
1713  MatchStatus status = compareTransitionToMatch(
1714  transition, transition->match);
1715  transition->matchStatus = status;
1716 
1717  if (status == MatchStatus::kExactMatch) {
1718  if (*prior) {
1719  (*prior)->matchStatus = MatchStatus::kFarPast;
1720  }
1721  (*prior) = transition;
1722  } else if (status == MatchStatus::kPrior) {
1723  if (*prior) {
1724  if ((*prior)->transitionTimeU <= transition->transitionTimeU) {
1725  (*prior)->matchStatus = MatchStatus::kFarPast;
1726  (*prior) = transition;
1727  } else {
1728  transition->matchStatus = MatchStatus::kFarPast;
1729  }
1730  } else {
1731  (*prior) = transition;
1732  }
1733  }
1734  }
1735 
1744  static extended::MatchStatus compareTransitionToMatch(
1745  const Transition* transition,
1746  const MatchingEra* match) {
1747 
1748  // Find the previous Match offsets.
1749  int16_t prevMatchOffsetMinutes;
1750  int16_t prevMatchDeltaMinutes;
1751  if (match->prevMatch) {
1752  prevMatchOffsetMinutes = match->prevMatch->lastOffsetMinutes;
1753  prevMatchDeltaMinutes = match->prevMatch->lastDeltaMinutes;
1754  } else {
1755  prevMatchOffsetMinutes = match->era.offsetMinutes();
1756  prevMatchDeltaMinutes = 0;
1757  }
1758 
1759  // Expand start times.
1760  extended::DateTuple stw;
1761  extended::DateTuple sts;
1762  extended::DateTuple stu;
1763  expandDateTuple(
1764  &match->startDateTime,
1765  prevMatchOffsetMinutes,
1766  prevMatchDeltaMinutes,
1767  &stw,
1768  &sts,
1769  &stu);
1770 
1771  // Transition times.
1772  const extended::DateTuple& ttw = transition->transitionTime;
1773  const extended::DateTuple& tts = transition->transitionTimeS;
1774  const extended::DateTuple& ttu = transition->transitionTimeU;
1775 
1776  // Compare Transition to Match, where equality is assumed if *any* of the
1777  // 'w', 's', or 'u' versions of the DateTuple are equal. This prevents
1778  // duplicate Transition instances from being created in a few cases.
1779  if (ttw == stw || tts == sts || ttu == stu) {
1780  return extended::MatchStatus::kExactMatch;
1781  }
1782 
1783  if (ttu < stu) {
1784  return extended::MatchStatus::kPrior;
1785  }
1786 
1787  // Now check if the transition occurs after the given match. The
1788  // untilDateTime of the current match uses the same UTC offsets as the
1789  // transitionTime of the current transition, so no complicated adjustments
1790  // are needed. We just make sure we compare 'w' with 'w', 's' with 's',
1791  // and 'u' with 'u'.
1792  const extended::DateTuple& matchUntil = match->untilDateTime;
1793  const extended::DateTuple* transitionTime;
1794  if (matchUntil.suffix == internal::ZoneContext::kSuffixS) {
1795  transitionTime = &tts;
1796  } else if (matchUntil.suffix == internal::ZoneContext::kSuffixU) {
1797  transitionTime = &ttu;
1798  } else { // assume 'w'
1799  transitionTime = &ttw;
1800  }
1801  if (*transitionTime < matchUntil) {
1802  return extended::MatchStatus::kWithinMatch;
1803  }
1804  return extended::MatchStatus::kFarFuture;
1805  }
1806 
1812  static void generateStartUntilTimes(Transition** begin, Transition** end) {
1813  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1814  logging::printf(
1815  "generateStartUntilTimes(): #transitions=%d\n",
1816  (int) (end - begin));
1817  }
1818 
1819  Transition* prev = *begin;
1820  bool isAfterFirst = false;
1821 
1822  for (Transition** iter = begin; iter != end; ++iter) {
1823  Transition* const t = *iter;
1824 
1825  // 1) Update the untilDateTime of the previous Transition
1826  const extended::DateTuple& tt = t->transitionTime;
1827  if (isAfterFirst) {
1828  prev->untilDateTime = tt;
1829  }
1830 
1831  // 2) Calculate the current startDateTime by shifting the
1832  // transitionTime (represented in the UTC offset of the previous
1833  // transition) into the UTC offset of the *current* transition.
1834  int16_t minutes = tt.minutes + (
1835  - prev->offsetMinutes - prev->deltaMinutes
1836  + t->offsetMinutes + t->deltaMinutes);
1837  t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
1838  tt.suffix};
1839  normalizeDateTuple(&t->startDateTime);
1840 
1841  // 3) The epochSecond of the 'transitionTime' is determined by the
1842  // UTC offset of the *previous* Transition. However, the
1843  // transitionTime can be represented by an illegal time (e.g. 24:00).
1844  // So, it is better to use the properly normalized startDateTime
1845  // (calculated above) with the *current* UTC offset.
1846  //
1847  // NOTE: We should also be able to calculate this directly from
1848  // 'transitionTimeU' which should still be a valid field, because it
1849  // hasn't been clobbered by 'untilDateTime' yet. Not sure if this saves
1850  // any CPU time though, since we still need to mutiply by 900.
1851  const extended::DateTuple& st = t->startDateTime;
1852  const acetime_t offsetSeconds = (acetime_t) 60
1853  * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
1854  LocalDate ld = LocalDate::forTinyComponents(
1855  st.yearTiny, st.month, st.day);
1856  t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1857 
1858  prev = t;
1859  isAfterFirst = true;
1860  }
1861 
1862  // The last Transition's until time is the until time of the MatchingEra.
1863  extended::DateTuple untilTimeW;
1864  extended::DateTuple untilTimeS;
1865  extended::DateTuple untilTimeU;
1866  expandDateTuple(
1867  &prev->match->untilDateTime,
1868  prev->offsetMinutes,
1869  prev->deltaMinutes,
1870  &untilTimeW,
1871  &untilTimeS,
1872  &untilTimeU);
1873  prev->untilDateTime = untilTimeW;
1874  }
1875 
1879  static void calcAbbreviations(Transition** begin, Transition** end) {
1880  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1881  logging::printf("calcAbbreviations(): #transitions: %d\n",
1882  (int) (end - begin));
1883  }
1884  for (Transition** iter = begin; iter != end; ++iter) {
1885  Transition* const t = *iter;
1886  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1887  logging::printf(
1888  "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1889  t->format(), t->deltaMinutes, t->letter());
1890  }
1891  createAbbreviation(
1892  t->abbrev,
1893  internal::kAbbrevSize,
1894  t->format(),
1895  t->deltaMinutes,
1896  t->letter());
1897  }
1898  }
1899 
1908  static void createAbbreviation(
1909  char* dest,
1910  uint8_t destSize,
1911  const char* format,
1912  uint16_t deltaMinutes,
1913  const char* letterString) {
1914 
1915  // Check if FORMAT contains a '%'.
1916  if (strchr(format, '%') != nullptr) {
1917  // Check if RULES column empty, therefore no 'letter'
1918  if (letterString == nullptr) {
1919  strncpy(dest, format, destSize - 1);
1920  dest[destSize - 1] = '\0';
1921  } else {
1922  ace_common::copyReplaceString(
1923  dest, destSize, format, '%', letterString);
1924  }
1925  } else {
1926  // Check if FORMAT contains a '/'.
1927  const char* slashPos = strchr(format, '/');
1928  if (slashPos != nullptr) {
1929  if (deltaMinutes == 0) {
1930  uint8_t headLength = (slashPos - format);
1931  if (headLength >= destSize) headLength = destSize - 1;
1932  memcpy(dest, format, headLength);
1933  dest[headLength] = '\0';
1934  } else {
1935  uint8_t tailLength = strlen(slashPos+1);
1936  if (tailLength >= destSize) tailLength = destSize - 1;
1937  memcpy(dest, slashPos+1, tailLength);
1938  dest[tailLength] = '\0';
1939  }
1940  } else {
1941  // Just copy the FORMAT disregarding deltaMinutes and letterString.
1942  strncpy(dest, format, destSize);
1943  dest[destSize - 1] = '\0';
1944  }
1945  }
1946  }
1947 
1948  const BF* mBrokerFactory;
1949  ZIB mZoneInfoBroker;
1950 
1951  mutable int16_t mYear = 0; // maybe create LocalDate::kInvalidYear?
1952  mutable bool mIsFilled = false;
1953  // NOTE: Maybe move mNumMatches and mMatches into a MatchStorage object.
1954  mutable uint8_t mNumMatches = 0; // actual number of matches
1955  mutable MatchingEra mMatches[kMaxMatches];
1956  mutable TransitionStorage mTransitionStorage;
1957 };
1958 
1959 
1965  extended::BrokerFactory,
1966  extended::ZoneInfoBroker,
1967  extended::ZoneEraBroker,
1968  extended::ZonePolicyBroker,
1969  extended::ZoneRuleBroker> {
1970 
1971  public:
1973  static const uint8_t kTypeExtended = 4;
1974 
1975  explicit ExtendedZoneProcessor(const extended::ZoneInfo* zoneInfo = nullptr)
1977  extended::BrokerFactory,
1978  extended::ZoneInfoBroker,
1979  extended::ZoneEraBroker,
1980  extended::ZonePolicyBroker,
1981  extended::ZoneRuleBroker>(
1982  kTypeExtended, &mBrokerFactory, (uintptr_t) zoneInfo)
1983  {}
1984 
1985  private:
1986  extended::BrokerFactory mBrokerFactory;
1987 };
1988 
1989 } // namespace ace_time
1990 
1991 #endif
ace_time::extended::TransitionTemplate::match
const MatchingEraTemplate< ZEB > * match
The match which generated this Transition.
Definition: ExtendedZoneProcessor.h:227
ace_time::extended::TransitionTemplate::untilDateTime
DateTuple untilDateTime
Until time expressed using the UTC offset of the current Transition.
Definition: ExtendedZoneProcessor.h:271
ace_time::ExtendedZoneProcessorTemplate::initForYear
bool initForYear(int16_t year) const
Initialize the zone rules cache, keyed by the "current" year.
Definition: ExtendedZoneProcessor.h:959
ace_time::extended::TransitionStorageTemplate::init
void init()
Initialize all pools.
Definition: ExtendedZoneProcessor.h:443
ace_time::extended::TransitionStorageTemplate::getPrior
Transition * getPrior()
Return the current prior transition.
Definition: ExtendedZoneProcessor.h:453
ace_time::extended::TransitionStorageTemplate::reservePrior
Transition ** reservePrior()
Allocate one Transition just after the Active pool, but before the Candidate pool,...
Definition: ExtendedZoneProcessor.h:522
ace_time::extended::TransitionTemplate::isValidPrior
bool isValidPrior
During findCandidateTransitions(), this flag indicates whether the current transition is a valid "pri...
Definition: ExtendedZoneProcessor.h:310
ace_time::extended::TransitionTemplate::letterBuf
char letterBuf[2]
Storage for the single letter 'letter' field if 'rule' is not null.
Definition: ExtendedZoneProcessor.h:301
ace_time::LocalDateTime::hour
uint8_t hour() const
Return the hour.
Definition: LocalDateTime.h:200
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::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:234
ace_time::TimeOffset::isError
bool isError() const
Return true if this TimeOffset represents an error.
Definition: TimeOffset.h:138
ace_time::extended::TransitionStorageTemplate::addPriorToCandidatePool
void addPriorToCandidatePool()
Add the current prior into the Candidates pool.
Definition: ExtendedZoneProcessor.h:547
ace_time::ExtendedZoneProcessorTemplate::getTransitionHighWater
uint8_t getTransitionHighWater() const
Get the TransitionStorage high water mark.
Definition: ExtendedZoneProcessor.h:916
ace_time::extended::TransitionTemplate::offsetMinutes
int16_t offsetMinutes
The base offset minutes, not the total effective UTC offset.
Definition: ExtendedZoneProcessor.h:292
ace_time::extended::TransitionStorageTemplate::getFreeAgent
Transition * getFreeAgent()
Return a pointer to the first Transition in the free pool.
Definition: ExtendedZoneProcessor.h:489
ace_time::extended::TransitionStorageTemplate::TransitionStorageTemplate
TransitionStorageTemplate()
Constructor.
Definition: ExtendedZoneProcessor.h:440
ace_time::extended::TransitionTemplate::letter
const char * letter() const
Return the letter string.
Definition: ExtendedZoneProcessor.h:330
ace_time::LocalDateTime::printTo
void printTo(Print &printer) const
Print LocalDateTime to 'printer' in ISO 8601 format.
Definition: LocalDateTime.cpp:14
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::extended::TransitionStorageTemplate::getHighWater
uint8_t getHighWater() const
Return the high water mark.
Definition: ExtendedZoneProcessor.h:721
ace_time::extended::MatchingEraTemplate::lastDeltaMinutes
uint16_t lastDeltaMinutes
The DST offset of the last Transition in this MatchingEra.
Definition: ExtendedZoneProcessor.h:157
ace_time::OffsetDateTime::forEpochSeconds
static OffsetDateTime forEpochSeconds(acetime_t epochSeconds, TimeOffset timeOffset)
Factory method.
Definition: OffsetDateTime.h:71
ace_time::internal::ZoneContext::kSuffixS
static const uint8_t kSuffixS
Represents 's' or standard time.
Definition: ZoneContext.h:21
ace_time::ExtendedZoneProcessorTemplate::getZoneId
uint32_t getZoneId() const override
Return the unique stable zoneId.
Definition: ExtendedZoneProcessor.h:805
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:1973
ace_time::extended::TransitionTemplate::transitionTimeU
DateTuple transitionTimeU
Version of transitionTime in 'u' mode, using the UTC offset of the previous transition.
Definition: ExtendedZoneProcessor.h:265
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:157
ace_time::ExtendedZoneProcessor
A specific implementation of ExtendedZoneProcessorTemplate that uses ZoneXxxBrokers which read from z...
Definition: ExtendedZoneProcessor.h:1964
ace_time::extended::TransitionStorageTemplate::resetCandidatePool
void resetCandidatePool()
Empty the Candidate pool by resetting the various indexes.
Definition: ExtendedZoneProcessor.h:465
ace_time::ExtendedZoneProcessorTemplate::TransitionStorage
extended::TransitionStorageTemplate< kMaxTransitions, ZEB, ZPB, ZRB > TransitionStorage
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:803
ace_time::extended::MatchingEraTemplate::lastOffsetMinutes
uint16_t lastOffsetMinutes
The STD offset of the last Transition in this MatchingEra.
Definition: ExtendedZoneProcessor.h:154
ace_time::ExtendedZoneProcessorTemplate::getAbbrev
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
Definition: ExtendedZoneProcessor.h:824
ace_time::ExtendedZoneProcessorTemplate::MatchingEra
extended::MatchingEraTemplate< ZEB > MatchingEra
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:799
ace_time::LocalDateTime::month
uint8_t month() const
Return the month with January=1, December=12.
Definition: LocalDateTime.h:188
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:1035
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:45
ace_time::ExtendedZoneProcessorTemplate::getUtcOffset
TimeOffset getUtcOffset(acetime_t epochSeconds) const override
Return the total UTC offset at epochSeconds, including DST offset.
Definition: ExtendedZoneProcessor.h:807
ace_time::ExtendedZoneProcessorTemplate::printShortNameTo
void printShortNameTo(Print &printer) const override
Print a short human-readable identifier (e.g.
Definition: ExtendedZoneProcessor.h:893
ace_time::extended::TransitionTemplate::matchStatus
MatchStatus matchStatus
During processTransitionMatchStatus(), this flag indicates how the transition falls within the time i...
Definition: ExtendedZoneProcessor.h:316
ace_time::extended::TransitionTemplate::abbrev
char abbrev[internal::kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
Definition: ExtendedZoneProcessor.h:298
ace_time::extended::TransitionStorageTemplate::findTransitionForDateTime
const Transition * findTransitionForDateTime(const LocalDateTime &ldt) const
Return the Transition matching the given dateTime.
Definition: ExtendedZoneProcessor.h:654
ace_time::extended::TransitionTemplate::startDateTime
DateTuple startDateTime
Start time expressed using the UTC offset of the current Transition.
Definition: ExtendedZoneProcessor.h:256
ace_time::extended::DateTuple
A tuple that represents a date and time.
Definition: ExtendedZoneProcessor.h:66
ace_time::ExtendedZoneProcessorTemplate::kMaxTransitions
static const uint8_t kMaxTransitions
Max number of Transitions required for a given Zone, including the most recent prior Transition.
Definition: ExtendedZoneProcessor.h:793
ace_time::extended::MatchingEraTemplate::prevMatch
MatchingEraTemplate * prevMatch
The previous MatchingEra, needed to interpret startDateTime.
Definition: ExtendedZoneProcessor.h:151
ace_time::ExtendedZoneProcessorTemplate::getOffsetDateTime
OffsetDateTime getOffsetDateTime(const LocalDateTime &ldt) const override
Return the best estimate of the OffsetDateTime at the given LocalDateTime for the timezone of the cur...
Definition: ExtendedZoneProcessor.h:831
ace_time::LocalDateTime::minute
uint8_t minute() const
Return the minute.
Definition: LocalDateTime.h:206
ace_time::extended::BrokerFactory
A factory that creates a basic::ZoneInfoBroker.
Definition: ExtendedBrokers.h:478
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:431
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:113
ace_time::ExtendedZoneProcessorTemplate::setBrokerFactory
void setBrokerFactory(const BF *brokerFactory)
Set the broker factory at runtime.
Definition: ExtendedZoneProcessor.h:940
ace_time::extended::TransitionStorageTemplate::addActiveCandidatesToActivePool
Transition * addActiveCandidatesToActivePool()
Add active candidates into the Active pool, and collapse the Candidate pool.
Definition: ExtendedZoneProcessor.h:583
ace_time::ExtendedZoneProcessorTemplate::initForEpochSeconds
bool initForEpochSeconds(acetime_t epochSeconds) const
Initialize using the epochSeconds.
Definition: ExtendedZoneProcessor.h:949
ace_time::LocalDate::year
int16_t year() const
Return the full year instead of just the last 2 digits.
Definition: LocalDate.h:231
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:168
ace_time::extended::TransitionTemplate::deltaMinutes
int16_t deltaMinutes
The DST delta minutes.
Definition: ExtendedZoneProcessor.h:295
ace_time::extended::TransitionTemplate::transitionTimeS
DateTuple transitionTimeS
Version of transitionTime in 's' mode, using the UTC offset of the previous Transition.
Definition: ExtendedZoneProcessor.h:250
ace_time::extended::TransitionStorageTemplate::setFreeAgentAsPriorIfValid
void setFreeAgentAsPriorIfValid()
Set the free agent transition as the most recent prior.
Definition: ExtendedZoneProcessor.h:531
ace_time::extended::MatchingEraTemplate::untilDateTime
DateTuple untilDateTime
The effective until time of the matching ZoneEra.
Definition: ExtendedZoneProcessor.h:145
ace_time::extended::MatchingEraTemplate
Data structure that captures the matching ZoneEra and its ZoneRule transitions for a given year.
Definition: ExtendedZoneProcessor.h:137
ace_time::LocalDateTime::yearTiny
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
Definition: LocalDateTime.h:178
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:142
ExtendedBrokers.h
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:920
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:930
ace_time::extended::TransitionStorageTemplate::resetHighWater
void resetHighWater()
Reset the high water mark.
Definition: ExtendedZoneProcessor.h:714
ace_time::extended::TransitionStorageTemplate::Transition
TransitionTemplate< ZEB, ZPB, ZRB > Transition
Template instantiation of TransitionTemplate used by this class.
Definition: ExtendedZoneProcessor.h:437
ace_time::extended::TransitionTemplate::startEpochSeconds
acetime_t startEpochSeconds
The calculated transition time of the given rule.
Definition: ExtendedZoneProcessor.h:283
ace_time::ExtendedZoneProcessorTemplate::log
void log() const
Used only for debugging.
Definition: ExtendedZoneProcessor.h:898
ace_time::extended::TransitionStorageTemplate::addFreeAgentToActivePool
void addFreeAgentToActivePool()
Immediately add the free agent Transition at index mIndexFree to the Active pool.
Definition: ExtendedZoneProcessor.h:510
ace_time::LocalDateTime::day
uint8_t day() const
Return the day of the month.
Definition: LocalDateTime.h:194
ace_time::extended::TransitionTemplate
Represents an interval of time where the time zone obeyed a certain UTC offset and DST delta.
Definition: ExtendedZoneProcessor.h:225
ace_time::ExtendedZoneProcessorTemplate::printNameTo
void printNameTo(Print &printer) const override
Print a human-readable identifier (e.g.
Definition: ExtendedZoneProcessor.h:889
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:125
ace_time::ExtendedZoneProcessorTemplate::getDeltaOffset
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
Definition: ExtendedZoneProcessor.h:817
ace_time::ExtendedZoneProcessorTemplate::resetTransitionHighWater
void resetTransitionHighWater()
Reset the TransitionStorage high water mark.
Definition: ExtendedZoneProcessor.h:911
ace_time::extended::DateTuple::log
void log() const
Used only for debugging.
Definition: ExtendedZoneProcessor.h:79
ace_time::extended::MatchingEraTemplate::era
ZEB era
The ZoneEra that matched the given year.
Definition: ExtendedZoneProcessor.h:148
ace_time::extended::TransitionTemplate::transitionTime
DateTuple transitionTime
The original transition time, usually 'w' but sometimes 's' or 'u'.
Definition: ExtendedZoneProcessor.h:242
ace_time::internal::ZoneContext::kSuffixW
static const uint8_t kSuffixW
Represents 'w' or wall time.
Definition: ZoneContext.h:18
ace_time::ExtendedZoneProcessorTemplate::Transition
extended::TransitionTemplate< ZEB, ZPB, ZRB > Transition
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:796
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:557
ace_time::extended::TransitionStorageTemplate::findTransition
const Transition * findTransition(acetime_t epochSeconds) const
Return the Transition matching the given epochSeconds.
Definition: ExtendedZoneProcessor.h:617
ace_time::extended::TransitionTemplate::log
void log() const
Used only for debugging.
Definition: ExtendedZoneProcessor.h:360
ace_time::ExtendedZoneProcessorTemplate
An implementation of ZoneProcessor that supports for all zones defined by the TZ Database.
Definition: ExtendedZoneProcessor.h:783
ace_time::extended::TransitionTemplate::logHourMinute
static void logHourMinute(int16_t minutes)
Print minutes as [+/-]hh:mm.
Definition: ExtendedZoneProcessor.h:385
ace_time::extended::TransitionStorageTemplate::log
void log() const
Verify that the indexes are valid.
Definition: ExtendedZoneProcessor.h:681