AceTime  1.9.0
Date and time classes for Arduino that support timezones from the TZ Database.
ExtendedZoneProcessor.h
1 /*
2  * MIT License
3  * Copyright (c) 2019 Brian T. Park
4  */
5 
6 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
7 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
8 
9 #include <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 
448  void init() {
449  for (uint8_t i = 0; i < SIZE; i++) {
450  mTransitions[i] = &mPool[i];
451  }
452  mIndexPrior = 0;
453  mIndexCandidates = 0;
454  mIndexFree = 0;
455  }
456 
459  return mTransitions[mIndexPrior];
460  }
461 
471  mIndexCandidates = mIndexPrior;
472  mIndexFree = mIndexPrior;
473  }
474 
475  Transition** getCandidatePoolBegin() {
476  return &mTransitions[mIndexCandidates];
477  }
478  Transition** getCandidatePoolEnd() {
479  return &mTransitions[mIndexFree];
480  }
481 
482  Transition** getActivePoolBegin() {
483  return &mTransitions[0];
484  }
485  Transition** getActivePoolEnd() {
486  return &mTransitions[mIndexFree];
487  }
488 
495  if (mIndexFree < SIZE) {
496  // Allocate a free transition.
497  if (mIndexFree >= mAllocSize) {
498  mAllocSize = mIndexFree + 1;
499  }
500  return mTransitions[mIndexFree];
501  } else {
502  // No more transition available in the buffer, so just return the last
503  // one. This will probably cause a bug in the timezone calculations, but
504  // I think this is better than triggering undefined behavior by running
505  // off the end of the mTransitions buffer.
506  return mTransitions[SIZE - 1];
507  }
508  }
509 
518  if (mIndexFree >= SIZE) return;
519  mIndexFree++;
520  mIndexPrior = mIndexFree;
521  mIndexCandidates = mIndexFree;
522  }
523 
533  getFreeAgent(); // allocate a new Transition
534 
535  mIndexCandidates++;
536  mIndexFree++;
537  return &mTransitions[mIndexPrior];
538  }
539 
542  Transition* ft = mTransitions[mIndexFree];
543  Transition* prior = mTransitions[mIndexPrior];
544  if ((prior->isValidPrior && prior->transitionTime < ft->transitionTime)
545  || !prior->isValidPrior) {
546  ft->isValidPrior = true;
547  prior->isValidPrior = false;
548  swap(mTransitions[mIndexPrior], mTransitions[mIndexFree]);
549  }
550  }
551 
558  mIndexCandidates--;
559  }
560 
568  if (mIndexFree >= SIZE) return;
569 
570  // This implementation makes pair-wise swaps to shift the current
571  // Transition leftwards into its correctly sorted position. At first
572  // glance, this seem inefficient compared to the alternative
573  // implementation where we save the current Transition, then slide all the
574  // elements to the left by one position rightwards. However,
575  // MemoryBenchmark shows that this implementation is 46 bytes smaller on
576  // an AVR processor.
577  for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
578  Transition* curr = mTransitions[i];
579  Transition* prev = mTransitions[i - 1];
580  if (curr->transitionTime >= prev->transitionTime) break;
581  mTransitions[i] = prev;
582  mTransitions[i - 1] = curr;
583  }
584  mIndexFree++;
585  }
586 
594  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
595  logging::printf("addActiveCandidatesToActivePool()\n");
596  }
597 
598  // Shift active candidates to the left into the Active pool.
599  uint8_t iActive = mIndexPrior;
600  uint8_t iCandidate = mIndexCandidates;
601  for (; iCandidate < mIndexFree; iCandidate++) {
602  if (isMatchStatusActive(mTransitions[iCandidate]->matchStatus)) {
603  if (iActive != iCandidate) {
604  // Must use swap(), because we are moving pointers instead of the
605  // actual Transition objects.
606  swap(mTransitions[iActive], mTransitions[iCandidate]);
607  }
608  ++iActive;
609  }
610  }
611 
612  mIndexPrior = iActive;
613  mIndexCandidates = iActive;
614  mIndexFree = iActive;
615 
616  return mTransitions[iActive - 1];
617  }
618 
627  const Transition* findTransition(acetime_t epochSeconds) const {
628  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
629  logging::printf( "findTransition(): mIndexFree: %d\n", mIndexFree);
630  }
631 
632  const Transition* match = nullptr;
633  for (uint8_t i = 0; i < mIndexFree; i++) {
634  const Transition* candidate = mTransitions[i];
635  if (candidate->startEpochSeconds > epochSeconds) break;
636  match = candidate;
637  }
638  return match;
639  }
640 
665  const {
666  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
667  logging::printf(
668  "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
669  }
670 
671  // Convert LocalDateTime to DateTuple.
672  DateTuple localDate{
673  ldt.yearTiny(),
674  ldt.month(),
675  ldt.day(),
676  (int16_t) (ldt.hour() * 60 + ldt.minute()),
678  };
679  const Transition* match = nullptr;
680 
681  // Find the last Transition that matches
682  for (uint8_t i = 0; i < mIndexFree; i++) {
683  const Transition* candidate = mTransitions[i];
684  if (candidate->startDateTime > localDate) break;
685  match = candidate;
686  }
687  return match;
688  }
689 
691  void log() const {
692  logging::printf("TransitionStorage: ");
693  logging::printf("nActives=%d", mIndexPrior);
694  logging::printf(", nPrior=%d", mIndexCandidates - mIndexPrior);
695  logging::printf(", nCandidates=%d", mIndexFree - mIndexCandidates);
696  logging::printf(", nFree=%d", SIZE - mIndexFree);
697  logging::printf("\n");
698 
699  if (mIndexPrior != 0) {
700  logging::printf(" Actives:\n");
701  for (uint8_t i = 0; i < mIndexPrior; i++) {
702  logging::printf(" ");
703  mTransitions[i]->log();
704  logging::printf("\n");
705  }
706  }
707  if (mIndexPrior != mIndexCandidates) {
708  logging::printf(" Prior: \n");
709  logging::printf(" ");
710  mTransitions[mIndexPrior]->log();
711  logging::printf("\n");
712  }
713  if (mIndexCandidates != mIndexFree) {
714  logging::printf(" Candidates:\n");
715  for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
716  logging::printf(" ");
717  mTransitions[i]->log();
718  logging::printf("\n");
719  }
720  }
721  }
722 
724  void resetAllocSize() { mAllocSize = 0; }
725 
731  uint8_t getAllocSize() const { return mAllocSize; }
732 
733  private:
734  friend class ::TransitionStorageTest_getFreeAgent;
735  friend class ::TransitionStorageTest_getFreeAgent2;
736  friend class ::TransitionStorageTest_addFreeAgentToActivePool;
737  friend class ::TransitionStorageTest_reservePrior;
738  friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
739  friend class ::TransitionStorageTest_setFreeAgentAsPriorIfValid;
740  friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
741  friend class ::TransitionStorageTest_findTransitionForDateTime;
742  friend class ::TransitionStorageTest_resetCandidatePool;
743 
745  Transition* getTransition(uint8_t i) {
746  return mTransitions[i];
747  }
748 
749  Transition mPool[SIZE];
750  Transition* mTransitions[SIZE];
751  uint8_t mIndexPrior;
752  uint8_t mIndexCandidates;
753  uint8_t mIndexFree;
754 
756  uint8_t mAllocSize = 0;
757 };
758 
759 } // namespace extended
760 
792 template <typename BF, typename ZIB, typename ZEB, typename ZPB, typename ZRB>
794  public:
805  static const uint8_t kMaxTransitions = 8;
806 
809 
812 
816 
817  uint32_t getZoneId() const override { return mZoneInfoBroker.zoneId(); }
818 
819  TimeOffset getUtcOffset(acetime_t epochSeconds) const override {
820  bool success = initForEpochSeconds(epochSeconds);
821  if (!success) return TimeOffset::forError();
822  const Transition* transition = findTransition(epochSeconds);
823  return (transition)
825  transition->offsetMinutes + transition->deltaMinutes)
827  }
828 
829  TimeOffset getDeltaOffset(acetime_t epochSeconds) const override {
830  bool success = initForEpochSeconds(epochSeconds);
831  if (!success) return TimeOffset::forError();
832  const Transition* transition = findTransition(epochSeconds);
833  return TimeOffset::forMinutes(transition->deltaMinutes);
834  }
835 
836  const char* getAbbrev(acetime_t epochSeconds) const override {
837  bool success = initForEpochSeconds(epochSeconds);
838  if (!success) return "";
839  const Transition* transition = findTransition(epochSeconds);
840  return transition->abbrev;
841  }
842 
843  OffsetDateTime getOffsetDateTime(const LocalDateTime& ldt) const override {
844  TimeOffset offset;
845  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
846  logging::printf("getOffsetDateTime(): ldt=");
847  ldt.printTo(SERIAL_PORT_MONITOR);
848  SERIAL_PORT_MONITOR.println();
849  }
850  bool success = initForYear(ldt.year());
851 
852  // Find the Transition to get the DST offset
853  if (success) {
854  const Transition* transition =
855  mTransitionStorage.findTransitionForDateTime(ldt);
856  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
857  logging::printf("getOffsetDateTime(): match transition=");
858  transition->log();
859  logging::printf("\n");
860  }
861  offset = (transition)
863  transition->offsetMinutes + transition->deltaMinutes)
865  } else {
866  offset = TimeOffset::forError();
867  }
868 
869  auto odt = OffsetDateTime::forLocalDateTimeAndOffset(ldt, offset);
870  if (offset.isError()) {
871  return odt;
872  }
873  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
874  logging::printf("getOffsetDateTime(): odt=");
875  odt.printTo(SERIAL_PORT_MONITOR);
876  SERIAL_PORT_MONITOR.println();
877  }
878 
879  // Normalize the OffsetDateTime, causing LocalDateTime in the DST
880  // transtion gap to be shifted forward one hour. For LocalDateTime in an
881  // overlap (DST->STD transition), the earlier UTC offset is selected// by
882  // findTransitionForDateTime(). Use that to calculate the epochSeconds,
883  // then recalculate the offset. Use this final offset to determine the
884  // effective OffsetDateTime that will survive a round-trip unchanged.
885  acetime_t epochSeconds = odt.toEpochSeconds();
886  const Transition* transition =
887  mTransitionStorage.findTransition(epochSeconds);
888  offset = (transition)
890  transition->offsetMinutes + transition->deltaMinutes)
892  odt = OffsetDateTime::forEpochSeconds(epochSeconds, offset);
893  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
894  logging::printf("getOffsetDateTime(): normalized(odt)=");
895  odt.printTo(SERIAL_PORT_MONITOR);
896  SERIAL_PORT_MONITOR.println();
897  }
898  return odt;
899  }
900 
901  void printNameTo(Print& printer) const override {
902  mZoneInfoBroker.printNameTo(printer);
903  }
904 
905  void printShortNameTo(Print& printer) const override {
906  mZoneInfoBroker.printShortNameTo(printer);
907  }
908 
910  void log() const {
911  logging::printf("ExtendedZoneProcessor\n");
912  logging::printf(" mYear: %d\n", mYear);
913  logging::printf(" mNumMatches: %d\n", mNumMatches);
914  for (int i = 0; i < mNumMatches; i++) {
915  logging::printf(" Match %d: ", i);
916  mMatches[i].log();
917  logging::printf("\n");
918  }
919  mTransitionStorage.log();
920  }
921 
924  mTransitionStorage.resetAllocSize();
925  }
926 
928  uint8_t getTransitionAllocSize() const {
929  return mTransitionStorage.getAllocSize();
930  }
931 
932  void setZoneKey(uintptr_t zoneKey) override {
933  if (mZoneInfoBroker.equals(zoneKey)) return;
934 
935  mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
936  mYear = 0;
937  mIsFilled = false;
938  mNumMatches = 0;
939  resetTransitionAllocSize(); // clear the alloc size for new zone
940  }
941 
942  bool equalsZoneKey(uintptr_t zoneKey) const override {
943  return mZoneInfoBroker.equals(zoneKey);
944  }
945 
952  void setBrokerFactory(const BF* brokerFactory) {
953  mBrokerFactory = brokerFactory;
954  }
955 
961  bool initForEpochSeconds(acetime_t epochSeconds) const {
962  LocalDate ld = LocalDate::forEpochSeconds(epochSeconds);
963  return initForYear(ld.year());
964  }
965 
971  bool initForYear(int16_t year) const {
972  if (isFilled(year)) return true;
973  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
974  logging::printf("initForYear(): %d\n", year);
975  }
976 
977  mYear = year;
978  mNumMatches = 0; // clear cache
979  mTransitionStorage.init();
980 
981  if (year < mZoneInfoBroker.zoneContext()->startYear - 1
982  || mZoneInfoBroker.zoneContext()->untilYear < year) {
983  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
984  logging::printf("init(): Year %d out of valid range [%d, %d)\n",
985  year,
986  mZoneInfoBroker.zoneContext()->startYear,
987  mZoneInfoBroker.zoneContext()->untilYear);
988  }
989  return false;
990  }
991 
992  extended::YearMonthTuple startYm = {
993  (int8_t) (year - LocalDate::kEpochYear - 1), 12 };
994  extended::YearMonthTuple untilYm = {
995  (int8_t) (year - LocalDate::kEpochYear + 1), 2 };
996 
997  // Step 1. The equivalent steps for the Python version are in the
998  // AceTimePython project, under
999  // zone_processor.ZoneProcessor.init_for_year().
1000  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1001  logging::printf("==== Step 1: findMatches()\n");
1002  }
1003  mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
1004  kMaxMatches);
1005  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1006 
1007  // Step 2
1008  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1009  logging::printf("==== Step 2: createTransitions()\n");
1010  }
1011  createTransitions(mTransitionStorage, mMatches, mNumMatches);
1012  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1013 
1014  // Step 3
1015  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1016  logging::printf("==== Step 3: fixTransitionTimes()\n");
1017  }
1018  Transition** begin = mTransitionStorage.getActivePoolBegin();
1019  Transition** end = mTransitionStorage.getActivePoolEnd();
1020  fixTransitionTimes(begin, end);
1021  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1022 
1023  // Step 4
1024  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1025  logging::printf("==== Step 4: generateStartUntilTimes()\n");
1026  }
1027  generateStartUntilTimes(begin, end);
1028  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1029 
1030  // Step 5
1031  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1032  logging::printf("==== Step 5: calcAbbreviations()\n");
1033  }
1034  calcAbbreviations(begin, end);
1035  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) { log(); }
1036 
1037  mIsFilled = true;
1038  return true;
1039  }
1040 
1041  protected:
1049  uint8_t type,
1050  const BF* brokerFactory,
1051  uintptr_t zoneKey
1052  ) :
1053  ZoneProcessor(type),
1054  mBrokerFactory(brokerFactory)
1055  {
1056  setZoneKey(zoneKey);
1057  }
1058 
1059  private:
1060  friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
1061  friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
1062  friend class ::ExtendedZoneProcessorTest_createMatch;
1063  friend class ::ExtendedZoneProcessorTest_findMatches_simple;
1064  friend class ::ExtendedZoneProcessorTest_findMatches_named;
1065  friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
1066  friend class ::ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
1067  friend class ::ExtendedZoneProcessorTest_getTransitionTime;
1068  friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
1069  friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
1070  friend class ::ExtendedZoneProcessorTest_expandDateTuple;
1071  friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
1072  friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
1073  friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
1074  friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
1075  friend class ::ExtendedZoneProcessorTest_processTransitionMatchStatus;
1076  friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
1077  friend class ::ExtendedZoneProcessorTest_createAbbreviation;
1078  friend class ::ExtendedZoneProcessorTest_setZoneKey;
1079  friend class ::TransitionStorageTest_findTransitionForDateTime;
1080  friend class ::TransitionValidation;
1081 
1082  // Disable copy constructor and assignment operator.
1084  const ExtendedZoneProcessorTemplate&) = delete;
1085  ExtendedZoneProcessorTemplate& operator=(
1086  const ExtendedZoneProcessorTemplate&) = delete;
1087 
1092  static const uint8_t kMaxMatches = 4;
1093 
1098  static const uint8_t kMaxInteriorYears = 4;
1099 
1100  bool equals(const ZoneProcessor& other) const override {
1101  return mZoneInfoBroker.equals(
1102  ((const ExtendedZoneProcessorTemplate&) other).mZoneInfoBroker);
1103  }
1104 
1109  const Transition* findTransition(acetime_t epochSeconds) const {
1110  return mTransitionStorage.findTransition(epochSeconds);
1111  }
1112 
1114  bool isFilled(int16_t year) const {
1115  return mIsFilled && (year == mYear);
1116  }
1117 
1125  static uint8_t findMatches(
1126  const ZIB& zoneInfo,
1127  const extended::YearMonthTuple& startYm,
1128  const extended::YearMonthTuple& untilYm,
1129  MatchingEra* matches,
1130  uint8_t maxMatches
1131  ) {
1132  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1133  logging::printf("findMatches()\n");
1134  }
1135  uint8_t iMatch = 0;
1136  MatchingEra* prevMatch = nullptr;
1137  for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
1138  const ZEB era = zoneInfo.era(iEra);
1139  if (eraOverlapsInterval(prevMatch, era, startYm, untilYm)) {
1140  if (iMatch < maxMatches) {
1141  matches[iMatch] = createMatch(prevMatch, era, startYm, untilYm);
1142  prevMatch = &matches[iMatch];
1143  iMatch++;
1144  }
1145  }
1146  }
1147  return iMatch;
1148  }
1149 
1161  static bool eraOverlapsInterval(
1162  const MatchingEra* prevMatch,
1163  const ZEB& era,
1164  const extended::YearMonthTuple& startYm,
1165  const extended::YearMonthTuple& untilYm) {
1166  return (prevMatch == nullptr || compareEraToYearMonth(
1167  prevMatch->era, untilYm.yearTiny, untilYm.month) < 0)
1168  && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1169  }
1170 
1172  static int8_t compareEraToYearMonth(const ZEB& era,
1173  int8_t yearTiny, uint8_t month) {
1174  if (era.untilYearTiny() < yearTiny) return -1;
1175  if (era.untilYearTiny() > yearTiny) return 1;
1176  if (era.untilMonth() < month) return -1;
1177  if (era.untilMonth() > month) return 1;
1178  if (era.untilDay() > 1) return 1;
1179  //if (era.untilTimeMinutes() < 0) return -1; // never possible
1180  if (era.untilTimeMinutes() > 0) return 1;
1181  return 0;
1182  }
1183 
1190  static MatchingEra createMatch(
1191  MatchingEra* prevMatch,
1192  const ZEB& era,
1193  const extended::YearMonthTuple& startYm,
1194  const extended::YearMonthTuple& untilYm) {
1195 
1196  // If prevMatch is null, set startDate to be earlier than all valid
1197  // ZoneEra.
1198  extended::DateTuple startDate = (prevMatch == nullptr)
1199  ? extended::DateTuple{
1201  1,
1202  1,
1203  0,
1205  }
1206  : extended::DateTuple{
1207  prevMatch->era.untilYearTiny(),
1208  prevMatch->era.untilMonth(),
1209  prevMatch->era.untilDay(),
1210  (int16_t) prevMatch->era.untilTimeMinutes(),
1211  prevMatch->era.untilTimeSuffix()
1212  };
1213  extended::DateTuple lowerBound{
1214  startYm.yearTiny,
1215  startYm.month,
1216  1,
1217  0,
1219  };
1220  if (startDate < lowerBound) {
1221  startDate = lowerBound;
1222  }
1223 
1224  extended::DateTuple untilDate{
1225  era.untilYearTiny(),
1226  era.untilMonth(),
1227  era.untilDay(),
1228  (int16_t) era.untilTimeMinutes(),
1229  era.untilTimeSuffix()
1230  };
1231  extended::DateTuple upperBound{
1232  untilYm.yearTiny,
1233  untilYm.month,
1234  1,
1235  0,
1237  };
1238  if (upperBound < untilDate) {
1239  untilDate = upperBound;
1240  }
1241 
1242  return {startDate, untilDate, era, prevMatch, 0, 0};
1243  }
1244 
1249  static void createTransitions(
1250  TransitionStorage& transitionStorage,
1251  MatchingEra* matches,
1252  uint8_t numMatches) {
1253  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1254  logging::printf("createTransitions()\n");
1255  }
1256 
1257  for (uint8_t i = 0; i < numMatches; i++) {
1258  createTransitionsForMatch(transitionStorage, &matches[i]);
1259  }
1260  }
1261 
1263  static void createTransitionsForMatch(
1264  TransitionStorage& transitionStorage,
1265  MatchingEra* match) {
1266  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1267  logging::printf("== createTransitionsForMatch()\n");
1268  }
1269  const ZPB policy = match->era.zonePolicy();
1270  if (policy.isNull()) {
1271  createTransitionsFromSimpleMatch(transitionStorage, match);
1272  } else {
1273  createTransitionsFromNamedMatch(transitionStorage, match);
1274  }
1275  }
1276 
1277  static void createTransitionsFromSimpleMatch(
1278  TransitionStorage& transitionStorage,
1279  MatchingEra* match) {
1280  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1281  logging::printf("== createTransitionsFromSimpleMatch()\n");
1282  }
1283 
1284  Transition* freeTransition = transitionStorage.getFreeAgent();
1285  createTransitionForYear(freeTransition, 0 /*not used*/,
1286  ZRB() /*rule*/, match);
1287  freeTransition->matchStatus = extended::MatchStatus::kExactMatch;
1288  match->lastOffsetMinutes = freeTransition->offsetMinutes;
1289  match->lastDeltaMinutes = freeTransition->deltaMinutes;
1290  transitionStorage.addFreeAgentToActivePool();
1291  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1292  freeTransition->log();
1293  logging::printf("\n");
1294  }
1295  }
1296 
1297  static void createTransitionsFromNamedMatch(
1298  TransitionStorage& transitionStorage,
1299  MatchingEra* match) {
1300  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1301  logging::printf("== createTransitionsFromNamedMatch()\n");
1302  }
1303 
1304  transitionStorage.resetCandidatePool();
1305  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1306  match->log(); logging::printf("\n");
1307  }
1308 
1309  // Pass 1: Find candidate transitions using whole years.
1310  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1311  logging::printf("---- Pass 1: findCandidateTransitions()\n");
1312  }
1313  findCandidateTransitions(transitionStorage, match);
1314  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1315  transitionStorage.log();
1316  }
1317 
1318  // Pass 2: Fix the transitions times, converting 's' and 'u' into 'w'
1319  // uniformly.
1320  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1321  logging::printf("---- Pass 2: fixTransitionTimes()\n");
1322  }
1323  fixTransitionTimes(
1324  transitionStorage.getCandidatePoolBegin(),
1325  transitionStorage.getCandidatePoolEnd());
1326 
1327  // Pass 3: Select only those Transitions which overlap with the actual
1328  // start and until times of the MatchingEra.
1329  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1330  logging::printf("---- Pass 3: selectActiveTransitions()\n");
1331  }
1332  selectActiveTransitions(
1333  transitionStorage.getCandidatePoolBegin(),
1334  transitionStorage.getCandidatePoolEnd());
1335  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1336  transitionStorage.log();
1337  }
1338  Transition* lastTransition =
1339  transitionStorage.addActiveCandidatesToActivePool();
1340  match->lastOffsetMinutes = lastTransition->offsetMinutes;
1341  match->lastDeltaMinutes = lastTransition->deltaMinutes;
1342  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1343  transitionStorage.log();
1344  }
1345  }
1346 
1347  static void findCandidateTransitions(
1348  TransitionStorage& transitionStorage,
1349  const MatchingEra* match) {
1350  using extended::MatchStatus;
1351 
1352  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1353  logging::printf("findCandidateTransitions(): \n");
1354  match->log();
1355  logging::printf("\n");
1356  }
1357  const ZPB policy = match->era.zonePolicy();
1358  uint8_t numRules = policy.numRules();
1359  int8_t startY = match->startDateTime.yearTiny;
1360  int8_t endY = match->untilDateTime.yearTiny;
1361 
1362  // The prior is referenced through a handle (i.e. pointer to pointer)
1363  // because the actual pointer to the prior could change through the
1364  // transitionStorage.setFreeAgentAsPriorIfValid() method.
1365  Transition** prior = transitionStorage.reservePrior();
1366  (*prior)->isValidPrior = false; // indicates "no prior transition"
1367  for (uint8_t r = 0; r < numRules; r++) {
1368  const ZRB rule = policy.rule(r);
1369 
1370  // Add Transitions for interior years
1371  int8_t interiorYears[kMaxInteriorYears];
1372  uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1373  rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1374  for (uint8_t y = 0; y < numYears; y++) {
1375  int8_t year = interiorYears[y];
1376  Transition* t = transitionStorage.getFreeAgent();
1377  createTransitionForYear(t, year, rule, match);
1378  MatchStatus status = compareTransitionToMatchFuzzy(t, match);
1379  if (status == MatchStatus::kPrior) {
1380  transitionStorage.setFreeAgentAsPriorIfValid();
1381  } else if (status == MatchStatus::kWithinMatch) {
1382  transitionStorage.addFreeAgentToCandidatePool();
1383  } else {
1384  // Must be kFarFuture.
1385  // Do nothing, allowing the free agent to be reused.
1386  }
1387  }
1388 
1389  // Add Transition for prior year
1390  int8_t priorYear = getMostRecentPriorYear(
1391  rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1392  if (priorYear != LocalDate::kInvalidYearTiny) {
1393  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1394  logging::printf(
1395  "findCandidateTransitions(): priorYear: %d\n",
1396  priorYear + LocalDate::kEpochYear);
1397  }
1398  Transition* t = transitionStorage.getFreeAgent();
1399  createTransitionForYear(t, priorYear, rule, match);
1400  transitionStorage.setFreeAgentAsPriorIfValid();
1401  }
1402  }
1403 
1404  // Add the reserved prior into the Candidate pool only if 'isValidPrior'
1405  // is true.
1406  if ((*prior)->isValidPrior) {
1407  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1408  logging::printf(
1409  "findCandidateTransitions(): adding prior to Candidate pool\n");
1410  (*prior)->log();
1411  logging::printf("\n");
1412  }
1413  transitionStorage.addPriorToCandidatePool();
1414  }
1415  }
1416 
1436  static uint8_t calcInteriorYears(
1437  int8_t* interiorYears,
1438  uint8_t maxInteriorYears,
1439  int8_t fromYear, int8_t toYear,
1440  int8_t startYear, int8_t endYear) {
1441  uint8_t i = 0;
1442  for (int8_t year = startYear; year <= endYear; year++) {
1443  if (fromYear <= year && year <= toYear) {
1444  interiorYears[i] = year;
1445  i++;
1446  if (i >= maxInteriorYears) break;
1447  }
1448  }
1449  return i;
1450  }
1451 
1458  static void createTransitionForYear(
1459  Transition* t,
1460  int8_t year,
1461  const ZRB& rule,
1462  const MatchingEra* match) {
1463  t->match = match;
1464  t->rule = rule;
1465  t->offsetMinutes = match->era.offsetMinutes();
1466  t->letterBuf[0] = '\0';
1467 
1468  if (! rule.isNull()) {
1469  t->transitionTime = getTransitionTime(year, rule);
1470  t->deltaMinutes = rule.deltaMinutes();
1471 
1472  char letter = rule.letter();
1473  if (letter >= 32) {
1474  // If LETTER is a '-', treat it the same as an empty string.
1475  if (letter != '-') {
1476  t->letterBuf[0] = letter;
1477  t->letterBuf[1] = '\0';
1478  }
1479  } else {
1480  // rule->letter is a long string, so is referenced as an offset index
1481  // into the ZonePolicy.letters array. The string cannot fit in
1482  // letterBuf, so will be retrieved by the letter() method below.
1483  }
1484  } else {
1485  // Create a Transition using the MatchingEra for the transitionTime.
1486  // Used for simple MatchingEra.
1487  t->transitionTime = match->startDateTime;
1488  t->deltaMinutes = match->era.deltaMinutes();
1489  }
1490  }
1491 
1504  static int8_t getMostRecentPriorYear(
1505  int8_t fromYear, int8_t toYear,
1506  int8_t startYear, int8_t endYear) {
1507 
1508  (void) endYear; // disable compiler warnings
1509 
1510  if (fromYear < startYear) {
1511  if (toYear < startYear) {
1512  return toYear;
1513  } else {
1514  return startYear - 1;
1515  }
1516  } else {
1518  }
1519  }
1520 
1525  static extended::DateTuple getTransitionTime(
1526  int8_t yearTiny, const ZRB& rule) {
1527 
1528  internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1529  yearTiny + LocalDate::kEpochYear,
1530  rule.inMonth(),
1531  rule.onDayOfWeek(),
1532  rule.onDayOfMonth());
1533  return {
1534  yearTiny,
1535  monthDay.month,
1536  monthDay.day,
1537  (int16_t) rule.atTimeMinutes(),
1538  rule.atTimeSuffix()
1539  };
1540  }
1541 
1552  static extended::MatchStatus compareTransitionToMatchFuzzy(
1553  const Transition* t, const MatchingEra* match) {
1554  using extended::MatchStatus;
1555 
1556  int16_t ttMonths = t->transitionTime.yearTiny * 12
1557  + t->transitionTime.month;
1558 
1559  int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1560  + match->startDateTime.month;
1561  if (ttMonths < matchStartMonths - 1) return MatchStatus::kPrior;
1562 
1563  int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1564  + match->untilDateTime.month;
1565  if (matchUntilMonths + 2 <= ttMonths) return MatchStatus::kFarFuture;
1566 
1567  return MatchStatus::kWithinMatch;
1568  }
1569 
1578  static void fixTransitionTimes(Transition** begin, Transition** end) {
1579  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1580  logging::printf("fixTransitionTimes(): START; #transitions=%d\n",
1581  (int) (end - begin));
1582  printTransitions(begin, end);
1583  }
1584 
1585  // extend first Transition to -infinity
1586  Transition* prev = *begin;
1587 
1588  for (Transition** iter = begin; iter != end; ++iter) {
1589  Transition* curr = *iter;
1590  expandDateTuple(
1591  &curr->transitionTime,
1592  prev->offsetMinutes,
1593  prev->deltaMinutes,
1594  &curr->transitionTime,
1595  &curr->transitionTimeS,
1596  &curr->transitionTimeU);
1597  prev = curr;
1598  }
1599  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1600  logging::printf("fixTransitionTimes(): FIXED\n");
1601  printTransitions(begin, end);
1602  logging::printf("fixTransitionTimes(): END\n");
1603  }
1604  }
1605 
1606  #ifdef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1607  static void printTransitions(Transition** begin, Transition** end) {
1608  for (Transition** iter = begin; iter != end; ++iter) {
1609  (*iter)->log();
1610  logging::printf("\n");
1611  }
1612  }
1613  #endif
1614 
1620  static void expandDateTuple(
1621  const extended::DateTuple* tt,
1622  int16_t offsetMinutes,
1623  int16_t deltaMinutes,
1624  extended::DateTuple* ttw,
1625  extended::DateTuple* tts,
1626  extended::DateTuple* ttu) {
1627 
1628  if (tt->suffix == internal::ZoneContext::kSuffixS) {
1629  *tts = *tt;
1630  *ttu = {tt->yearTiny, tt->month, tt->day,
1631  (int16_t) (tt->minutes - offsetMinutes),
1633  *ttw = {tt->yearTiny, tt->month, tt->day,
1634  (int16_t) (tt->minutes + deltaMinutes),
1636  } else if (tt->suffix == internal::ZoneContext::kSuffixU) {
1637  *ttu = *tt;
1638  *tts = {tt->yearTiny, tt->month, tt->day,
1639  (int16_t) (tt->minutes + offsetMinutes),
1641  *ttw = {tt->yearTiny, tt->month, tt->day,
1642  (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1644  } else {
1645  // Explicit set the suffix to 'w' in case it was something else.
1646  *ttw = *tt;
1647  ttw->suffix = internal::ZoneContext::kSuffixW;
1648  *tts = {tt->yearTiny, tt->month, tt->day,
1649  (int16_t) (tt->minutes - deltaMinutes),
1651  *ttu = {tt->yearTiny, tt->month, tt->day,
1652  (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1654  }
1655 
1656  normalizeDateTuple(ttw);
1657  normalizeDateTuple(tts);
1658  normalizeDateTuple(ttu);
1659  }
1660 
1665  static void normalizeDateTuple(extended::DateTuple* dt) {
1666  const int16_t kOneDayAsMinutes = 60 * 24;
1667  if (dt->minutes <= -kOneDayAsMinutes) {
1668  LocalDate ld = LocalDate::forTinyComponents(
1669  dt->yearTiny, dt->month, dt->day);
1670  local_date_mutation::decrementOneDay(ld);
1671  dt->yearTiny = ld.yearTiny();
1672  dt->month = ld.month();
1673  dt->day = ld.day();
1674  dt->minutes += kOneDayAsMinutes;
1675  } else if (kOneDayAsMinutes <= dt->minutes) {
1676  LocalDate ld = LocalDate::forTinyComponents(
1677  dt->yearTiny, dt->month, dt->day);
1678  local_date_mutation::incrementOneDay(ld);
1679  dt->yearTiny = ld.yearTiny();
1680  dt->month = ld.month();
1681  dt->day = ld.day();
1682  dt->minutes -= kOneDayAsMinutes;
1683  } else {
1684  // do nothing
1685  }
1686  }
1687 
1692  static void selectActiveTransitions(Transition** begin, Transition** end) {
1693  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1694  logging::printf("selectActiveTransitions(): #candidates: %d\n",
1695  (int) (end - begin));
1696  }
1697 
1698  Transition* prior = nullptr;
1699  for (Transition** iter = begin; iter != end; ++iter) {
1700  Transition* transition = *iter;
1701  processTransitionMatchStatus(transition, &prior);
1702  }
1703 
1704  // If the latest prior transition is found, shift it to start at the
1705  // startDateTime of the current match.
1706  if (prior) {
1707  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1708  logging::printf(
1709  "selectActiveTransitions(): found latest prior\n");
1710  }
1711  #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1712  prior->originalTransitionTime = prior->transitionTime;
1713  #endif
1714  prior->transitionTime = prior->match->startDateTime;
1715  }
1716  }
1717 
1724  static void processTransitionMatchStatus(
1725  Transition* transition,
1726  Transition** prior) {
1727  using extended::MatchStatus;
1728 
1729  MatchStatus status = compareTransitionToMatch(
1730  transition, transition->match);
1731  transition->matchStatus = status;
1732 
1733  if (status == MatchStatus::kExactMatch) {
1734  if (*prior) {
1735  (*prior)->matchStatus = MatchStatus::kFarPast;
1736  }
1737  (*prior) = transition;
1738  } else if (status == MatchStatus::kPrior) {
1739  if (*prior) {
1740  if ((*prior)->transitionTimeU <= transition->transitionTimeU) {
1741  (*prior)->matchStatus = MatchStatus::kFarPast;
1742  (*prior) = transition;
1743  } else {
1744  transition->matchStatus = MatchStatus::kFarPast;
1745  }
1746  } else {
1747  (*prior) = transition;
1748  }
1749  }
1750  }
1751 
1760  static extended::MatchStatus compareTransitionToMatch(
1761  const Transition* transition,
1762  const MatchingEra* match) {
1763 
1764  // Find the previous Match offsets.
1765  int16_t prevMatchOffsetMinutes;
1766  int16_t prevMatchDeltaMinutes;
1767  if (match->prevMatch) {
1768  prevMatchOffsetMinutes = match->prevMatch->lastOffsetMinutes;
1769  prevMatchDeltaMinutes = match->prevMatch->lastDeltaMinutes;
1770  } else {
1771  prevMatchOffsetMinutes = match->era.offsetMinutes();
1772  prevMatchDeltaMinutes = 0;
1773  }
1774 
1775  // Expand start times.
1776  extended::DateTuple stw;
1777  extended::DateTuple sts;
1778  extended::DateTuple stu;
1779  expandDateTuple(
1780  &match->startDateTime,
1781  prevMatchOffsetMinutes,
1782  prevMatchDeltaMinutes,
1783  &stw,
1784  &sts,
1785  &stu);
1786 
1787  // Transition times.
1788  const extended::DateTuple& ttw = transition->transitionTime;
1789  const extended::DateTuple& tts = transition->transitionTimeS;
1790  const extended::DateTuple& ttu = transition->transitionTimeU;
1791 
1792  // Compare Transition to Match, where equality is assumed if *any* of the
1793  // 'w', 's', or 'u' versions of the DateTuple are equal. This prevents
1794  // duplicate Transition instances from being created in a few cases.
1795  if (ttw == stw || tts == sts || ttu == stu) {
1796  return extended::MatchStatus::kExactMatch;
1797  }
1798 
1799  if (ttu < stu) {
1800  return extended::MatchStatus::kPrior;
1801  }
1802 
1803  // Now check if the transition occurs after the given match. The
1804  // untilDateTime of the current match uses the same UTC offsets as the
1805  // transitionTime of the current transition, so no complicated adjustments
1806  // are needed. We just make sure we compare 'w' with 'w', 's' with 's',
1807  // and 'u' with 'u'.
1808  const extended::DateTuple& matchUntil = match->untilDateTime;
1809  const extended::DateTuple* transitionTime;
1810  if (matchUntil.suffix == internal::ZoneContext::kSuffixS) {
1811  transitionTime = &tts;
1812  } else if (matchUntil.suffix == internal::ZoneContext::kSuffixU) {
1813  transitionTime = &ttu;
1814  } else { // assume 'w'
1815  transitionTime = &ttw;
1816  }
1817  if (*transitionTime < matchUntil) {
1818  return extended::MatchStatus::kWithinMatch;
1819  }
1820  return extended::MatchStatus::kFarFuture;
1821  }
1822 
1828  static void generateStartUntilTimes(Transition** begin, Transition** end) {
1829  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1830  logging::printf(
1831  "generateStartUntilTimes(): #transitions=%d\n",
1832  (int) (end - begin));
1833  }
1834 
1835  Transition* prev = *begin;
1836  bool isAfterFirst = false;
1837 
1838  for (Transition** iter = begin; iter != end; ++iter) {
1839  Transition* const t = *iter;
1840 
1841  // 1) Update the untilDateTime of the previous Transition
1842  const extended::DateTuple& tt = t->transitionTime;
1843  if (isAfterFirst) {
1844  prev->untilDateTime = tt;
1845  }
1846 
1847  // 2) Calculate the current startDateTime by shifting the
1848  // transitionTime (represented in the UTC offset of the previous
1849  // transition) into the UTC offset of the *current* transition.
1850  int16_t minutes = tt.minutes + (
1851  - prev->offsetMinutes - prev->deltaMinutes
1852  + t->offsetMinutes + t->deltaMinutes);
1853  t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
1854  tt.suffix};
1855  normalizeDateTuple(&t->startDateTime);
1856 
1857  // 3) The epochSecond of the 'transitionTime' is determined by the
1858  // UTC offset of the *previous* Transition. However, the
1859  // transitionTime can be represented by an illegal time (e.g. 24:00).
1860  // So, it is better to use the properly normalized startDateTime
1861  // (calculated above) with the *current* UTC offset.
1862  //
1863  // NOTE: We should also be able to calculate this directly from
1864  // 'transitionTimeU' which should still be a valid field, because it
1865  // hasn't been clobbered by 'untilDateTime' yet. Not sure if this saves
1866  // any CPU time though, since we still need to mutiply by 900.
1867  const extended::DateTuple& st = t->startDateTime;
1868  const acetime_t offsetSeconds = (acetime_t) 60
1869  * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
1870  LocalDate ld = LocalDate::forTinyComponents(
1871  st.yearTiny, st.month, st.day);
1872  t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1873 
1874  prev = t;
1875  isAfterFirst = true;
1876  }
1877 
1878  // The last Transition's until time is the until time of the MatchingEra.
1879  extended::DateTuple untilTimeW;
1880  extended::DateTuple untilTimeS;
1881  extended::DateTuple untilTimeU;
1882  expandDateTuple(
1883  &prev->match->untilDateTime,
1884  prev->offsetMinutes,
1885  prev->deltaMinutes,
1886  &untilTimeW,
1887  &untilTimeS,
1888  &untilTimeU);
1889  prev->untilDateTime = untilTimeW;
1890  }
1891 
1895  static void calcAbbreviations(Transition** begin, Transition** end) {
1896  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1897  logging::printf("calcAbbreviations(): #transitions: %d\n",
1898  (int) (end - begin));
1899  }
1900  for (Transition** iter = begin; iter != end; ++iter) {
1901  Transition* const t = *iter;
1902  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1903  logging::printf(
1904  "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1905  t->format(), t->deltaMinutes, t->letter());
1906  }
1907  createAbbreviation(
1908  t->abbrev,
1909  internal::kAbbrevSize,
1910  t->format(),
1911  t->deltaMinutes,
1912  t->letter());
1913  }
1914  }
1915 
1924  static void createAbbreviation(
1925  char* dest,
1926  uint8_t destSize,
1927  const char* format,
1928  uint16_t deltaMinutes,
1929  const char* letterString) {
1930 
1931  // Check if FORMAT contains a '%'.
1932  if (strchr(format, '%') != nullptr) {
1933  // Check if RULES column empty, therefore no 'letter'
1934  if (letterString == nullptr) {
1935  strncpy(dest, format, destSize - 1);
1936  dest[destSize - 1] = '\0';
1937  } else {
1938  ace_common::copyReplaceString(
1939  dest, destSize, format, '%', letterString);
1940  }
1941  } else {
1942  // Check if FORMAT contains a '/'.
1943  const char* slashPos = strchr(format, '/');
1944  if (slashPos != nullptr) {
1945  if (deltaMinutes == 0) {
1946  uint8_t headLength = (slashPos - format);
1947  if (headLength >= destSize) headLength = destSize - 1;
1948  memcpy(dest, format, headLength);
1949  dest[headLength] = '\0';
1950  } else {
1951  uint8_t tailLength = strlen(slashPos+1);
1952  if (tailLength >= destSize) tailLength = destSize - 1;
1953  memcpy(dest, slashPos+1, tailLength);
1954  dest[tailLength] = '\0';
1955  }
1956  } else {
1957  // Just copy the FORMAT disregarding deltaMinutes and letterString.
1958  strncpy(dest, format, destSize);
1959  dest[destSize - 1] = '\0';
1960  }
1961  }
1962  }
1963 
1964  const BF* mBrokerFactory;
1965  ZIB mZoneInfoBroker;
1966 
1967  mutable int16_t mYear = 0; // maybe create LocalDate::kInvalidYear?
1968  mutable bool mIsFilled = false;
1969  // NOTE: Maybe move mNumMatches and mMatches into a MatchStorage object.
1970  mutable uint8_t mNumMatches = 0; // actual number of matches
1971  mutable MatchingEra mMatches[kMaxMatches];
1972  mutable TransitionStorage mTransitionStorage;
1973 };
1974 
1975 
1981  extended::BrokerFactory,
1982  extended::ZoneInfoBroker,
1983  extended::ZoneEraBroker,
1984  extended::ZonePolicyBroker,
1985  extended::ZoneRuleBroker> {
1986 
1987  public:
1989  static const uint8_t kTypeExtended = 4;
1990 
1991  explicit ExtendedZoneProcessor(const extended::ZoneInfo* zoneInfo = nullptr)
1993  extended::BrokerFactory,
1994  extended::ZoneInfoBroker,
1995  extended::ZoneEraBroker,
1996  extended::ZonePolicyBroker,
1997  extended::ZoneRuleBroker>(
1998  kTypeExtended, &mBrokerFactory, (uintptr_t) zoneInfo)
1999  {}
2000 
2001  private:
2002  extended::BrokerFactory mBrokerFactory;
2003 };
2004 
2005 } // namespace ace_time
2006 
2007 #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:971
ace_time::extended::TransitionStorageTemplate::init
void init()
Initialize all pools to 0 size, usually when a new year is initialized.
Definition: ExtendedZoneProcessor.h:448
ace_time::extended::TransitionStorageTemplate::getPrior
Transition * getPrior()
Return the current prior transition.
Definition: ExtendedZoneProcessor.h:458
ace_time::extended::TransitionStorageTemplate::reservePrior
Transition ** reservePrior()
Allocate a free Transition then add it to the Prior pool.
Definition: ExtendedZoneProcessor.h:532
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:557
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:494
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::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:817
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:1989
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::ExtendedZoneProcessorTemplate::getTransitionAllocSize
uint8_t getTransitionAllocSize() const
Get the largest allocation size of TransitionStorage.
Definition: ExtendedZoneProcessor.h:928
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:1980
ace_time::extended::TransitionStorageTemplate::resetCandidatePool
void resetCandidatePool()
Empty the Candidate pool by resetting the various indexes.
Definition: ExtendedZoneProcessor.h:470
ace_time::ExtendedZoneProcessorTemplate::TransitionStorage
extended::TransitionStorageTemplate< kMaxTransitions, ZEB, ZPB, ZRB > TransitionStorage
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:815
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:836
ace_time::ExtendedZoneProcessorTemplate::MatchingEra
extended::MatchingEraTemplate< ZEB > MatchingEra
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:811
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:1048
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:819
ace_time::ExtendedZoneProcessorTemplate::printShortNameTo
void printShortNameTo(Print &printer) const override
Print a short human-readable identifier (e.g.
Definition: ExtendedZoneProcessor.h:905
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:664
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 all Zones supported by this class.
Definition: ExtendedZoneProcessor.h:805
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:843
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:480
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:952
ace_time::extended::TransitionStorageTemplate::addActiveCandidatesToActivePool
Transition * addActiveCandidatesToActivePool()
Add active candidates into the Active pool, and collapse the Candidate pool.
Definition: ExtendedZoneProcessor.h:593
ace_time::ExtendedZoneProcessorTemplate::initForEpochSeconds
bool initForEpochSeconds(acetime_t epochSeconds) const
Initialize using the epochSeconds.
Definition: ExtendedZoneProcessor.h:961
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::extended::TransitionStorageTemplate::resetAllocSize
void resetAllocSize()
Reset the current allocation size.
Definition: ExtendedZoneProcessor.h:724
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:541
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:932
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:942
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:910
ace_time::extended::TransitionStorageTemplate::addFreeAgentToActivePool
void addFreeAgentToActivePool()
Immediately add the free agent Transition at index mIndexFree to the Active pool.
Definition: ExtendedZoneProcessor.h:517
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:901
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:829
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::extended::TransitionStorageTemplate::getAllocSize
uint8_t getAllocSize() const
Return the maximum number of transitions which was allocated.
Definition: ExtendedZoneProcessor.h:731
ace_time::ExtendedZoneProcessorTemplate::Transition
extended::TransitionTemplate< ZEB, ZPB, ZRB > Transition
Exposed only for testing purposes.
Definition: ExtendedZoneProcessor.h:808
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:567
ace_time::extended::TransitionStorageTemplate::findTransition
const Transition * findTransition(acetime_t epochSeconds) const
Return the Transition matching the given epochSeconds.
Definition: ExtendedZoneProcessor.h:627
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:793
ace_time::extended::TransitionTemplate::logHourMinute
static void logHourMinute(int16_t minutes)
Print minutes as [+/-]hh:mm.
Definition: ExtendedZoneProcessor.h:385
ace_time::ExtendedZoneProcessorTemplate::resetTransitionAllocSize
void resetTransitionAllocSize()
Reset the TransitionStorage high water mark.
Definition: ExtendedZoneProcessor.h:923
ace_time::extended::TransitionStorageTemplate::log
void log() const
Verify that the indexes are valid.
Definition: ExtendedZoneProcessor.h:691