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), suffix(mod), minutes(min) {}
78 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
79 char c =
"wsu"[(suffix>>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.suffix == b.suffix;
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;
854 void setZoneInfo(
const void* zoneInfo)
override {
855 if (mZoneInfo.zoneInfo() == zoneInfo)
return;
869 return mTransitionStorage.findTransition(epochSeconds);
873 bool init(acetime_t epochSeconds)
const {
883 int16_t year = ld.
year();
884 if (isFilled(year))
return true;
885 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
886 logging::printf(
"init(): %d\n", year);
891 mTransitionStorage.init();
893 if (year < mZoneInfo.startYear() - 1 || mZoneInfo.untilYear() < year) {
902 mNumMatches = findMatches(mZoneInfo, startYm, untilYm, mMatches,
904 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
905 findTransitions(mTransitionStorage, mMatches, mNumMatches);
908 fixTransitionTimes(begin, end);
909 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
910 generateStartUntilTimes(begin, end);
911 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
912 calcAbbreviations(begin, end);
913 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
920 bool isFilled(int16_t year)
const {
921 return mIsFilled && (year == mYear);
935 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
936 logging::printf(
"findMatches()\n");
940 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
942 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
943 if (iMatch < maxMatches) {
944 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
963 static bool eraOverlapsInterval(
968 return compareEraToYearMonth(prev, untilYm.yearTiny, untilYm.month) < 0
969 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
974 int8_t yearTiny, uint8_t month) {
975 if (era.untilYearTiny() < yearTiny)
return -1;
976 if (era.untilYearTiny() > yearTiny)
return 1;
977 if (era.untilMonth() < month)
return -1;
978 if (era.untilMonth() > month)
return 1;
979 if (era.untilDay() > 1)
return 1;
981 if (era.untilTimeMinutes() > 0)
return 1;
997 prev.untilYearTiny(), prev.untilMonth(), prev.untilDay(),
998 (int16_t) prev.untilTimeMinutes(), prev.untilTimeSuffix()
1001 startYm.yearTiny, startYm.month, 1, 0,
1004 if (startDate < lowerBound) {
1005 startDate = lowerBound;
1009 era.untilYearTiny(), era.untilMonth(), era.untilDay(),
1010 (int16_t) era.untilTimeMinutes(), era.untilTimeSuffix()
1013 untilYm.yearTiny, untilYm.month, 1, 0,
1016 if (upperBound < untilDate) {
1017 untilDate = upperBound;
1020 return {startDate, untilDate, era};
1027 static void findTransitions(
1030 uint8_t numMatches) {
1031 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1032 logging::printf(
"findTransitions()\n");
1034 for (uint8_t i = 0; i < numMatches; i++) {
1035 findTransitionsForMatch(transitionStorage, &matches[i]);
1040 static void findTransitionsForMatch(
1043 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1044 logging::printf(
"findTransitionsForMatch()\n");
1047 if (policy.isNull()) {
1048 findTransitionsFromSimpleMatch(transitionStorage, match);
1050 findTransitionsFromNamedMatch(transitionStorage, match);
1054 static void findTransitionsFromSimpleMatch(
1057 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1058 logging::printf(
"findTransitionsFromSimpleMatch()\n");
1061 createTransitionForYear(freeTransition, 0 ,
1066 static void findTransitionsFromNamedMatch(
1069 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1070 logging::printf(
"findTransitionsFromNamedMatch()\n");
1073 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1074 match->log(); logging::printf(
"\n");
1076 findCandidateTransitions(transitionStorage, match);
1077 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1078 transitionStorage.
log(); logging::printf(
"\n");
1081 transitionStorage.getCandidatePoolBegin(),
1082 transitionStorage.getCandidatePoolEnd());
1083 selectActiveTransitions(transitionStorage, match);
1084 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1085 transitionStorage.
log(); logging::printf(
"\n");
1089 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1090 transitionStorage.
log(); logging::printf(
"\n");
1094 static void findCandidateTransitions(
1097 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1098 logging::printf(
"findCandidateTransitions(): ");
1100 logging::printf(
"\n");
1103 uint8_t numRules = policy.numRules();
1108 (*prior)->
active =
false;
1109 for (uint8_t r = 0; r < numRules; r++) {
1113 int8_t interiorYears[kMaxInteriorYears];
1114 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1115 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1116 for (uint8_t y = 0; y < numYears; y++) {
1117 int8_t year = interiorYears[y];
1119 createTransitionForYear(t, year, rule, match);
1120 int8_t status = compareTransitionToMatchFuzzy(t, match);
1122 setAsPriorTransition(transitionStorage, t);
1123 }
else if (status == 1) {
1129 int8_t priorYear = getMostRecentPriorYear(
1130 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1132 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1134 "findCandidateTransitions(): priorYear: %d\n", priorYear);
1137 createTransitionForYear(t, priorYear, rule, match);
1138 setAsPriorTransition(transitionStorage, t);
1144 if ((*prior)->active) {
1145 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1147 "findCandidateTransitions(): adding prior to Candidate pool\n");
1157 static uint8_t calcInteriorYears(int8_t* interiorYears,
1158 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1159 int8_t startYear, int8_t endYear) {
1161 for (int8_t year = startYear; year <= endYear; year++) {
1162 if (fromYear <= year && year <= toYear) {
1163 interiorYears[i] = year;
1165 if (i >= maxInteriorYears)
break;
1185 if (rule.isNotNull()) {
1189 char letter = rule.letter();
1192 if (letter !=
'-') {
1212 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1213 int8_t startYear, int8_t ) {
1214 if (fromYear < startYear) {
1215 if (toYear < startYear) {
1218 return startYear - 1;
1229 rule.onDayOfMonth());
1230 return {yearTiny, monthDay.month, monthDay.day,
1231 (int16_t) rule.atTimeMinutes(), rule.atTimeSuffix()};
1244 static int8_t compareTransitionToMatchFuzzy(
1249 int16_t matchStartMonths = match->
startDateTime.yearTiny * 12
1251 if (ttMonths < matchStartMonths - 1)
return -1;
1253 int16_t matchUntilMonths = match->
untilDateTime.yearTiny * 12
1255 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1261 static void setAsPriorTransition(
1264 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1265 logging::printf(
"setAsPriorTransition()\n");
1287 static void fixTransitionTimes(
1289 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1290 logging::printf(
"fixTransitionTimes(): #transitions: %d;\n",
1291 (
int) (end - begin));
1299 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1300 logging::printf(
"fixTransitionTimes(): LOOP\n");
1302 logging::printf(
"\n");
1309 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1310 logging::printf(
"fixTransitionTimes(): END\n");
1321 int8_t offsetCode, int8_t deltaCode) {
1322 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1323 logging::printf(
"expandDateTuple()\n");
1327 *ttu = {tt->yearTiny, tt->month, tt->day,
1328 (int16_t) (tt->minutes - 15 * offsetCode),
1330 *tt = {tt->yearTiny, tt->month, tt->day,
1331 (int16_t) (tt->minutes + 15 * deltaCode),
1335 *tts = {tt->yearTiny, tt->month, tt->day,
1336 (int16_t) (tt->minutes + 15 * offsetCode),
1338 *tt = {tt->yearTiny, tt->month, tt->day,
1339 (int16_t) (tt->minutes + 15 * (offsetCode + deltaCode)),
1344 *tts = {tt->yearTiny, tt->month, tt->day,
1345 (int16_t) (tt->minutes - 15 * deltaCode),
1347 *ttu = {tt->yearTiny, tt->month, tt->day,
1348 (int16_t) (tt->minutes - 15 * (deltaCode + offsetCode)),
1352 normalizeDateTuple(tt);
1353 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1354 logging::printf(
"expandDateTuple(): normalizeDateTuple(tt): ");
1356 logging::printf(
"\n");
1359 normalizeDateTuple(tts);
1360 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1361 logging::printf(
"expandDateTuple(): normalizeDateTuple(tts): ");
1363 logging::printf(
"\n");
1366 normalizeDateTuple(ttu);
1367 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1368 logging::printf(
"expandDateTuple(): normalizeDateTuple(ttu): ");
1370 logging::printf(
"\n");
1379 const int16_t kOneDayAsMinutes = 60 * 24;
1380 if (dt->minutes <= -kOneDayAsMinutes) {
1382 dt->yearTiny, dt->month, dt->day);
1383 local_date_mutation::decrementOneDay(ld);
1385 dt->month = ld.
month();
1387 dt->minutes += kOneDayAsMinutes;
1388 }
else if (kOneDayAsMinutes <= dt->minutes) {
1390 dt->yearTiny, dt->month, dt->day);
1391 local_date_mutation::incrementOneDay(ld);
1393 dt->month = ld.
month();
1395 dt->minutes -= kOneDayAsMinutes;
1405 static void selectActiveTransitions(
1410 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1411 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1412 (
int) (end - begin));
1417 processActiveTransition(match, transition, &prior);
1423 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1425 "selectActiveTransitions(): found latest prior\n");
1439 static void processActiveTransition(
1443 int8_t status = compareTransitionToMatch(transition, match);
1445 transition->
active =
false;
1446 }
else if (status == 1) {
1447 transition->
active =
true;
1448 }
else if (status == 0) {
1450 (*prior)->active =
false;
1452 transition->
active =
true;
1453 (*prior) = transition;
1457 (*prior)->
active =
false;
1458 transition->
active =
true;
1459 (*prior) = transition;
1462 transition->
active =
true;
1463 (*prior) = transition;
1482 static int8_t compareTransitionToMatch(
1490 }
else if (matchStart.suffix ==
1496 if (*transitionTime < matchStart)
return -1;
1497 if (*transitionTime == matchStart)
return 0;
1502 }
else if (matchUntil.suffix ==
1508 if (*transitionTime < matchUntil)
return 1;
1517 static void generateStartUntilTimes(
1519 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1521 "generateStartUntilTimes(): #transitions: %d;\n",
1522 (
int) (end - begin));
1526 bool isAfterFirst =
false;
1540 int16_t minutes = tt.minutes + 15 * (
1558 const acetime_t offsetSeconds = (acetime_t) 60
1561 st.yearTiny, st.month, st.day);
1565 isAfterFirst =
true;
1572 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1580 static void calcAbbreviations(
1582 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1583 logging::printf(
"calcAbbreviations(): #transitions: %d;\n",
1584 (
int) (end - begin));
1588 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1590 "calcAbbreviations(): format:%s, deltaCode:%d, letter:%s\n",
1606 static void createAbbreviation(
char* dest, uint8_t destSize,
1607 const char* format, uint8_t deltaCode,
const char* letterString) {
1609 if (strchr(format,
'%') !=
nullptr) {
1611 if (letterString ==
nullptr) {
1612 strncpy(dest, format, destSize - 1);
1613 dest[destSize - 1] =
'\0';
1615 copyAndReplace(dest, destSize, format,
'%', letterString);
1619 const char* slashPos = strchr(format,
'/');
1620 if (slashPos !=
nullptr) {
1621 if (deltaCode == 0) {
1622 uint8_t headLength = (slashPos - format);
1623 if (headLength >= destSize) headLength = destSize - 1;
1624 memcpy(dest, format, headLength);
1625 dest[headLength] =
'\0';
1627 uint8_t tailLength = strlen(slashPos+1);
1628 if (tailLength >= destSize) tailLength = destSize - 1;
1629 memcpy(dest, slashPos+1, tailLength);
1630 dest[tailLength] =
'\0';
1634 strncpy(dest, format, destSize);
1635 dest[destSize - 1] =
'\0';
1645 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
1646 char oldChar,
const char* newString) {
1647 while (*src !=
'\0' && dstSize > 0) {
1648 if (*src == oldChar) {
1649 while (*newString !=
'\0' && dstSize > 0) {
1650 *dst++ = *newString++;
1668 mutable int16_t mYear = 0;
1669 mutable bool mIsFilled =
false;
1671 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.
static const uint8_t TIME_SUFFIX_S
Represents 's' or standard time.
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...
static const uint8_t TIME_SUFFIX_U
Represents 'u' or UTC time.
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 supports for all zones defined by the TZ Database.
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...
static const uint8_t TIME_SUFFIX_W
Represents 'w' or wall time.
Class that holds the date-time as the components (year, month, day, hour, minute, second) without reg...