6 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_H 7 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_H 12 #include "internal/ZonePolicy.h" 13 #include "internal/ZoneInfo.h" 14 #include "common/logging.h" 15 #include "TimeOffset.h" 16 #include "LocalDate.h" 17 #include "OffsetDateTime.h" 18 #include "ZoneProcessor.h" 19 #include "BasicZoneProcessor.h" 20 #include "local_date_mutation.h" 22 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG 0 24 class ExtendedZoneProcessorTest_compareEraToYearMonth;
25 class ExtendedZoneProcessorTest_compareEraToYearMonth2;
26 class ExtendedZoneProcessorTest_createMatch;
27 class ExtendedZoneProcessorTest_findMatches_simple;
28 class ExtendedZoneProcessorTest_findMatches_named;
29 class ExtendedZoneProcessorTest_findCandidateTransitions;
30 class ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
31 class ExtendedZoneProcessorTest_getTransitionTime;
32 class ExtendedZoneProcessorTest_createTransitionForYear;
33 class ExtendedZoneProcessorTest_normalizeDateTuple;
34 class ExtendedZoneProcessorTest_expandDateTuple;
35 class ExtendedZoneProcessorTest_calcInteriorYears;
36 class ExtendedZoneProcessorTest_getMostRecentPriorYear;
37 class ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
38 class ExtendedZoneProcessorTest_compareTransitionToMatch;
39 class ExtendedZoneProcessorTest_processActiveTransition;
40 class ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
41 class ExtendedZoneProcessorTest_createAbbreviation;
42 class ExtendedZoneProcessorTest_setZoneInfo;
43 class TransitionStorageTest_getFreeAgent;
44 class TransitionStorageTest_getFreeAgent2;
45 class TransitionStorageTest_addFreeAgentToActivePool;
46 class TransitionStorageTest_reservePrior;
47 class TransitionStorageTest_addFreeAgentToCandidatePool;
48 class TransitionStorageTest_setFreeAgentAsPrior;
49 class TransitionStorageTest_addActiveCandidatesToActivePool;
50 class TransitionStorageTest_resetCandidatePool;
54 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
55 class ZoneProcessorCacheImpl;
73 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
74 logging::print(
"DateTuple(%d-%u-%uT%d'%c')",
82 if (a.yearTiny < b.yearTiny)
return true;
83 if (a.yearTiny > b.yearTiny)
return false;
84 if (a.month < b.month)
return true;
85 if (a.month > b.month)
return false;
86 if (a.day < b.day)
return true;
87 if (a.day > b.day)
return false;
88 if (a.timeCode < b.timeCode)
return true;
89 if (a.timeCode > b.timeCode)
return false;
107 return a.yearTiny == b.yearTiny
108 && a.month == b.month
110 && a.timeCode == b.timeCode
111 && a.modifier == b.modifier;
135 logging::print(
"ZoneMatch(");
136 logging::print(
"Start:"); startDateTime.
log();
137 logging::print(
"; Until:"); untilDateTime.
log();
138 logging::print(
"; Era: %snull", (era.isNotNull()) ?
"!" :
"");
230 char abbrev[kAbbrevSize];
263 const char* format()
const {
264 return match->
era.format();
280 char letter = rule.letter();
289 uint8_t numLetters = policy.numLetters();
290 if (letter >= numLetters) {
298 return policy.letter(letter);
303 logging::print(
"Transition(");
304 if (
sizeof(acetime_t) ==
sizeof(
int)) {
305 logging::print(
"sE: %d", startEpochSeconds);
307 logging::print(
"sE: %ld", startEpochSeconds);
309 logging::print(
"; match: %snull", (match) ?
"!" :
"");
310 logging::print(
"; era: %snull",
311 (match && match->
era.isNotNull()) ?
"!" :
"");
312 logging::print(
"; oCode: %d", offsetCode);
313 logging::print(
"; dCode: %d", deltaCode);
314 logging::print(
"; tt: "); transitionTime.
log();
315 if (rule.isNotNull()) {
316 logging::print(
"; R.fY: %d", rule.fromYearTiny());
317 logging::print(
"; R.tY: %d", rule.toYearTiny());
318 logging::print(
"; R.M: %d", rule.inMonth());
319 logging::print(
"; R.dow: %d", rule.onDayOfWeek());
320 logging::print(
"; R.dom: %d", rule.onDayOfMonth());
351 template<u
int8_t SIZE>
359 for (uint8_t i = 0; i < SIZE; i++) {
360 mTransitions[i] = &mPool[i];
363 mIndexCandidates = 0;
386 mIndexCandidates = mIndexPrior;
387 mIndexFree = mIndexPrior;
391 return &mTransitions[mIndexCandidates];
394 return &mTransitions[mIndexFree];
397 Transition** getActivePoolBegin() {
return &mTransitions[0]; }
398 Transition** getActivePoolEnd() {
return &mTransitions[mIndexFree]; }
408 if (mIndexFree > mHighWater) {
409 mHighWater = mIndexFree;
412 if (mIndexFree < SIZE) {
413 return mTransitions[mIndexFree];
415 return mTransitions[SIZE - 1];
427 if (mIndexFree >= SIZE)
return;
429 mIndexPrior = mIndexFree;
430 mIndexCandidates = mIndexFree;
441 return &mTransitions[mIndexPrior];
446 swap(&mTransitions[mIndexPrior], &mTransitions[mIndexFree]);
465 if (mIndexFree >= SIZE)
return;
466 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
470 mTransitions[i] = prev;
471 mTransitions[i - 1] = curr;
481 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
482 logging::println(
"addActiveCandidatesToActivePool()");
484 uint8_t iActive = mIndexPrior;
485 uint8_t iCandidate = mIndexCandidates;
486 for (; iCandidate < mIndexFree; iCandidate++) {
487 if (mTransitions[iCandidate]->active) {
488 if (iActive != iCandidate) {
489 swap(&mTransitions[iActive], &mTransitions[iCandidate]);
494 mIndexPrior = iActive;
495 mIndexCandidates = iActive;
496 mIndexFree = iActive;
508 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
509 logging::println(
"findTransition(): mIndexFree: %d", mIndexFree);
513 for (uint8_t i = 0; i < mIndexFree; i++) {
514 const Transition* candidate = mTransitions[i];
546 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
548 "findTransitionForDateTime(): mIndexFree: %d", mIndexFree);
554 (int8_t) (ldt.
hour() * 4 + ldt.
minute() / 15),
'w' };
556 for (uint8_t i = 0; i < mIndexFree; i++) {
557 const Transition* candidate = mTransitions[i];
566 logging::println(
"TransitionStorage:");
567 logging::println(
" mIndexPrior: %d", mIndexPrior);
568 logging::println(
" mIndexCandidates: %d", mIndexCandidates);
569 logging::println(
" mIndexFree: %d", mIndexFree);
570 if (mIndexPrior != 0) {
571 logging::println(
" Actives:");
572 for (uint8_t i = 0; i < mIndexPrior; i++) {
573 mTransitions[i]->log();
577 if (mIndexPrior != mIndexCandidates) {
578 logging::print(
" Prior: ");
579 mTransitions[mIndexPrior]->log();
582 if (mIndexCandidates != mIndexFree) {
583 logging::println(
" Candidates:");
584 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
585 mTransitions[i]->log();
602 friend class ::TransitionStorageTest_getFreeAgent;
603 friend class ::TransitionStorageTest_getFreeAgent2;
604 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
605 friend class ::TransitionStorageTest_reservePrior;
606 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
607 friend class ::TransitionStorageTest_setFreeAgentAsPrior;
608 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
609 friend class ::TransitionStorageTest_resetCandidatePool;
612 Transition* getTransition(uint8_t i) {
return mTransitions[i]; }
617 uint8_t mIndexCandidates;
621 uint8_t mHighWater = 0;
663 mZoneInfo(zoneInfo) {}
667 return mZoneInfo.zoneInfo();
670 uint32_t
getZoneId()
const override {
return mZoneInfo.zoneId(); }
673 bool success = init(epochSeconds);
683 bool success = init(epochSeconds);
689 const char*
getAbbrev(acetime_t epochSeconds)
const override {
690 bool success = init(epochSeconds);
691 if (!success)
return "";
693 return transition->
abbrev;
701 mTransitionStorage.findTransitionForDateTime(ldt);
702 offset = (transition)
721 acetime_t epochSeconds = odt.toEpochSeconds();
723 mTransitionStorage.findTransition(epochSeconds);
724 offset = (transition)
732 void printTo(Print& printer)
const override;
734 void printShortTo(Print& printer)
const override;
738 logging::println(
"ExtendedZoneProcessor:");
739 logging::println(
" mYear: %d", mYear);
740 logging::println(
" mNumMatches: %d", mNumMatches);
741 for (
int i = 0; i < mNumMatches; i++) {
742 logging::print(
" Match %d: ", i);
746 mTransitionStorage.log();
751 mTransitionStorage.resetHighWater();
756 return mTransitionStorage.getHighWater();
760 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
761 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
762 friend class ::ExtendedZoneProcessorTest_createMatch;
763 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
764 friend class ::ExtendedZoneProcessorTest_findMatches_named;
765 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
766 friend class ::ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
767 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
768 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
769 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
770 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
771 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
772 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
773 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
774 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
775 friend class ::ExtendedZoneProcessorTest_processActiveTransition;
776 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
777 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
778 friend class ::ExtendedZoneProcessorTest_setZoneInfo;
780 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
791 static const uint8_t kMaxMatches = 4;
800 static const uint8_t kMaxTransitions = 8;
806 static const uint8_t kMaxInteriorYears = 4;
817 void setZoneInfo(
const void* zoneInfo)
override {
818 if (mZoneInfo.zoneInfo() == zoneInfo)
return;
832 return mTransitionStorage.findTransition(epochSeconds);
836 bool init(acetime_t epochSeconds)
const {
846 int16_t year = ld.
year();
847 if (isFilled(year))
return true;
848 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
849 logging::println(
"init(): %d", year);
854 mTransitionStorage.init();
856 if (year < mZoneInfo.startYear() - 1 || mZoneInfo.untilYear() < year) {
865 mNumMatches = findMatches(mZoneInfo, startYm, untilYm, mMatches,
867 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
870 findTransitions(mTransitionStorage, mMatches, mNumMatches);
873 fixTransitionTimes(begin, end);
874 generateStartUntilTimes(begin, end);
875 calcAbbreviations(begin, end);
882 bool isFilled(int16_t year)
const {
883 return mIsFilled && (year == mYear);
897 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
898 logging::println(
"findMatches()");
902 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
904 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
905 if (iMatch < maxMatches) {
906 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
925 static bool eraOverlapsInterval(
930 return compareEraToYearMonth(prev, untilYm.yearTiny, untilYm.month) < 0
931 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
936 int8_t yearTiny, uint8_t month) {
937 if (era.untilYearTiny() < yearTiny)
return -1;
938 if (era.untilYearTiny() > yearTiny)
return 1;
939 if (era.untilMonth() < month)
return -1;
940 if (era.untilMonth() > month)
return 1;
941 if (era.untilDay() > 1)
return 1;
943 if (era.untilTimeCode() > 0)
return 1;
959 prev.untilYearTiny(), prev.untilMonth(), prev.untilDay(),
960 (int8_t) prev.untilTimeCode(), prev.untilTimeModifier()
963 startYm.yearTiny, startYm.month, 1, 0,
'w' 965 if (startDate < lowerBound) {
966 startDate = lowerBound;
970 era.untilYearTiny(), era.untilMonth(), era.untilDay(),
971 (int8_t) era.untilTimeCode(), era.untilTimeModifier()
974 untilYm.yearTiny, untilYm.month, 1, 0,
'w' 976 if (upperBound < untilDate) {
977 untilDate = upperBound;
980 return {startDate, untilDate, era};
987 static void findTransitions(
990 uint8_t numMatches) {
991 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
992 logging::println(
"findTransitions()");
994 for (uint8_t i = 0; i < numMatches; i++) {
995 findTransitionsForMatch(transitionStorage, &matches[i]);
1000 static void findTransitionsForMatch(
1003 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1004 logging::println(
"findTransitionsForMatch()");
1007 if (policy.isNull()) {
1008 findTransitionsFromSimpleMatch(transitionStorage, match);
1010 findTransitionsFromNamedMatch(transitionStorage, match);
1014 static void findTransitionsFromSimpleMatch(
1017 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1018 logging::println(
"findTransitionsFromSimpleMatch()");
1021 createTransitionForYear(freeTransition, 0 ,
1026 static void findTransitionsFromNamedMatch(
1029 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1030 logging::println(
"findTransitionsFromNamedMatch()");
1033 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1034 match->log(); logging::println();
1036 findCandidateTransitions(transitionStorage, match);
1037 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1038 transitionStorage.
log(); logging::println();
1041 transitionStorage.getCandidatePoolBegin(),
1042 transitionStorage.getCandidatePoolEnd());
1043 selectActiveTransitions(transitionStorage, match);
1044 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1045 transitionStorage.
log(); logging::println();
1049 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1050 transitionStorage.
log(); logging::println();
1054 static void findCandidateTransitions(
1057 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1058 logging::print(
"findCandidateTransitions(): ");
1063 uint8_t numRules = policy.numRules();
1068 (*prior)->
active =
false;
1069 for (uint8_t r = 0; r < numRules; r++) {
1073 int8_t interiorYears[kMaxInteriorYears];
1074 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1075 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1076 for (uint8_t y = 0; y < numYears; y++) {
1077 int8_t year = interiorYears[y];
1079 createTransitionForYear(t, year, rule, match);
1080 int8_t status = compareTransitionToMatchFuzzy(t, match);
1082 setAsPriorTransition(transitionStorage, t);
1083 }
else if (status == 1) {
1089 int8_t priorYear = getMostRecentPriorYear(
1090 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1092 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1094 "findCandidateTransitions(): priorYear: %d", priorYear);
1097 createTransitionForYear(t, priorYear, rule, match);
1098 setAsPriorTransition(transitionStorage, t);
1104 if ((*prior)->active) {
1105 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1107 "findCandidateTransitions(): adding prior to Candidate pool");
1117 static uint8_t calcInteriorYears(int8_t* interiorYears,
1118 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1119 int8_t startYear, int8_t endYear) {
1121 for (int8_t year = startYear; year <= endYear; year++) {
1122 if (fromYear <= year && year <= toYear) {
1123 interiorYears[i] = year;
1125 if (i >= maxInteriorYears)
break;
1145 if (rule.isNotNull()) {
1149 char letter = rule.letter();
1152 if (letter !=
'-') {
1172 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1173 int8_t startYear, int8_t ) {
1174 if (fromYear < startYear) {
1175 if (toYear < startYear) {
1178 return startYear - 1;
1189 rule.onDayOfMonth());
1190 return {yearTiny, monthDay.month, monthDay.day,
1191 (int8_t) rule.atTimeCode(), rule.atTimeModifier()};
1204 static int8_t compareTransitionToMatchFuzzy(
1209 int16_t matchStartMonths = match->
startDateTime.yearTiny * 12
1211 if (ttMonths < matchStartMonths - 1)
return -1;
1213 int16_t matchUntilMonths = match->
untilDateTime.yearTiny * 12
1215 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1221 static void setAsPriorTransition(
1224 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1225 logging::println(
"setAsPriorTransition()");
1247 static void fixTransitionTimes(
1249 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1250 logging::println(
"fixTransitionTimes(): #transitions: %d;",
1251 (
int) (end - begin));
1259 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1260 logging::println(
"fixTransitionTimes(): LOOP");
1269 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1270 logging::println(
"fixTransitionTimes(): END");
1281 int8_t offsetCode, int8_t deltaCode) {
1282 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1283 logging::println(
"expandDateTuple()");
1285 if (tt->modifier ==
's') {
1287 *ttu = {tt->yearTiny, tt->month, tt->day,
1288 (int8_t) (tt->timeCode - offsetCode),
'u'};
1289 *tt = {tt->yearTiny, tt->month, tt->day,
1290 (int8_t) (tt->timeCode + deltaCode),
'w'};
1291 }
else if (tt->modifier ==
'u') {
1293 *tts = {tt->yearTiny, tt->month, tt->day,
1294 (int8_t) (tt->timeCode + offsetCode),
's'};
1295 *tt = {tt->yearTiny, tt->month, tt->day,
1296 (int8_t) (tt->timeCode + offsetCode + deltaCode),
'w'};
1300 *tts = {tt->yearTiny, tt->month, tt->day,
1301 (int8_t) (tt->timeCode - deltaCode),
's'};
1302 *ttu = {tt->yearTiny, tt->month, tt->day,
1303 (int8_t) (tt->timeCode - deltaCode - offsetCode),
'u'};
1306 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1307 logging::println(
"expandDateTuple(): normalizeDateTuple(): 1");
1309 normalizeDateTuple(tt);
1310 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1311 logging::println(
"expandDateTuple(): normalizeDateTuple(): 2");
1313 normalizeDateTuple(tts);
1314 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1315 logging::println(
"expandDateTuple(): normalizeDateTuple(): 3");
1317 normalizeDateTuple(ttu);
1322 const int8_t kOneDayAsCode = 4 * 24;
1323 if (dt->timeCode <= -kOneDayAsCode) {
1325 dt->yearTiny, dt->month, dt->day);
1326 local_date_mutation::decrementOneDay(ld);
1328 dt->month = ld.
month();
1330 dt->timeCode += kOneDayAsCode;
1331 }
else if (kOneDayAsCode <= dt->timeCode) {
1333 dt->yearTiny, dt->month, dt->day);
1334 local_date_mutation::incrementOneDay(ld);
1336 dt->month = ld.
month();
1338 dt->timeCode -= kOneDayAsCode;
1348 static void selectActiveTransitions(
1353 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1354 logging::println(
"selectActiveTransitions(): #candidates: %d",
1355 (
int) (end - begin));
1360 processActiveTransition(match, transition, &prior);
1366 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1368 "selectActiveTransitions(): found latest prior");
1382 static void processActiveTransition(
1386 int8_t status = compareTransitionToMatch(transition, match);
1388 transition->
active =
false;
1389 }
else if (status == 1) {
1390 transition->
active =
true;
1391 }
else if (status == 0) {
1393 (*prior)->active =
false;
1395 transition->
active =
true;
1396 (*prior) = transition;
1400 (*prior)->
active =
false;
1401 transition->
active =
true;
1402 (*prior) = transition;
1405 transition->
active =
true;
1406 (*prior) = transition;
1425 static int8_t compareTransitionToMatch(
1431 if (matchStart.modifier ==
's') {
1433 }
else if (matchStart.modifier ==
'u') {
1438 if (*transitionTime < matchStart)
return -1;
1439 if (*transitionTime == matchStart)
return 0;
1442 if (matchUntil.modifier ==
's') {
1444 }
else if (matchUntil.modifier ==
'u') {
1449 if (*transitionTime < matchUntil)
return 1;
1458 static void generateStartUntilTimes(
1460 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1462 "generateStartUntilTimes(): #transitions: %d;",
1463 (
int) (end - begin));
1467 bool isAfterFirst =
false;
1483 t->
startDateTime = {tt.yearTiny, tt.month, tt.day, code, tt.modifier};
1497 const acetime_t offsetSeconds = (acetime_t) 900
1500 st.yearTiny, st.month, st.day);
1504 isAfterFirst =
true;
1511 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1519 static void calcAbbreviations(
1521 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1522 logging::println(
"calcAbbreviations(): #transitions: %d;",
1523 (
int) (end - begin));
1527 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1529 "calcAbbreviations(): format:%s, deltaCode:%d, letter:%s",
1571 static void createAbbreviation(
char* dest, uint8_t destSize,
1572 const char* format, uint8_t deltaCode,
const char* letterString) {
1575 if (letterString ==
nullptr) {
1576 strncpy(dest, format, destSize);
1577 dest[destSize - 1] =
'\0';
1582 if (strchr(format,
'%') !=
nullptr) {
1583 copyAndReplace(dest, destSize, format,
'%', letterString);
1586 const char* slashPos = strchr(format,
'/');
1587 if (slashPos !=
nullptr) {
1588 if (deltaCode == 0) {
1589 uint8_t headLength = (slashPos - format);
1590 if (headLength >= destSize) headLength = destSize - 1;
1591 memcpy(dest, format, headLength);
1592 dest[headLength] =
'\0';
1594 uint8_t tailLength = strlen(slashPos+1);
1595 if (tailLength >= destSize) tailLength = destSize - 1;
1596 memcpy(dest, slashPos+1, tailLength);
1597 dest[tailLength] =
'\0';
1601 strncpy(dest, format, destSize);
1602 dest[destSize - 1] =
'\0';
1612 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
1613 char oldChar,
const char* newString) {
1614 while (*src !=
'\0' && dstSize > 0) {
1615 if (*src == oldChar) {
1616 while (*newString !=
'\0' && dstSize > 0) {
1617 *dst++ = *newString++;
1635 mutable int16_t mYear = 0;
1636 mutable bool mIsFilled =
false;
1638 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.
Base interface for ZoneProcessor classes.
ZoneEraBroker era
The ZoneEra that matched the given year.
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.
uint8_t minute() const
Return the minute.
void addActiveCandidatesToActivePool()
Add active candidates into the Active pool, and collapse the Candidate pool.
DateTuple transitionTimeU
Version of transitionTime in 'u' mode, using the UTC offset of the previous transition.
void resetCandidatePool()
Empty the Candidate pool by resetting the various indexes.
char letterBuf[2]
Storage for the single letter 'letter' field if 'rule' is not null.
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
DateTuple startDateTime
The effective start time of the matching ZoneEra.
Transition * getFreeAgent()
Return a pointer to the first Transition in the free pool.
A cache of ZoneProcessors that provides a ZoneProcessor to the TimeZone upon request.
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 getHighWater() const
Return the high water mark.
bool isError() const
Return true if this TimeOffset represents an error.
TransitionStorage()
Constructor.
static OffsetDateTime forLocalDateTimeAndOffset(const LocalDateTime &localDateTime, TimeOffset timeOffset)
Factory method from LocalDateTime and TimeOffset.
DateTuple originalTransitionTime
If the transition is shifted to the beginning of a ZoneMatch, this is set to the transitionTime for d...
uint8_t day() const
Return the day of the month.
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
The result of calcStartDayOfMonth().
virtual const void * getZoneInfo() const =0
Return the opaque zoneInfo.
Represents an interval of time where the time zone obeyed a certain UTC offset and DST delta...
void log() const
Verify that the indexes are valid.
const void * getZoneInfo() const override
Return the underlying ZoneInfo.
void addPriorToCandidatePool()
Add the current prior into the Candidates pool.
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
Representation of a given time zone, implemented as an array of ZoneEra records.
void setFreeAgentAsPrior()
Swap the Free agrent transition with the current Prior transition.
Transition * getPrior()
Return the current prior transition.
static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month, uint8_t day)
Factory method using components with an int8_t yearTiny.
TimeOffset getUtcOffset(acetime_t epochSeconds) const override
Return the total UTC offset at epochSeconds, including DST offset.
char abbrev[kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
const char * letter() const
Return the letter string.
An entry in ZoneInfo which describes which ZonePolicy was being followed during a particular time per...
uint8_t day() const
Return the day of the month.
uint8_t month() const
Return the month with January=1, December=12.
void init()
Initialize all pools.
static OffsetDateTime forEpochSeconds(acetime_t epochSeconds, TimeOffset timeOffset)
Factory method.
void addFreeAgentToCandidatePool()
Add the free agent Transition at index mIndexFree to the Candidate pool, sorted by transitionTime...
DateTuple transitionTime
The original transition time, usually 'w' but sometimes 's' or 'u'.
acetime_t toEpochSeconds() const
Return the number of seconds since AceTime epoch (2000-01-01 00:00:00).
const LocalDate & localDate() const
Return the LocalDate.
static const uint8_t kAbbrevSize
Size of the timezone abbreviation.
An implementation of ZoneProcessor that works for all zones defined by the TZ Database (with some zon...
const Transition * findTransitionForDateTime(const LocalDateTime &ldt) const
Return the Transition matching the given dateTime.
uint8_t hour() const
Return the hour.
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
DateTuple transitionTimeS
Version of transitionTime in 's' mode, using the UTC offset of the previous Transition.
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.
void resetTransitionHighWater()
Reset the TransitionStorage high water mark.
bool active
Flag used for 2 slightly different meanings at different stages of init() processing.
uint32_t getZoneId() const override
Return the unique stable zoneId.
The date (year, month, day), time (hour, minute, second) and offset from UTC (timeOffset).
void log() const
Used only for debugging.
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'...
void resetHighWater()
Reset the high water mark.
Macros and definitions that provide a consistency layer among the various Arduino boards for compatib...
void log() const
Used only for debugging.
A thin wrapper that represents a time offset from a reference point, usually 00:00 at UTC...
int8_t deltaCode
The DST delta code.
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.
void log() const
Used only for debugging.
ExtendedZoneProcessor(const extended::ZoneInfo *zoneInfo=nullptr)
Constructor.
uint8_t getTransitionHighWater() const
Get the TransitionStorage high water mark.
static const int16_t kEpochYear
Base year of epoch.
DateTuple untilDateTime
The effective until time of the matching ZoneEra.
DateTuple startDateTime
Start time expressed using the UTC offset of the current Transition.
uint8_t month() const
Return the month with January=1, December=12.
int16_t year() const
Return the full year instead of just the last 2 digits.
static basic::MonthDay calcStartDayOfMonth(int16_t year, uint8_t month, uint8_t onDayOfWeek, int8_t onDayOfMonth)
Calculate the actual (month, day) of the expresssion (onDayOfWeek >= onDayOfMonth) or (onDayOfWeek <=...
A tuple that represents a date and time, using a timeCode that tracks the time component using 15-min...
Data broker for accessing ZonePolicy in PROGMEM.
const Transition * findTransition(acetime_t epochSeconds) const
Return the Transition matching the given epochSeconds.
OffsetDateTime getOffsetDateTime(const LocalDateTime &ldt) const override
Return the best estimate of the OffsetDateTime at the given LocalDateTime for the timezone of the cur...
int8_t offsetCode
The base offset code, not the total effective UTC offset.
ZoneRuleBroker rule
The Zone transition rule that matched for the the given year.
Data structure that captures the matching ZoneEra and its ZoneRule transitions for a given year...
Class that holds the date-time as the components (year, month, day, hour, minute, second) without reg...