1 #ifndef ACE_TIME_EXTENDED_ZONE_SPECIFIER_H 2 #define ACE_TIME_EXTENDED_ZONE_SPECIFIER_H 7 #include "common/ZonePolicy.h" 8 #include "common/ZoneInfo.h" 9 #include "common/logger.h" 10 #include "TimeOffset.h" 11 #include "LocalDate.h" 12 #include "OffsetDateTime.h" 13 #include "ZoneSpecifier.h" 14 #include "BasicZoneSpecifier.h" 15 #include "local_date_mutation.h" 19 class ExtendedZoneSpecifierTest_compareEraToYearMonth;
20 class ExtendedZoneSpecifierTest_createMatch;
21 class ExtendedZoneSpecifierTest_findMatches_simple;
22 class ExtendedZoneSpecifierTest_findMatches_named;
23 class ExtendedZoneSpecifierTest_findCandidateTransitions;
24 class ExtendedZoneSpecifierTest_findTransitionsFromNamedMatch;
25 class ExtendedZoneSpecifierTest_getTransitionTime;
26 class ExtendedZoneSpecifierTest_createTransitionForYear;
27 class ExtendedZoneSpecifierTest_normalizeDateTuple;
28 class ExtendedZoneSpecifierTest_expandDateTuple;
29 class ExtendedZoneSpecifierTest_calcInteriorYears;
30 class ExtendedZoneSpecifierTest_getMostRecentPriorYear;
31 class ExtendedZoneSpecifierTest_compareTransitionToMatchFuzzy;
32 class ExtendedZoneSpecifierTest_compareTransitionToMatch;
33 class ExtendedZoneSpecifierTest_processActiveTransition;
34 class ExtendedZoneSpecifierTest_fixTransitionTimes_generateStartUntilTimes;
35 class ExtendedZoneSpecifierTest_createAbbreviation;
36 class TransitionStorageTest_getFreeAgent;
37 class TransitionStorageTest_getFreeAgent2;
38 class TransitionStorageTest_addFreeAgentToActivePool;
39 class TransitionStorageTest_reservePrior;
40 class TransitionStorageTest_addFreeAgentToCandidatePool;
41 class TransitionStorageTest_setFreeAgentAsPrior;
42 class TransitionStorageTest_addActiveCandidatesToActivePool;
43 class TransitionStorageTest_resetCandidatePool;
63 logging::print(
"DateTuple(%d-%u-%uT%d'%c')",
70 if (a.yearTiny < b.yearTiny)
return true;
71 if (a.yearTiny > b.yearTiny)
return false;
72 if (a.month < b.month)
return true;
73 if (a.month > b.month)
return false;
74 if (a.day < b.day)
return true;
75 if (a.day > b.day)
return false;
76 if (a.timeCode < b.timeCode)
return true;
77 if (a.timeCode > b.timeCode)
return false;
95 return a.yearTiny == b.yearTiny
98 && a.timeCode == b.timeCode
99 && a.modifier == b.modifier;
123 logging::print(
"ZoneMatch(");
124 logging::print(
"Start:"); startDateTime.
log();
125 logging::print(
"; Until:"); untilDateTime.
log();
126 logging::print(
"; Era: %snull", (era) ?
"!" :
"");
205 char abbrev[kAbbrevSize];
216 const char* format()
const {
240 static char letterBuf[2];
250 if (rule->
letter ==
'-') {
253 letterBuf[0] = rule->
letter;
263 uint8_t numLetters = policy->numLetters;
264 if (rule->
letter >= numLetters) {
272 return policy->letters[rule->
letter];
282 logging::print(
"Transition(");
283 if (
sizeof(acetime_t) ==
sizeof(
int)) {
284 logging::print(
"sE: %d", startEpochSeconds);
286 logging::print(
"sE: %ld", startEpochSeconds);
288 logging::print(
"; match: %snull", (match) ?
"!" :
"");
289 logging::print(
"; era: %snull", (match && match->
era) ?
"!" :
"");
290 logging::print(
"; oCode: %d", offsetCode());
291 logging::print(
"; dCode: %d", deltaCode());
292 logging::print(
"; tt: "); transitionTime.
log();
293 if (rule !=
nullptr) {
295 logging::print(
"; R.tY: %d", rule->
toYearTiny);
296 logging::print(
"; R.M: %d", rule->
inMonth);
329 template<u
int8_t SIZE>
337 for (uint8_t i = 0; i < SIZE; i++) {
338 mTransitions[i] = &mPool[i];
341 mIndexCandidates = 0;
364 mIndexCandidates = mIndexPrior;
365 mIndexFree = mIndexPrior;
369 return &mTransitions[mIndexCandidates];
372 return &mTransitions[mIndexFree];
375 Transition** getActivePoolBegin() {
return &mTransitions[0]; }
376 Transition** getActivePoolEnd() {
return &mTransitions[mIndexFree]; }
386 if (mIndexFree > mHighWater) {
387 mHighWater = mIndexFree;
390 if (mIndexFree < SIZE) {
391 return mTransitions[mIndexFree];
393 return mTransitions[SIZE - 1];
405 if (mIndexFree >= SIZE)
return;
407 mIndexPrior = mIndexFree;
408 mIndexCandidates = mIndexFree;
419 return &mTransitions[mIndexPrior];
424 swap(&mTransitions[mIndexPrior], &mTransitions[mIndexFree]);
442 if (mIndexFree >= SIZE)
return;
443 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
447 mTransitions[i] = prev;
448 mTransitions[i - 1] = curr;
461 if (DEBUG) logging::println(
"addActiveCandidatesToActivePool()");
462 uint8_t iActive = mIndexPrior;
463 uint8_t iCandidate = mIndexCandidates;
464 for (; iCandidate < mIndexFree; iCandidate++) {
465 if (mTransitions[iCandidate]->active) {
466 if (iActive != iCandidate) {
467 swap(&mTransitions[iActive], &mTransitions[iCandidate]);
472 mIndexPrior = iActive;
473 mIndexCandidates = iActive;
474 mIndexFree = iActive;
482 if (DEBUG) logging::println(
483 "findTransition(): mIndexFree: %d", mIndexFree);
486 for (uint8_t i = 0; i < mIndexFree; i++) {
487 const Transition* candidate = mTransitions[i];
522 if (DEBUG) logging::println(
523 "findTransitionForDateTime(): mIndexFree: %d", mIndexFree);
528 (int8_t) (ldt.
hour() * 4 + ldt.
minute() / 15),
'w' };
530 for (uint8_t i = 0; i < mIndexFree; i++) {
531 const Transition* candidate = mTransitions[i];
543 logging::println(
"TransitionStorage:");
544 logging::println(
" mIndexPrior: %d", mIndexPrior);
545 logging::println(
" mIndexCandidates: %d", mIndexCandidates);
546 logging::println(
" mIndexFree: %d", mIndexFree);
547 if (mIndexPrior != 0) {
548 logging::println(
" Actives:");
549 for (uint8_t i = 0; i < mIndexPrior; i++) {
550 mTransitions[i]->log();
554 if (mIndexPrior != mIndexCandidates) {
555 logging::print(
" Prior: ");
556 mTransitions[mIndexPrior]->log();
559 if (mIndexCandidates != mIndexFree) {
560 logging::println(
" Candidates:");
561 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
562 mTransitions[i]->log();
579 friend class ::TransitionStorageTest_getFreeAgent;
580 friend class ::TransitionStorageTest_getFreeAgent2;
581 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
582 friend class ::TransitionStorageTest_reservePrior;
583 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
584 friend class ::TransitionStorageTest_setFreeAgentAsPrior;
585 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
586 friend class ::TransitionStorageTest_resetCandidatePool;
589 Transition* getTransition(uint8_t i) {
return mTransitions[i]; }
594 uint8_t mIndexCandidates;
598 uint8_t mHighWater = 0;
639 mZoneInfo(zoneInfo) {}
661 const char*
getAbbrev(acetime_t epochSeconds)
const override {
663 if (mIsOutOfBounds)
return "";
665 return transition->
abbrev;
670 init(ldt.getLocalDate());
673 mTransitionStorage.findTransitionForDateTime(ldt);
680 void printTo(Print& printer)
const override;
684 logging::println(
"ExtendedZoneSpecifier:");
685 logging::println(
" mYear: %d", mYear);
686 logging::println(
" mNumMatches: %d", mNumMatches);
687 for (
int i = 0; i < mNumMatches; i++) {
688 logging::print(
" Match %d: ", i);
692 mTransitionStorage.log();
697 mTransitionStorage.resetHighWater();
702 return mTransitionStorage.getHighWater();
706 friend class ::ExtendedZoneSpecifierTest_compareEraToYearMonth;
707 friend class ::ExtendedZoneSpecifierTest_createMatch;
708 friend class ::ExtendedZoneSpecifierTest_findMatches_simple;
709 friend class ::ExtendedZoneSpecifierTest_findMatches_named;
710 friend class ::ExtendedZoneSpecifierTest_findCandidateTransitions;
711 friend class ::ExtendedZoneSpecifierTest_findTransitionsFromNamedMatch;
712 friend class ::ExtendedZoneSpecifierTest_getTransitionTime;
713 friend class ::ExtendedZoneSpecifierTest_createTransitionForYear;
714 friend class ::ExtendedZoneSpecifierTest_normalizeDateTuple;
715 friend class ::ExtendedZoneSpecifierTest_expandDateTuple;
716 friend class ::ExtendedZoneSpecifierTest_calcInteriorYears;
717 friend class ::ExtendedZoneSpecifierTest_getMostRecentPriorYear;
718 friend class ::ExtendedZoneSpecifierTest_compareTransitionToMatchFuzzy;
719 friend class ::ExtendedZoneSpecifierTest_compareTransitionToMatch;
720 friend class ::ExtendedZoneSpecifierTest_processActiveTransition;
721 friend class ::ExtendedZoneSpecifierTest_fixTransitionTimes_generateStartUntilTimes;
722 friend class ::ExtendedZoneSpecifierTest_createAbbreviation;
728 static const uint8_t kMaxMatches = 4;
736 static const uint8_t kMaxTransitions = 8;
742 static const uint8_t kMaxInteriorYears = 4;
753 return getZoneInfo() == that.getZoneInfo();
758 return mTransitionStorage.findTransition(epochSeconds);
762 void init(acetime_t epochSeconds)
const {
769 int16_t year = ld.
year();
770 if (isFilled(year))
return;
771 if (DEBUG) logging::println(
"init(): %d", year);
775 mTransitionStorage.init();
777 if (year < mZoneInfo->zoneContext->startYear - 1
778 || mZoneInfo->zoneContext->untilYear < year) {
779 mIsOutOfBounds =
true;
788 mNumMatches = findMatches(mZoneInfo, startYm, untilYm, mMatches,
791 findTransitions(mTransitionStorage, mMatches, mNumMatches);
794 fixTransitionTimes(begin, end);
795 generateStartUntilTimes(begin, end);
796 calcAbbreviations(begin, end);
799 mIsOutOfBounds =
false;
803 bool isFilled(int16_t year)
const {
804 return mIsFilled && (year == mYear);
818 if (DEBUG) logging::println(
"findMatches()");
821 for (uint8_t iEra = 0; iEra < zoneInfo->
numEras; iEra++) {
823 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
824 if (iMatch < maxMatches) {
825 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
844 static bool eraOverlapsInterval(
849 return compareEraToYearMonth(prev, untilYm.yearTiny, untilYm.month) < 0
850 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
855 int8_t yearTiny, uint8_t month) {
882 startYm.yearTiny, startYm.month, 1, 0,
'w' 884 if (startDate < lowerBound) {
885 startDate = lowerBound;
893 untilYm.yearTiny, untilYm.month, 1, 0,
'w' 895 if (upperBound < untilDate) {
896 untilDate = upperBound;
899 return {startDate, untilDate, era};
906 static void findTransitions(
909 uint8_t numMatches) {
910 if (DEBUG) logging::println(
"findTransitions()");
911 for (uint8_t i = 0; i < numMatches; i++) {
912 findTransitionsForMatch(transitionStorage, &matches[i]);
917 static void findTransitionsForMatch(
920 if (DEBUG) logging::println(
"findTransitionsForMatch()");
922 if (policy ==
nullptr) {
923 findTransitionsFromSimpleMatch(transitionStorage, match);
925 findTransitionsFromNamedMatch(transitionStorage, match);
929 static void findTransitionsFromSimpleMatch(
932 if (DEBUG) logging::println(
"findTransitionsFromSimpleMatch()");
934 freeTransition->
match = match;
935 freeTransition->
rule =
nullptr;
941 static void findTransitionsFromNamedMatch(
944 if (DEBUG) logging::println(
"findTransitionsFromNamedMatch()");
946 if (DEBUG) { match->log(); logging::println(); }
947 findCandidateTransitions(transitionStorage, match);
948 if (DEBUG) { transitionStorage.
log(); logging::println(); }
950 transitionStorage.getCandidatePoolBegin(),
951 transitionStorage.getCandidatePoolEnd());
952 selectActiveTransitions(transitionStorage, match);
953 if (DEBUG) { transitionStorage.
log(); logging::println(); }
956 if (DEBUG) { transitionStorage.
log(); logging::println(); }
959 static void findCandidateTransitions(
963 logging::print(
"findCandidateTransitions(): ");
968 uint8_t numRules = policy->numRules;
975 for (uint8_t r = 0; r < numRules; r++) {
979 int8_t interiorYears[kMaxInteriorYears];
980 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
982 for (uint8_t y = 0; y < numYears; y++) {
983 int8_t year = interiorYears[y];
985 createTransitionForYear(t, year, rule, match);
986 int8_t status = compareTransitionToMatchFuzzy(t, match);
988 setAsPriorTransition(transitionStorage, t);
989 }
else if (status == 1) {
995 int8_t priorYear = getMostRecentPriorYear(
998 if (DEBUG) logging::println(
999 "findCandidateTransitions(): priorYear: %d", priorYear);
1001 createTransitionForYear(t, priorYear, rule, match);
1002 setAsPriorTransition(transitionStorage, t);
1008 if ((*prior)->active) {
1009 if (DEBUG) logging::println(
1010 "findCandidateTransitions(): adding prior to Candidate pool");
1019 static uint8_t calcInteriorYears(int8_t* interiorYears,
1020 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1021 int8_t startYear, int8_t endYear) {
1023 for (int8_t year = startYear; year <= endYear; year++) {
1024 if (fromYear <= year && year <= toYear) {
1025 interiorYears[i] = year;
1027 if (i >= maxInteriorYears)
break;
1047 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1048 int8_t startYear, int8_t ) {
1049 if (fromYear < startYear) {
1050 if (toYear < startYear) {
1053 return startYear - 1;
1062 uint8_t dayOfMonth = BasicZoneSpecifier::calcStartDayOfMonth(
1065 return {yearTiny, rule->
inMonth, dayOfMonth,
1079 static int8_t compareTransitionToMatchFuzzy(
1084 int16_t matchStartMonths = match->
startDateTime.yearTiny * 12
1086 if (ttMonths < matchStartMonths - 1)
return -1;
1088 int16_t matchUntilMonths = match->
untilDateTime.yearTiny * 12
1090 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1096 static void setAsPriorTransition(
1099 if (DEBUG) logging::println(
"setAsPriorTransition()");
1120 static void fixTransitionTimes(
1122 if (DEBUG) logging::println(
"fixTransitionTimes(): #transitions: %d;",
1123 (
int) (end - begin));
1129 logging::println(
"fixTransitionTimes(): LOOP");
1138 if (DEBUG) logging::println(
"fixTransitionTimes(): END");
1148 int8_t offsetCode, int8_t deltaCode) {
1149 if (DEBUG) logging::println(
"expandDateTuple()");
1150 if (tt->modifier ==
's') {
1152 *ttu = {tt->yearTiny, tt->month, tt->day,
1153 (int8_t) (tt->timeCode - offsetCode),
'u'};
1154 *tt = {tt->yearTiny, tt->month, tt->day,
1155 (int8_t) (tt->timeCode + deltaCode),
'w'};
1156 }
else if (tt->modifier ==
'u') {
1158 *tts = {tt->yearTiny, tt->month, tt->day,
1159 (int8_t) (tt->timeCode + offsetCode),
's'};
1160 *tt = {tt->yearTiny, tt->month, tt->day,
1161 (int8_t) (tt->timeCode + offsetCode + deltaCode),
'w'};
1165 *tts = {tt->yearTiny, tt->month, tt->day,
1166 (int8_t) (tt->timeCode - deltaCode),
's'};
1167 *ttu = {tt->yearTiny, tt->month, tt->day,
1168 (int8_t) (tt->timeCode - deltaCode - offsetCode),
'u'};
1171 if (DEBUG) logging::println(
"expandDateTuple(): normalizeDateTuple(): 1");
1172 normalizeDateTuple(tt);
1173 if (DEBUG) logging::println(
"expandDateTuple(): normalizeDateTuple(): 2");
1174 normalizeDateTuple(tts);
1175 if (DEBUG) logging::println(
"expandDateTuple(): normalizeDateTuple(): 3");
1176 normalizeDateTuple(ttu);
1181 const int8_t kOneDayAsCode = 4 * 24;
1182 if (dt->timeCode <= -kOneDayAsCode) {
1183 LocalDate ld(dt->yearTiny, dt->month, dt->day);
1184 local_date_mutation::decrementOneDay(ld);
1186 dt->month = ld.
month();
1188 dt->timeCode += kOneDayAsCode;
1189 }
else if (kOneDayAsCode <= dt->timeCode) {
1190 LocalDate ld(dt->yearTiny, dt->month, dt->day);
1191 local_date_mutation::incrementOneDay(ld);
1193 dt->month = ld.
month();
1195 dt->timeCode -= kOneDayAsCode;
1205 static void selectActiveTransitions(
1210 if (DEBUG) logging::println(
"selectActiveTransitions(): #candidates: %d",
1211 (
int) (end - begin));
1215 processActiveTransition(match, transition, &prior);
1221 if (DEBUG) logging::println(
1222 "selectActiveTransitions(): found latest prior");
1235 static void processActiveTransition(
1239 int8_t status = compareTransitionToMatch(transition, match);
1241 transition->
active =
false;
1242 }
else if (status == 1) {
1243 transition->
active =
true;
1244 }
else if (status == 0) {
1246 (*prior)->active =
false;
1248 transition->
active =
true;
1249 (*prior) = transition;
1253 (*prior)->
active =
false;
1254 transition->
active =
true;
1255 (*prior) = transition;
1258 transition->
active =
true;
1259 (*prior) = transition;
1278 static int8_t compareTransitionToMatch(
1284 if (matchStart.modifier ==
's') {
1286 }
else if (matchStart.modifier ==
'u') {
1291 if (*transitionTime < matchStart)
return -1;
1292 if (*transitionTime == matchStart)
return 0;
1295 if (matchUntil.modifier ==
's') {
1297 }
else if (matchUntil.modifier ==
'u') {
1302 if (*transitionTime < matchUntil)
return 1;
1311 static void generateStartUntilTimes(
1313 if (DEBUG) logging::println(
1314 "generateStartUntilTimes(): #transitions: %d;",
1315 (
int) (end - begin));
1318 bool isAfterFirst =
false;
1333 t->
startDateTime = {tt.yearTiny, tt.month, tt.day, code, tt.modifier};
1347 const acetime_t offsetSeconds = (acetime_t) 900
1349 LocalDate ld(st.yearTiny, st.month, st.day);
1353 isAfterFirst =
true;
1360 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1368 static void calcAbbreviations(
1370 if (DEBUG) logging::println(
"calcAbbreviations(): #transitions: %d;",
1371 (
int) (end - begin));
1374 const char* format = t->format();
1376 const char* letter = t->
letter();
1378 format, deltaCode, letter);
1416 static void createAbbreviation(
char* dest, uint8_t destSize,
1417 const char* format, uint8_t deltaCode,
const char* letterString) {
1420 if (letterString ==
nullptr) {
1421 strncpy(dest, format, destSize);
1422 dest[destSize - 1] =
'\0';
1427 if (strchr(format,
'%') !=
nullptr) {
1428 copyAndReplace(dest, destSize, format,
'%', letterString);
1431 const char* slashPos = strchr(format,
'/');
1432 if (slashPos !=
nullptr) {
1433 if (deltaCode == 0) {
1434 uint8_t headLength = (slashPos - format);
1435 if (headLength >= destSize) headLength = destSize - 1;
1436 memcpy(dest, format, headLength);
1437 dest[headLength] =
'\0';
1439 uint8_t tailLength = strlen(slashPos+1);
1440 if (tailLength >= destSize) tailLength = destSize - 1;
1441 memcpy(dest, slashPos+1, tailLength);
1442 dest[tailLength] =
'\0';
1446 strncpy(dest, format, destSize);
1447 dest[destSize - 1] =
'\0';
1457 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
1458 char oldChar,
const char* newString) {
1459 while (*src !=
'\0' && dstSize > 0) {
1460 if (*src == oldChar) {
1461 while (*newString !=
'\0' && dstSize > 0) {
1462 *dst++ = *newString++;
1480 mutable int16_t mYear = 0;
1481 mutable bool mIsFilled =
false;
1482 mutable bool mIsOutOfBounds =
false;
1484 mutable uint8_t mNumMatches = 0;
void addFreeAgentToActivePool()
Immediately add the free agent Transition at index mIndexFree to the Active pool. ...
static TimeOffset forError()
Return an error indicator.
uint8_t hour() const
Return the hour.
A heap manager which is specialized and tuned to manage a collection of Transitions, keeping track of unused, used, and active states, using a fixed array of Transitions.
void addActiveCandidatesToActivePool()
Add active candidates into the Active pool, and collapse the Candidate pool.
int8_t offsetCode() const
The base offset code.
uint8_t month() const
Return the month with January=1, December=12.
uint8_t const untilMonth
The month field in UNTIL (1-12).
DateTuple transitionTimeU
Version of transitionTime in 'u' mode, using the UTC offset of the previous transition.
int8_t const toYearTiny
TO year as an offset from year 2000 stored as a single byte.
uint8_t getHighWater() const
Return the high water mark.
void resetCandidatePool()
Empty the Candidate pool by resetting the various indexes.
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
acetime_t toEpochSeconds() const
Return the number of seconds since AceTime epoch (2000-01-01 00:00:00).
uint8_t const onDayOfMonth
Determined by the ON column.
DateTuple startDateTime
The effective start time of the matching ZoneEra.
Transition * getFreeAgent()
Return a pointer to the first Transition in the free pool.
static const uint8_t kAbbrevSize
Longest abbreviation currently seems to be 5 characters (https://www.timeanddate.com/time/zones/) but...
const ZoneMatch * match
The match which generated this Transition.
uint8_t month() const
Return the month with January=1, December=12.
TransitionStorage()
Constructor.
uint8_t minute() const
Return the minute.
DateTuple originalTransitionTime
If the transition is shifted to the beginning of a ZoneMatch, this is set to the transitionTime for d...
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
An implementation of ZoneSpecifier that works for all zones defined by the TZ Database (with some zon...
const Transition * findTransition(acetime_t epochSeconds) const
Return the Transition matching the given epochSeconds.
A collection of transition rules which describe the DST rules of a given administrative region...
const extended::ZoneRule * rule
The Zone transition rule that matched for the the given year.
int8_t const offsetCode
UTC offset in 15 min increments.
Represents an interval of time where the time zone obeyed a certain UTC offset and DST delta...
void addPriorToCandidatePool()
Add the current prior into the Candidates pool.
Representation of a given time zone, implemented as an array of ZoneEra records.
const ZonePolicy *const zonePolicy
Zone policy, determined by the RULES column.
void setFreeAgentAsPrior()
Swap the Free agrent transition with the current Prior transition.
void log() const
Used only for debugging.
Transition * getPrior()
Return the current prior transition.
void log() const
Verify that the indexes are valid.
char abbrev[kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
void resetTransitionHighWater()
Reset the TransitionStorage high water mark.
An entry in ZoneInfo which describes which ZonePolicy was being followed during a particular time per...
void init()
Initialize all pools.
uint8_t const untilTimeModifier
UNTIL time modifier suffix: 'w', 's' or 'u'.
void addFreeAgentToCandidatePool()
Add the free agent Transition at index mIndexFree to the Candidate pool, sorted by transitionTime...
Base interface for ZoneSpecifier classes.
DateTuple transitionTime
The original transition time, usually 'w' but sometimes 's' or 'u'.
uint8_t const untilTimeCode
The time field of UNTIL field in 15-minute increments.
A time zone transition rule.
uint8_t day() const
Return the day of the month.
static const uint8_t kAbbrevSize
Size of the timezone abbreviation.
int16_t year() const
Return the full year instead of just the last 2 digits.
uint8_t const onDayOfWeek
Determined by the ON column.
uint8_t const numEras
Number of ZoneEra entries.
uint8_t const untilDay
The day field in UNTIL (1-31).
int8_t deltaCode() const
The DST offset code.
DateTuple transitionTimeS
Version of transitionTime in 's' mode, using the UTC offset of the previous Transition.
int8_t const untilYearTiny
Era is valid until currentTime < untilYear.
uint8_t day() const
Return the day of the month.
acetime_t startEpochSeconds
The calculated transition time of the given rule.
A simple tuple to represent a year/month pair.
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since AceTime epoch of 2000-01-01.
static TimeOffset forOffsetCode(int8_t offsetCode)
Create TimeOffset from the offset code.
const Transition * findTransitionForDateTime(const LocalDateTime &ldt) const
Return the Transition matching the given dateTime.
void log() const
Used only for debugging.
const extended::ZoneEra * era
The ZoneEra that matched the given year.
bool active
Determines if this transition is valid.
Transition ** reservePrior()
Allocate one Transition just after the Active pool, but before the Candidate pool, to keep the most recent prior Transition.
static const int8_t kInvalidYearTiny
Sentinel yearTiny which indicates an error condition or sometimes a year that 'does not exist'...
ExtendedZoneSpecifier(const extended::ZoneInfo *zoneInfo)
Constructor.
void resetHighWater()
Reset the high water mark.
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
A thin wrapper that represents a time offset from a reference point, usually 00:00 at UTC...
uint8_t const inMonth
Determined by the IN column.
uint8_t const atTimeCode
Determined by the AT column in units of 15-minutes from 00:00.
TimeOffset getUtcOffsetForDateTime(const LocalDateTime &ldt) const override
Return the UTC offset matching the given the date/time components.
void swap(Transition **a, Transition **b)
Swap 2 transitions.
The date (year, month, day) representing the date without regards to time zone.
DateTuple untilDateTime
Until time expressed using the UTC offset of the current Transition.
int8_t const fromYearTiny
FROM year as an offset from year 2000 stored as a single byte.
uint8_t getTransitionHighWater() const
Get the TransitionStorage high water mark.
uint8_t const atTimeModifier
Determined by the suffix in the AT column: 'w'=wall; 's'=standard; 'u'=meridian ('g' and 'z' mean the...
static const int16_t kEpochYear
Base year of epoch.
DateTuple untilDateTime
The effective until time of the matching ZoneEra.
const char * letter() const
Return the letter string.
const extended::ZoneInfo * getZoneInfo() const
Return the underlying ZoneInfo.
uint8_t const letter
Determined by the LETTER column.
DateTuple startDateTime
Start time expressed using the UTC offset of the current Transition.
const ZoneEra *const eras
ZoneEra entries in increasing order of UNTIL time.
A tuple that represents a date and time, using a timeCode that tracks the time component using 15-min...
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
TimeOffset getUtcOffset(acetime_t epochSeconds) const override
Return the total UTC offset at epochSeconds, including DST offset.
int8_t const deltaCode
Determined by the SAVE column, containing the offset from UTC, in 15-min increments.
const char *const format
Zone abbreviations (e.g.
Data structure that captures the matching ZoneEra and its ZoneRule transitions for a given year...
int8_t const deltaCode
If zonePolicy is nullptr, then this indicates the DST offset in 15 minute increments.
void log() const
Used only for debugging.