AceTime  2.1.0
Date and time classes for Arduino that support timezones from the TZ Database.
Transition.h
1 /*
2  * MIT License
3  * Copyright (c) 2019 Brian T. Park
4  */
5 
6 #ifndef ACE_TIME_EXTENDED_TRANSITION_H
7 #define ACE_TIME_EXTENDED_TRANSITION_H
8 
9 #include <stdint.h> // uintptr_t
10 #include "common/logging.h"
11 #include "local_date_mutation.h"
12 
13 class TransitionStorageTest_getFreeAgent;
14 class TransitionStorageTest_getFreeAgent2;
15 class TransitionStorageTest_addFreeAgentToActivePool;
16 class TransitionStorageTest_reservePrior;
17 class TransitionStorageTest_addPriorToCandidatePool;
18 class TransitionStorageTest_addFreeAgentToCandidatePool;
19 class TransitionStorageTest_setFreeAgentAsPriorIfValid;
20 class TransitionStorageTest_addActiveCandidatesToActivePool;
21 class TransitionStorageTest_findTransitionForDateTime;
22 class TransitionStorageTest_resetCandidatePool;
23 
24 class Print;
25 
26 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
27 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG 0
28 #endif
29 
30 namespace ace_time {
31 namespace extended {
32 
33 //---------------------------------------------------------------------------
34 
39 enum class MatchStatus : uint8_t {
40  kFarPast, // 0
41  kPrior, // 1
42  kExactMatch, // 2
43  kWithinMatch, // 3
44  kFarFuture, // 4
45 };
46 
47 inline bool isMatchStatusActive(MatchStatus status) {
48  return status == MatchStatus::kExactMatch
49  || status == MatchStatus::kWithinMatch
50  || status == MatchStatus::kPrior;
51 }
52 
53 //---------------------------------------------------------------------------
54 
59 struct DateTuple {
60  DateTuple() = default;
61 
62  DateTuple(int16_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
63  year(y), month(mon), day(d), suffix(mod), minutes(min) {}
64 
65  int16_t year; // [-1,10000]
66  uint8_t month; // [1,12]
67  uint8_t day; // [1,31]
68  uint8_t suffix; // kSuffixS, kSuffixW, kSuffixU
69  int16_t minutes; // negative values allowed
70 
72  void log() const {
73  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
74  int hour = minutes / 60;
75  int minute = minutes - hour * 60;
76  char c = "wsu"[(suffix>>4)];
77  logging::printf("%04d-%02u-%02uT%02d:%02d%c",
78  year, month, day, hour, minute, c);
79  }
80  }
81 };
82 
84 inline bool operator<(const DateTuple& a, const DateTuple& b) {
85  if (a.year < b.year) return true;
86  if (a.year > b.year) return false;
87  if (a.month < b.month) return true;
88  if (a.month > b.month) return false;
89  if (a.day < b.day) return true;
90  if (a.day > b.day) return false;
91  if (a.minutes < b.minutes) return true;
92  if (a.minutes > b.minutes) return false;
93  return false;
94 }
95 
96 inline bool operator>=(const DateTuple& a, const DateTuple& b) {
97  return ! (a < b);
98 }
99 
100 inline bool operator<=(const DateTuple& a, const DateTuple& b) {
101  return ! (b < a);
102 }
103 
104 inline bool operator>(const DateTuple& a, const DateTuple& b) {
105  return (b < a);
106 }
107 
109 inline bool operator==(const DateTuple& a, const DateTuple& b) {
110  return a.year == b.year
111  && a.month == b.month
112  && a.day == b.day
113  && a.minutes == b.minutes
114  && a.suffix == b.suffix;
115 }
116 
118 inline void normalizeDateTuple(DateTuple* dt) {
119  const int16_t kOneDayAsMinutes = 60 * 24;
120  if (dt->minutes <= -kOneDayAsMinutes) {
121  LocalDate ld = LocalDate::forComponents(dt->year, dt->month, dt->day);
122  local_date_mutation::decrementOneDay(ld);
123  dt->year = ld.year();
124  dt->month = ld.month();
125  dt->day = ld.day();
126  dt->minutes += kOneDayAsMinutes;
127  } else if (kOneDayAsMinutes <= dt->minutes) {
128  LocalDate ld = LocalDate::forComponents(dt->year, dt->month, dt->day);
129  local_date_mutation::incrementOneDay(ld);
130  dt->year = ld.year();
131  dt->month = ld.month();
132  dt->day = ld.day();
133  dt->minutes -= kOneDayAsMinutes;
134  } else {
135  // do nothing
136  }
137 }
138 
145 inline acetime_t subtractDateTuple(const DateTuple& a, const DateTuple& b) {
146  int32_t epochDaysA = LocalDate::forComponents(
147  a.year, a.month, a.day).toEpochDays();
148 
149  int32_t epochDaysB = LocalDate::forComponents(
150  b.year, b.month, b.day).toEpochDays();
151 
152  // Perform the subtraction of the days first, before converting to seconds, to
153  // prevent overflow if a.year or b.year is more than 68 years from the current
154  // epoch year.
155  return (epochDaysA - epochDaysB) * 86400 + (a.minutes - b.minutes) * 60;
156 }
157 
170 inline MatchStatus compareDateTupleFuzzy(
171  const DateTuple& t,
172  const DateTuple& start,
173  const DateTuple& until) {
174  // Use int32_t because a delta year of 2730 or greater will exceed
175  // the range of an int16_t.
176  int32_t tMonths = t.year * (int32_t) 12 + t.month;
177  int32_t startMonths = start.year * (int32_t) 12 + start.month;
178  if (tMonths < startMonths - 1) return MatchStatus::kPrior;
179  int32_t untilMonths = until.year * 12 + until.month;
180  if (untilMonths + 1 < tMonths) return MatchStatus::kFarFuture;
181  return MatchStatus::kWithinMatch;
182 }
183 
184 //---------------------------------------------------------------------------
185 
192 template<typename ZEB>
199 
202 
204  ZEB era;
205 
208 
211 
214 
215  void log() const {
216  logging::printf("MatchingEra(");
217  logging::printf("start="); startDateTime.log();
218  logging::printf("; until="); untilDateTime.log();
219  logging::printf("; era=%c", (era.isNull()) ? '-' : '*');
220  logging::printf("; prevMatch=%c", (prevMatch) ? '*' : '-');
221  logging::printf(")");
222  }
223 };
224 
225 //---------------------------------------------------------------------------
226 
259 template <typename ZEB, typename ZPB, typename ZRB>
261 
264 
270  ZRB rule;
271 
279 
280  union {
287 
293  };
294 
295  union {
302 
308  };
309 
310 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
315  DateTuple originalTransitionTime;
316 #endif
317 
320 
328  int16_t offsetMinutes;
329 
331  int16_t deltaMinutes;
332 
334  char abbrev[internal::kAbbrevSize];
335 
337  char letterBuf[2];
338 
339  union {
347 
352  MatchStatus matchStatus;
353  };
354 
355  //-------------------------------------------------------------------------
356 
357  const char* format() const {
358  return match->era.format();
359  }
360 
366  const char* letter() const {
367  // RULES column is '-' or hh:mm, so return nullptr to indicate this.
368  if (rule.isNull()) {
369  return nullptr;
370  }
371 
372  // RULES point to a named rule, and LETTER is a single, printable character.
373  // Return the letterBuf which contains a NUL-terminated string containing
374  // the single character, as initialized in createTransitionForYear().
375  char letter = rule.letter();
376  if (letter >= 32) {
377  return letterBuf;
378  }
379 
380  // RULES points to a named rule, and the LETTER is a string. The
381  // rule->letter is a non-printable number < 32, which is an index into
382  // a list of strings given by match->era->zonePolicy->letters[].
383  const ZPB policy = match->era.zonePolicy();
384  uint8_t numLetters = policy.numLetters();
385  if (letter >= numLetters) {
386  // This should never happen unless there is a programming error. If it
387  // does, return an empty string. (createTransitionForYear() sets
388  // letterBuf to a NUL terminated empty string if rule->letter < 32)
389  return letterBuf;
390  }
391 
392  // Return the string at index 'rule->letter'.
393  return policy.letter(letter);
394  }
395 
397  void log() const {
398  logging::printf("Transition(");
399  if (sizeof(acetime_t) <= sizeof(int)) {
400  logging::printf("start=%d", startEpochSeconds);
401  } else {
402  logging::printf("start=%ld", startEpochSeconds);
403  }
404  logging::printf("; status=%d", matchStatus);
405  logging::printf("; UTC");
408  logging::printf("; tt="); transitionTime.log();
409  logging::printf("; tts="); transitionTimeS.log();
410  logging::printf("; ttu="); transitionTimeU.log();
411  if (rule.isNull()) {
412  logging::printf("; rule=-");
413  } else {
414  logging::printf("; rule=");
415  logging::printf("[%d,%d]", rule.fromYear(), rule.toYear());
416  }
417  }
418 
420  static void logHourMinute(int16_t minutes) {
421  char sign;
422  if (minutes < 0) {
423  sign = '-';
424  minutes = -minutes;
425  } else {
426  sign = '+';
427  }
428  uint8_t hour = minutes / 60;
429  uint8_t minute = minutes - hour * 60;
430  logging::printf("%c%02u:%02u", sign, (unsigned) hour, (unsigned) minute);
431  }
432 
433 #ifdef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
435  static void printTransitions(
436  const char* prefix,
437  const TransitionTemplate* const* begin,
438  const TransitionTemplate* const* end) {
439  for (const TransitionTemplate* const* iter = begin; iter != end; ++iter) {
440  logging::printf(prefix);
441  (*iter)->log();
442  logging::printf("\n");
443  }
444  }
445 #endif
446 };
447 
455 template <typename ZEB, typename ZPB, typename ZRB>
459 
461  uint8_t fold;
462 
469  uint8_t num;
470 };
471 
485 template <typename ZEB, typename ZPB, typename ZRB>
493 
495  uint8_t num;
496 };
497 
529 template<uint8_t SIZE, typename ZEB, typename ZPB, typename ZRB>
531  public:
537 
544 
551 
554 
561  void init() {
562  for (uint8_t i = 0; i < SIZE; i++) {
563  mTransitions[i] = &mPool[i];
564  }
565  mIndexPrior = 0;
566  mIndexCandidates = 0;
567  mIndexFree = 0;
568  }
569 
572  return mTransitions[mIndexPrior];
573  }
574 
584  mIndexCandidates = mIndexPrior;
585  mIndexFree = mIndexPrior;
586  }
587 
588  Transition** getCandidatePoolBegin() {
589  return &mTransitions[mIndexCandidates];
590  }
591  Transition** getCandidatePoolEnd() {
592  return &mTransitions[mIndexFree];
593  }
594 
595  Transition** getActivePoolBegin() {
596  return &mTransitions[0];
597  }
598  Transition** getActivePoolEnd() {
599  return &mTransitions[mIndexFree];
600  }
601 
608  if (mIndexFree < SIZE) {
609  // Allocate a free transition.
610  if (mIndexFree >= mAllocSize) {
611  mAllocSize = mIndexFree + 1;
612  }
613  return mTransitions[mIndexFree];
614  } else {
615  // No more transition available in the buffer, so just return the last
616  // one. This will probably cause a bug in the timezone calculations, but
617  // I think this is better than triggering undefined behavior by running
618  // off the end of the mTransitions buffer.
619  return mTransitions[SIZE - 1];
620  }
621  }
622 
631  if (mIndexFree >= SIZE) return;
632  mIndexFree++;
633  mIndexPrior = mIndexFree;
634  mIndexCandidates = mIndexFree;
635  }
636 
646  getFreeAgent(); // allocate a new Transition
647 
648  mIndexCandidates++;
649  mIndexFree++;
650  return &mTransitions[mIndexPrior];
651  }
652 
655  Transition* ft = mTransitions[mIndexFree];
656  Transition* prior = mTransitions[mIndexPrior];
657  if ((prior->isValidPrior && prior->transitionTime < ft->transitionTime)
658  || !prior->isValidPrior) {
659  ft->isValidPrior = true;
660  prior->isValidPrior = false;
661  internal::swap(mTransitions[mIndexPrior], mTransitions[mIndexFree]);
662  }
663  }
664 
671  mIndexCandidates--;
672  }
673 
681  if (mIndexFree >= SIZE) return;
682 
683  // This implementation makes pair-wise swaps to shift the current
684  // Transition leftwards into its correctly sorted position. At first
685  // glance, this seem inefficient compared to the alternative
686  // implementation where we save the current Transition, then slide all the
687  // elements to the left by one position rightwards. However,
688  // MemoryBenchmark shows that this implementation is 46 bytes smaller on
689  // an AVR processor.
690  for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
691  Transition* curr = mTransitions[i];
692  Transition* prev = mTransitions[i - 1];
693  if (curr->transitionTime >= prev->transitionTime) break;
694  mTransitions[i] = prev;
695  mTransitions[i - 1] = curr;
696  }
697  mIndexFree++;
698  }
699 
707  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
708  logging::printf("addActiveCandidatesToActivePool()\n");
709  }
710 
711  // Shift active candidates to the left into the Active pool.
712  uint8_t iActive = mIndexPrior;
713  uint8_t iCandidate = mIndexCandidates;
714  for (; iCandidate < mIndexFree; iCandidate++) {
715  if (isMatchStatusActive(mTransitions[iCandidate]->matchStatus)) {
716  if (iActive != iCandidate) {
717  // Must use swap(), because we are moving pointers instead of the
718  // actual Transition objects.
719  internal::swap(mTransitions[iActive], mTransitions[iCandidate]);
720  }
721  ++iActive;
722  }
723  }
724 
725  mIndexPrior = iActive;
726  mIndexCandidates = iActive;
727  mIndexFree = iActive;
728 
729  return mTransitions[iActive - 1];
730  }
731 
741  const {
742  if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
743  logging::printf(
744  "findTransitionForSeconds(): mIndexFree: %d\n", mIndexFree);
745  }
746 
747  const Transition* prev = nullptr;
748  const Transition* curr = nullptr;
749  const Transition* next = nullptr;
750  for (uint8_t i = 0; i < mIndexFree; i++) {
751  next = mTransitions[i];
752  if (next->startEpochSeconds > epochSeconds) break;
753  prev = curr;
754  curr = next;
755  next = nullptr;
756  }
757 
758  uint8_t fold;
759  uint8_t num;
760  calcFoldAndOverlap(&fold, &num, prev, curr, next, epochSeconds);
761  //fprintf(stderr, "prev=%p;curr=%p;next=%p;fold=%d;num=%d\n",
762  // prev, curr, next, fold, num);
763  return TransitionForSeconds{curr, fold, num};
764  }
765 
767  static void calcFoldAndOverlap(
768  uint8_t* fold,
769  uint8_t* num,
770  const Transition* prev,
771  const Transition* curr,
772  const Transition* next,
773  acetime_t epochSeconds) {
774 
775  if (curr == nullptr) {
776  *fold = 0;
777  *num = 0;
778  return;
779  }
780 
781  // Check if within forward overlap shadow from prev
782  bool isOverlap;
783  if (prev == nullptr) {
784  isOverlap = false;
785  } else {
786  // Extract the shift from prev transition. Can be 0 in some cases where
787  // the zone changed from DST of one zone to the STD into another zone,
788  // causing the overall UTC offset to remain unchanged.
789  acetime_t shiftSeconds = subtractDateTuple(
790  curr->startDateTime, prev->untilDateTime);
791  if (shiftSeconds >= 0) {
792  // spring forward, or unchanged
793  isOverlap = false;
794  } else {
795  // Check if within the forward overlap shadow from prev
796  isOverlap = epochSeconds - curr->startEpochSeconds < -shiftSeconds;
797  }
798  }
799  if (isOverlap) {
800  *fold = 1; // epochSeconds selects the second match
801  *num = 2;
802  return;
803  }
804 
805  // Check if within backward overlap shawdow from next
806  if (next == nullptr) {
807  isOverlap = false;
808  } else {
809  // Extract the shift to next transition. Can be 0 in some cases where
810  // the zone changed from DST of one zone to the STD into another zone,
811  // causing the overall UTC offset to remain unchanged.
812  acetime_t shiftSeconds = subtractDateTuple(
813  next->startDateTime, curr->untilDateTime);
814  if (shiftSeconds >= 0) {
815  // spring forward, or unchanged
816  isOverlap = false;
817  } else {
818  // Check if within the backward overlap shadow from next
819  isOverlap = next->startEpochSeconds - epochSeconds <= -shiftSeconds;
820  }
821  }
822  if (isOverlap) {
823  *fold = 0; // epochSeconds selects the first match
824  *num = 2;
825  return;
826  }
827 
828  // Normal single match, no overlap.
829  *fold = 0;
830  *num = 1;
831  }
832 
839  const LocalDateTime& ldt) const {
840  // Convert LocalDateTime to DateTuple.
841  DateTuple localDate{
842  ldt.year(),
843  ldt.month(),
844  ldt.day(),
845  (int16_t) (ldt.hour() * 60 + ldt.minute()),
847  };
848 
849  // Examine adjacent pairs of Transitions, looking for an exact match, gap,
850  // or overlap.
851  const Transition* prev = nullptr;
852  const Transition* curr = nullptr;
853  uint8_t num = 0;
854  for (uint8_t i = 0; i < mIndexFree; i++) {
855  curr = mTransitions[i];
856 
857  const DateTuple& startDateTime = curr->startDateTime;
858  const DateTuple& untilDateTime = curr->untilDateTime;
859  bool isExactMatch = (startDateTime <= localDate)
860  && (localDate < untilDateTime);
861 
862  if (isExactMatch) {
863  // Check for a previous exact match to detect an overlap.
864  if (num == 1) {
865  num++;
866  break;
867  }
868 
869  // Loop again to detect an overlap.
870  num = 1;
871  } else if (startDateTime > localDate) {
872  // Exit loop since no more candidate transition.
873  break;
874  }
875 
876  prev = curr;
877 
878  // Set the curr to nullptr so that if the loop runs off the end of the
879  // list of Transitions, the curr is marked as nullptr.
880  curr = nullptr;
881  }
882 
883  // Check if the prev was an exact match, and set the curr to be identical.
884  // avoid confusion.
885  if (num == 1) {
886  curr = prev;
887  }
888 
889  // This should get optimized by RVO.
890  return TransitionForDateTime{prev, curr, num};
891  }
892 
894  void log() const {
895  logging::printf("TransitionStorage: ");
896  logging::printf("SIZE=%d, mAllocSize=%d\n", SIZE, mAllocSize);
897  int nActives = mIndexPrior;
898  int nPrior = mIndexCandidates - mIndexPrior;
899  int nCandidates = mIndexFree - mIndexCandidates;
900  int nAllocFree = mAllocSize - mIndexFree;
901  int nVirginFree = SIZE - mAllocSize;
902 
903  logging::printf(" Actives: %d\n", nActives);
905  " ", &mTransitions[0], &mTransitions[mIndexPrior]);
906 
907  logging::printf(" Prior: %d\n", nPrior);
909  " ", &mTransitions[mIndexPrior], &mTransitions[mIndexCandidates]);
910 
911  logging::printf(" Candidates: %d\n", nCandidates);
913  " ", &mTransitions[mIndexCandidates], &mTransitions[mIndexFree]);
914 
915  logging::printf(" Allocated Free: %d\n", nAllocFree);
916  logging::printf(" Virgin Free: %d\n", nVirginFree);
917  }
918 
920  void resetAllocSize() { mAllocSize = 0; }
921 
927  uint8_t getAllocSize() const { return mAllocSize; }
928 
929  private:
930  friend class ::TransitionStorageTest_getFreeAgent;
931  friend class ::TransitionStorageTest_getFreeAgent2;
932  friend class ::TransitionStorageTest_addFreeAgentToActivePool;
933  friend class ::TransitionStorageTest_reservePrior;
934  friend class ::TransitionStorageTest_addPriorToCandidatePool;
935  friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
936  friend class ::TransitionStorageTest_setFreeAgentAsPriorIfValid;
937  friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
938  friend class ::TransitionStorageTest_findTransitionForDateTime;
939  friend class ::TransitionStorageTest_resetCandidatePool;
940 
942  Transition* getTransition(uint8_t i) {
943  return mTransitions[i];
944  }
945 
946  Transition mPool[SIZE];
947  Transition* mTransitions[SIZE];
948  uint8_t mIndexPrior;
949  uint8_t mIndexCandidates;
950  uint8_t mIndexFree;
951 
953  uint8_t mAllocSize = 0;
954 };
955 
956 } // namespace extended
957 } // namespace ace_time
958 
959 #endif
Class that holds the date-time as the components (year, month, day, hour, minute, second) without reg...
Definition: LocalDateTime.h:31
uint8_t day() const
Return the day of the month.
uint8_t month() const
Return the month with January=1, December=12.
uint8_t minute() const
Return the minute.
uint8_t hour() const
Return the hour.
int16_t year() const
Return the year.
static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day)
Factory method using separated year, month and day fields.
Definition: LocalDate.h:156
int32_t toEpochDays() const
Return number of days since the current epoch year sCurrentEpochYear.
Definition: LocalDate.h:355
A heap manager which is specialized and tuned to manage a collection of Transitions,...
Definition: Transition.h:530
void resetAllocSize()
Reset the current allocation size.
Definition: Transition.h:920
void addFreeAgentToActivePool()
Immediately add the free agent Transition at index mIndexFree to the Active pool.
Definition: Transition.h:630
TransitionForSecondsTemplate< ZEB, ZPB, ZRB > TransitionForSeconds
Template instantiation of TransitionForSecondsTemplate used by this class.
Definition: Transition.h:543
TransitionForDateTimeTemplate< ZEB, ZPB, ZRB > TransitionForDateTime
Template instantiation of TransitionForDateTimeTemplate used by this class.
Definition: Transition.h:550
void init()
Initialize all pools to 0 size, usually when a new year is initialized.
Definition: Transition.h:561
void setFreeAgentAsPriorIfValid()
Set the free agent transition as the most recent prior.
Definition: Transition.h:654
Transition * getFreeAgent()
Return a pointer to the first Transition in the free pool.
Definition: Transition.h:607
TransitionTemplate< ZEB, ZPB, ZRB > Transition
Template instantiation of TransitionTemplate used by this class.
Definition: Transition.h:536
void resetCandidatePool()
Empty the Candidate pool by resetting the various indexes.
Definition: Transition.h:583
static void calcFoldAndOverlap(uint8_t *fold, uint8_t *num, const Transition *prev, const Transition *curr, const Transition *next, acetime_t epochSeconds)
Calculate the fold and num parameters of TransitionForSecond.
Definition: Transition.h:767
uint8_t getAllocSize() const
Return the maximum number of transitions which was allocated.
Definition: Transition.h:927
void addFreeAgentToCandidatePool()
Add the free agent Transition at index mIndexFree to the Candidate pool, sorted by transitionTime.
Definition: Transition.h:680
Transition * getPrior()
Return the current prior transition.
Definition: Transition.h:571
TransitionForDateTime findTransitionForDateTime(const LocalDateTime &ldt) const
Return the candidate Transitions matching the given dateTime.
Definition: Transition.h:838
Transition * addActiveCandidatesToActivePool()
Add active candidates into the Active pool, and collapse the Candidate pool.
Definition: Transition.h:706
void addPriorToCandidatePool()
Add the current prior into the Candidates pool.
Definition: Transition.h:670
TransitionForSeconds findTransitionForSeconds(acetime_t epochSeconds) const
Return the Transition matching the given epochSeconds.
Definition: Transition.h:740
Transition ** reservePrior()
Allocate a free Transition then add it to the Prior pool.
Definition: Transition.h:645
void log() const
Verify that the indexes are valid.
Definition: Transition.h:894
int32_t acetime_t
Type for the number of seconds from epoch.
Definition: common.h:24
A tuple that represents a date and time.
Definition: Transition.h:59
void log() const
Used only for debugging.
Definition: Transition.h:72
Data structure that captures the matching ZoneEra and its ZoneRule transitions for a given year.
Definition: Transition.h:193
MatchingEraTemplate * prevMatch
The previous MatchingEra, needed to interpret startDateTime.
Definition: Transition.h:207
int16_t lastDeltaMinutes
The DST offset of the last Transition in this MatchingEra.
Definition: Transition.h:213
ZEB era
The ZoneEra that matched the given year.
Definition: Transition.h:204
DateTuple untilDateTime
The effective until time of the matching ZoneEra.
Definition: Transition.h:201
DateTuple startDateTime
The effective start time of the matching ZoneEra, which uses the UTC offsets of the previous matching...
Definition: Transition.h:198
int16_t lastOffsetMinutes
The STD offset of the last Transition in this MatchingEra.
Definition: Transition.h:210
The result of the findTransitionForDateTime(const LocalDatetime& ldt) method which can return 0,...
Definition: Transition.h:486
uint8_t num
Number of matches: 0, 1, 2.
Definition: Transition.h:495
const TransitionTemplate< ZEB, ZPB, ZRB > * prev
The previous transition.
Definition: Transition.h:489
const TransitionTemplate< ZEB, ZPB, ZRB > * curr
The matching transition, or null if not found or in gap.
Definition: Transition.h:492
Tuple of a matching Transition and its 'fold'.
Definition: Transition.h:456
uint8_t fold
1 if corresponding datetime occurred the second time
Definition: Transition.h:461
const TransitionTemplate< ZEB, ZPB, ZRB > * curr
The matching transition, or null if not found.
Definition: Transition.h:458
uint8_t num
Number of occurrences of the resulting LocalDateTime: 0, 1, or 2.
Definition: Transition.h:469
Represents an interval of time where the time zone obeyed a certain UTC offset and DST delta.
Definition: Transition.h:260
static void printTransitions(const char *prefix, const TransitionTemplate *const *begin, const TransitionTemplate *const *end)
Print an iterable of Transitions from 'begin' to 'end'.
Definition: Transition.h:435
ZRB rule
The Zone transition rule that matched for the the given year.
Definition: Transition.h:270
bool isValidPrior
During findCandidateTransitions(), this flag indicates whether the current transition is a valid "pri...
Definition: Transition.h:346
DateTuple transitionTime
The original transition time, usually 'w' but sometimes 's' or 'u'.
Definition: Transition.h:278
acetime_t startEpochSeconds
The calculated transition time of the given rule.
Definition: Transition.h:319
DateTuple transitionTimeS
Version of transitionTime in 's' mode, using the UTC offset of the previous Transition.
Definition: Transition.h:286
DateTuple untilDateTime
Until time expressed using the UTC offset of the current Transition.
Definition: Transition.h:307
const char * letter() const
Return the letter string.
Definition: Transition.h:366
int16_t offsetMinutes
The base offset minutes, not the total effective UTC offset.
Definition: Transition.h:328
void log() const
Used only for debugging.
Definition: Transition.h:397
char letterBuf[2]
Storage for the single letter 'letter' field if 'rule' is not null.
Definition: Transition.h:337
DateTuple transitionTimeU
Version of transitionTime in 'u' mode, using the UTC offset of the previous transition.
Definition: Transition.h:301
static void logHourMinute(int16_t minutes)
Print minutes as [+/-]hh:mm.
Definition: Transition.h:420
MatchStatus matchStatus
During processTransitionMatchStatus(), this flag indicates how the transition falls within the time i...
Definition: Transition.h:352
DateTuple startDateTime
Start time expressed using the UTC offset of the current Transition.
Definition: Transition.h:292
char abbrev[internal::kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
Definition: Transition.h:334
int16_t deltaMinutes
The DST delta minutes.
Definition: Transition.h:331
const MatchingEraTemplate< ZEB > * match
The match which generated this Transition.
Definition: Transition.h:263
static const uint8_t kSuffixW
Represents 'w' or wall time.
Definition: ZoneContext.h:18