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;
51 class TransitionStorageTest_findTransitionForDateTime;
55 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
56 class ZoneProcessorCacheImpl;
67 DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
68 yearTiny(y), month(mon), day(d), modifier(mod), minutes(min) {}
78 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
79 char c =
"wsu"[(modifier>>4)];
80 logging::printf(
"DateTuple(%04d-%02u-%02uT%d'%c')",
88 if (a.yearTiny < b.yearTiny)
return true;
89 if (a.yearTiny > b.yearTiny)
return false;
90 if (a.month < b.month)
return true;
91 if (a.month > b.month)
return false;
92 if (a.day < b.day)
return true;
93 if (a.day > b.day)
return false;
94 if (a.minutes < b.minutes)
return true;
95 if (a.minutes > b.minutes)
return false;
113 return a.yearTiny == b.yearTiny
114 && a.month == b.month
116 && a.minutes == b.minutes
117 && a.modifier == b.modifier;
141 logging::printf(
"ZoneMatch(");
142 logging::printf(
"Start:"); startDateTime.
log();
143 logging::printf(
"; Until:"); untilDateTime.
log();
144 logging::printf(
"; Era: %snull", (era.isNotNull()) ?
"!" :
"");
145 logging::printf(
")");
236 char abbrev[kAbbrevSize];
269 const char* format()
const {
270 return match->
era.format();
286 char letter = rule.letter();
295 uint8_t numLetters = policy.numLetters();
296 if (letter >= numLetters) {
304 return policy.letter(letter);
309 logging::printf(
"Transition(");
310 if (
sizeof(acetime_t) ==
sizeof(
int)) {
311 logging::printf(
"sE: %d", startEpochSeconds);
313 logging::printf(
"sE: %ld", startEpochSeconds);
315 logging::printf(
"; match: %snull", (match) ?
"!" :
"");
316 logging::printf(
"; era: %snull",
317 (match && match->
era.isNotNull()) ?
"!" :
"");
318 logging::printf(
"; oCode: %d", offsetCode);
319 logging::printf(
"; dCode: %d", deltaCode);
320 logging::printf(
"; tt: "); transitionTime.
log();
321 if (rule.isNotNull()) {
322 logging::printf(
"; R.fY: %d", rule.fromYearTiny());
323 logging::printf(
"; R.tY: %d", rule.toYearTiny());
324 logging::printf(
"; R.M: %d", rule.inMonth());
325 logging::printf(
"; R.dow: %d", rule.onDayOfWeek());
326 logging::printf(
"; R.dom: %d", rule.onDayOfMonth());
357 template<u
int8_t SIZE>
365 for (uint8_t i = 0; i < SIZE; i++) {
366 mTransitions[i] = &mPool[i];
369 mIndexCandidates = 0;
392 mIndexCandidates = mIndexPrior;
393 mIndexFree = mIndexPrior;
397 return &mTransitions[mIndexCandidates];
400 return &mTransitions[mIndexFree];
403 Transition** getActivePoolBegin() {
return &mTransitions[0]; }
404 Transition** getActivePoolEnd() {
return &mTransitions[mIndexFree]; }
414 if (mIndexFree > mHighWater) {
415 mHighWater = mIndexFree;
418 if (mIndexFree < SIZE) {
419 return mTransitions[mIndexFree];
421 return mTransitions[SIZE - 1];
433 if (mIndexFree >= SIZE)
return;
435 mIndexPrior = mIndexFree;
436 mIndexCandidates = mIndexFree;
447 return &mTransitions[mIndexPrior];
452 swap(&mTransitions[mIndexPrior], &mTransitions[mIndexFree]);
471 if (mIndexFree >= SIZE)
return;
472 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
476 mTransitions[i] = prev;
477 mTransitions[i - 1] = curr;
487 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
488 logging::printf(
"addActiveCandidatesToActivePool()\n");
490 uint8_t iActive = mIndexPrior;
491 uint8_t iCandidate = mIndexCandidates;
492 for (; iCandidate < mIndexFree; iCandidate++) {
493 if (mTransitions[iCandidate]->active) {
494 if (iActive != iCandidate) {
495 swap(&mTransitions[iActive], &mTransitions[iCandidate]);
500 mIndexPrior = iActive;
501 mIndexCandidates = iActive;
502 mIndexFree = iActive;
514 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
515 logging::printf(
"findTransition(): mIndexFree: %d\n", mIndexFree);
519 for (uint8_t i = 0; i < mIndexFree; i++) {
520 const Transition* candidate = mTransitions[i];
552 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
554 "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
564 for (uint8_t i = 0; i < mIndexFree; i++) {
565 const Transition* candidate = mTransitions[i];
574 logging::printf(
"TransitionStorage:\n");
575 logging::printf(
" mIndexPrior: %d\n", mIndexPrior);
576 logging::printf(
" mIndexCandidates: %d\n", mIndexCandidates);
577 logging::printf(
" mIndexFree: %d\n", mIndexFree);
578 if (mIndexPrior != 0) {
579 logging::printf(
" Actives:\n");
580 for (uint8_t i = 0; i < mIndexPrior; i++) {
581 mTransitions[i]->log();
582 logging::printf(
"\n");
585 if (mIndexPrior != mIndexCandidates) {
586 logging::printf(
" Prior: ");
587 mTransitions[mIndexPrior]->log();
588 logging::printf(
"\n");
590 if (mIndexCandidates != mIndexFree) {
591 logging::printf(
" Candidates:\n");
592 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
593 mTransitions[i]->log();
594 logging::printf(
"\n");
610 friend class ::TransitionStorageTest_getFreeAgent;
611 friend class ::TransitionStorageTest_getFreeAgent2;
612 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
613 friend class ::TransitionStorageTest_reservePrior;
614 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
615 friend class ::TransitionStorageTest_setFreeAgentAsPrior;
616 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
617 friend class ::TransitionStorageTest_findTransitionForDateTime;
618 friend class ::TransitionStorageTest_resetCandidatePool;
621 Transition* getTransition(uint8_t i) {
return mTransitions[i]; }
626 uint8_t mIndexCandidates;
630 uint8_t mHighWater = 0;
667 mZoneInfo(zoneInfo) {}
671 return mZoneInfo.zoneInfo();
674 uint32_t
getZoneId()
const override {
return mZoneInfo.zoneId(); }
677 bool success = init(epochSeconds);
687 bool success = init(epochSeconds);
693 const char*
getAbbrev(acetime_t epochSeconds)
const override {
694 bool success = init(epochSeconds);
695 if (!success)
return "";
697 return transition->
abbrev;
702 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
703 logging::printf(
"getOffsetDateTime(): ldt=");
704 ldt.
printTo(SERIAL_PORT_MONITOR);
705 SERIAL_PORT_MONITOR.println();
712 mTransitionStorage.findTransitionForDateTime(ldt);
713 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
714 logging::printf(
"getOffsetDateTime(): match transition=");
716 logging::printf(
"\n");
718 offset = (transition)
730 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
731 logging::printf(
"getOffsetDateTime(): odt=");
732 odt.printTo(SERIAL_PORT_MONITOR);
733 SERIAL_PORT_MONITOR.println();
742 acetime_t epochSeconds = odt.toEpochSeconds();
744 mTransitionStorage.findTransition(epochSeconds);
745 offset = (transition)
750 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
751 logging::printf(
"getOffsetDateTime(): normalized(odt)=");
752 odt.printTo(SERIAL_PORT_MONITOR);
753 SERIAL_PORT_MONITOR.println();
758 void printTo(Print& printer)
const override;
760 void printShortTo(Print& printer)
const override;
764 logging::printf(
"ExtendedZoneProcessor:\n");
765 logging::printf(
" mYear: %d\n", mYear);
766 logging::printf(
" mNumMatches: %d\n", mNumMatches);
767 for (
int i = 0; i < mNumMatches; i++) {
768 logging::printf(
" Match %d: ", i);
770 logging::printf(
"\n");
772 mTransitionStorage.log();
777 mTransitionStorage.resetHighWater();
782 return mTransitionStorage.getHighWater();
786 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
787 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
788 friend class ::ExtendedZoneProcessorTest_createMatch;
789 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
790 friend class ::ExtendedZoneProcessorTest_findMatches_named;
791 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
792 friend class ::ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
793 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
794 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
795 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
796 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
797 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
798 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
799 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
800 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
801 friend class ::ExtendedZoneProcessorTest_processActiveTransition;
802 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
803 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
804 friend class ::ExtendedZoneProcessorTest_setZoneInfo;
805 friend class ::TransitionStorageTest_findTransitionForDateTime;
807 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
818 static const uint8_t kMaxMatches = 4;
827 static const uint8_t kMaxTransitions = 8;
833 static const uint8_t kMaxInteriorYears = 4;
844 void setZoneInfo(
const void* zoneInfo)
override {
845 if (mZoneInfo.zoneInfo() == zoneInfo)
return;
859 return mTransitionStorage.findTransition(epochSeconds);
863 bool init(acetime_t epochSeconds)
const {
873 int16_t year = ld.
year();
874 if (isFilled(year))
return true;
875 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
876 logging::printf(
"init(): %d\n", year);
881 mTransitionStorage.init();
883 if (year < mZoneInfo.startYear() - 1 || mZoneInfo.untilYear() < year) {
892 mNumMatches = findMatches(mZoneInfo, startYm, untilYm, mMatches,
894 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
895 findTransitions(mTransitionStorage, mMatches, mNumMatches);
898 fixTransitionTimes(begin, end);
899 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
900 generateStartUntilTimes(begin, end);
901 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
902 calcAbbreviations(begin, end);
903 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
910 bool isFilled(int16_t year)
const {
911 return mIsFilled && (year == mYear);
925 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
926 logging::printf(
"findMatches()\n");
930 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
932 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
933 if (iMatch < maxMatches) {
934 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
953 static bool eraOverlapsInterval(
958 return compareEraToYearMonth(prev, untilYm.yearTiny, untilYm.month) < 0
959 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
964 int8_t yearTiny, uint8_t month) {
965 if (era.untilYearTiny() < yearTiny)
return -1;
966 if (era.untilYearTiny() > yearTiny)
return 1;
967 if (era.untilMonth() < month)
return -1;
968 if (era.untilMonth() > month)
return 1;
969 if (era.untilDay() > 1)
return 1;
971 if (era.untilTimeMinutes() > 0)
return 1;
987 prev.untilYearTiny(), prev.untilMonth(), prev.untilDay(),
988 (int16_t) prev.untilTimeMinutes(), prev.untilTimeModifier()
991 startYm.yearTiny, startYm.month, 1, 0,
994 if (startDate < lowerBound) {
995 startDate = lowerBound;
999 era.untilYearTiny(), era.untilMonth(), era.untilDay(),
1000 (int16_t) era.untilTimeMinutes(), era.untilTimeModifier()
1003 untilYm.yearTiny, untilYm.month, 1, 0,
1006 if (upperBound < untilDate) {
1007 untilDate = upperBound;
1010 return {startDate, untilDate, era};
1017 static void findTransitions(
1020 uint8_t numMatches) {
1021 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1022 logging::printf(
"findTransitions()\n");
1024 for (uint8_t i = 0; i < numMatches; i++) {
1025 findTransitionsForMatch(transitionStorage, &matches[i]);
1030 static void findTransitionsForMatch(
1033 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1034 logging::printf(
"findTransitionsForMatch()\n");
1037 if (policy.isNull()) {
1038 findTransitionsFromSimpleMatch(transitionStorage, match);
1040 findTransitionsFromNamedMatch(transitionStorage, match);
1044 static void findTransitionsFromSimpleMatch(
1047 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1048 logging::printf(
"findTransitionsFromSimpleMatch()\n");
1051 createTransitionForYear(freeTransition, 0 ,
1056 static void findTransitionsFromNamedMatch(
1059 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1060 logging::printf(
"findTransitionsFromNamedMatch()\n");
1063 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1064 match->log(); logging::printf(
"\n");
1066 findCandidateTransitions(transitionStorage, match);
1067 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1068 transitionStorage.
log(); logging::printf(
"\n");
1071 transitionStorage.getCandidatePoolBegin(),
1072 transitionStorage.getCandidatePoolEnd());
1073 selectActiveTransitions(transitionStorage, match);
1074 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1075 transitionStorage.
log(); logging::printf(
"\n");
1079 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1080 transitionStorage.
log(); logging::printf(
"\n");
1084 static void findCandidateTransitions(
1087 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1088 logging::printf(
"findCandidateTransitions(): ");
1090 logging::printf(
"\n");
1093 uint8_t numRules = policy.numRules();
1098 (*prior)->
active =
false;
1099 for (uint8_t r = 0; r < numRules; r++) {
1103 int8_t interiorYears[kMaxInteriorYears];
1104 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1105 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1106 for (uint8_t y = 0; y < numYears; y++) {
1107 int8_t year = interiorYears[y];
1109 createTransitionForYear(t, year, rule, match);
1110 int8_t status = compareTransitionToMatchFuzzy(t, match);
1112 setAsPriorTransition(transitionStorage, t);
1113 }
else if (status == 1) {
1119 int8_t priorYear = getMostRecentPriorYear(
1120 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1122 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1124 "findCandidateTransitions(): priorYear: %d\n", priorYear);
1127 createTransitionForYear(t, priorYear, rule, match);
1128 setAsPriorTransition(transitionStorage, t);
1134 if ((*prior)->active) {
1135 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1137 "findCandidateTransitions(): adding prior to Candidate pool\n");
1147 static uint8_t calcInteriorYears(int8_t* interiorYears,
1148 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1149 int8_t startYear, int8_t endYear) {
1151 for (int8_t year = startYear; year <= endYear; year++) {
1152 if (fromYear <= year && year <= toYear) {
1153 interiorYears[i] = year;
1155 if (i >= maxInteriorYears)
break;
1175 if (rule.isNotNull()) {
1179 char letter = rule.letter();
1182 if (letter !=
'-') {
1202 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1203 int8_t startYear, int8_t ) {
1204 if (fromYear < startYear) {
1205 if (toYear < startYear) {
1208 return startYear - 1;
1219 rule.onDayOfMonth());
1220 return {yearTiny, monthDay.month, monthDay.day,
1221 (int16_t) rule.atTimeMinutes(), rule.atTimeModifier()};
1234 static int8_t compareTransitionToMatchFuzzy(
1239 int16_t matchStartMonths = match->
startDateTime.yearTiny * 12
1241 if (ttMonths < matchStartMonths - 1)
return -1;
1243 int16_t matchUntilMonths = match->
untilDateTime.yearTiny * 12
1245 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1251 static void setAsPriorTransition(
1254 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1255 logging::printf(
"setAsPriorTransition()\n");
1277 static void fixTransitionTimes(
1279 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1280 logging::printf(
"fixTransitionTimes(): #transitions: %d;\n",
1281 (
int) (end - begin));
1289 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1290 logging::printf(
"fixTransitionTimes(): LOOP\n");
1292 logging::printf(
"\n");
1299 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1300 logging::printf(
"fixTransitionTimes(): END\n");
1311 int8_t offsetCode, int8_t deltaCode) {
1312 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1313 logging::printf(
"expandDateTuple()\n");
1317 *ttu = {tt->yearTiny, tt->month, tt->day,
1318 (int16_t) (tt->minutes - 15 * offsetCode),
1320 *tt = {tt->yearTiny, tt->month, tt->day,
1321 (int16_t) (tt->minutes + 15 * deltaCode),
1325 *tts = {tt->yearTiny, tt->month, tt->day,
1326 (int16_t) (tt->minutes + 15 * offsetCode),
1328 *tt = {tt->yearTiny, tt->month, tt->day,
1329 (int16_t) (tt->minutes + 15 * (offsetCode + deltaCode)),
1334 *tts = {tt->yearTiny, tt->month, tt->day,
1335 (int16_t) (tt->minutes - 15 * deltaCode),
1337 *ttu = {tt->yearTiny, tt->month, tt->day,
1338 (int16_t) (tt->minutes - 15 * (deltaCode + offsetCode)),
1342 normalizeDateTuple(tt);
1343 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1344 logging::printf(
"expandDateTuple(): normalizeDateTuple(tt): ");
1346 logging::printf(
"\n");
1349 normalizeDateTuple(tts);
1350 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1351 logging::printf(
"expandDateTuple(): normalizeDateTuple(tts): ");
1353 logging::printf(
"\n");
1356 normalizeDateTuple(ttu);
1357 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1358 logging::printf(
"expandDateTuple(): normalizeDateTuple(ttu): ");
1360 logging::printf(
"\n");
1369 const int16_t kOneDayAsMinutes = 60 * 24;
1370 if (dt->minutes <= -kOneDayAsMinutes) {
1372 dt->yearTiny, dt->month, dt->day);
1373 local_date_mutation::decrementOneDay(ld);
1375 dt->month = ld.
month();
1377 dt->minutes += kOneDayAsMinutes;
1378 }
else if (kOneDayAsMinutes <= dt->minutes) {
1380 dt->yearTiny, dt->month, dt->day);
1381 local_date_mutation::incrementOneDay(ld);
1383 dt->month = ld.
month();
1385 dt->minutes -= kOneDayAsMinutes;
1395 static void selectActiveTransitions(
1400 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1401 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1402 (
int) (end - begin));
1407 processActiveTransition(match, transition, &prior);
1413 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1415 "selectActiveTransitions(): found latest prior\n");
1429 static void processActiveTransition(
1433 int8_t status = compareTransitionToMatch(transition, match);
1435 transition->
active =
false;
1436 }
else if (status == 1) {
1437 transition->
active =
true;
1438 }
else if (status == 0) {
1440 (*prior)->active =
false;
1442 transition->
active =
true;
1443 (*prior) = transition;
1447 (*prior)->
active =
false;
1448 transition->
active =
true;
1449 (*prior) = transition;
1452 transition->
active =
true;
1453 (*prior) = transition;
1472 static int8_t compareTransitionToMatch(
1480 }
else if (matchStart.modifier ==
1486 if (*transitionTime < matchStart)
return -1;
1487 if (*transitionTime == matchStart)
return 0;
1492 }
else if (matchUntil.modifier ==
1498 if (*transitionTime < matchUntil)
return 1;
1507 static void generateStartUntilTimes(
1509 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1511 "generateStartUntilTimes(): #transitions: %d;\n",
1512 (
int) (end - begin));
1516 bool isAfterFirst =
false;
1530 int16_t minutes = tt.minutes + 15 * (
1548 const acetime_t offsetSeconds = (acetime_t) 60
1551 st.yearTiny, st.month, st.day);
1555 isAfterFirst =
true;
1562 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1570 static void calcAbbreviations(
1572 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1573 logging::printf(
"calcAbbreviations(): #transitions: %d;\n",
1574 (
int) (end - begin));
1578 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1580 "calcAbbreviations(): format:%s, deltaCode:%d, letter:%s\n",
1622 static void createAbbreviation(
char* dest, uint8_t destSize,
1623 const char* format, uint8_t deltaCode,
const char* letterString) {
1626 if (letterString ==
nullptr) {
1627 strncpy(dest, format, destSize);
1628 dest[destSize - 1] =
'\0';
1633 if (strchr(format,
'%') !=
nullptr) {
1634 copyAndReplace(dest, destSize, format,
'%', letterString);
1637 const char* slashPos = strchr(format,
'/');
1638 if (slashPos !=
nullptr) {
1639 if (deltaCode == 0) {
1640 uint8_t headLength = (slashPos - format);
1641 if (headLength >= destSize) headLength = destSize - 1;
1642 memcpy(dest, format, headLength);
1643 dest[headLength] =
'\0';
1645 uint8_t tailLength = strlen(slashPos+1);
1646 if (tailLength >= destSize) tailLength = destSize - 1;
1647 memcpy(dest, slashPos+1, tailLength);
1648 dest[tailLength] =
'\0';
1652 strncpy(dest, format, destSize);
1653 dest[destSize - 1] =
'\0';
1663 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
1664 char oldChar,
const char* newString) {
1665 while (*src !=
'\0' && dstSize > 0) {
1666 if (*src == oldChar) {
1667 while (*newString !=
'\0' && dstSize > 0) {
1668 *dst++ = *newString++;
1686 mutable int16_t mYear = 0;
1687 mutable bool mIsFilled =
false;
1689 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.
void printTo(Print &printer) const
Print LocalDateTime to 'printer' in ISO 8601 format.
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.
Data broker for accessing ZonePolicy in PROGMEM.
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.
static const uint8_t TIME_MODIFIER_W
Represents 'w' or wall time.
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.
static const uint8_t TIME_MODIFIER_S
Represents 's' or standard time.
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 supports for all zones defined by the TZ Database.
static const uint8_t TIME_MODIFIER_U
Represents 'u' or UTC time.
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.
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...