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(
")");
248 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(
"; oMinutes: %d", offsetMinutes);
319 logging::printf(
"; dMinutes: %d", deltaMinutes);
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) {
894 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
895 logging::printf(
"init(): Year %d out of valid range [%d, %d)\n",
896 year, mZoneInfo.startYear(), mZoneInfo.untilYear());
906 mNumMatches = findMatches(mZoneInfo, startYm, untilYm, mMatches,
908 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
909 findTransitions(mTransitionStorage, mMatches, mNumMatches);
912 fixTransitionTimes(begin, end);
913 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
914 generateStartUntilTimes(begin, end);
915 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
916 calcAbbreviations(begin, end);
917 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
924 bool isFilled(int16_t year)
const {
925 return mIsFilled && (year == mYear);
939 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
940 logging::printf(
"findMatches()\n");
944 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
946 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
947 if (iMatch < maxMatches) {
948 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
967 static bool eraOverlapsInterval(
972 return compareEraToYearMonth(prev, untilYm.yearTiny, untilYm.month) < 0
973 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
978 int8_t yearTiny, uint8_t month) {
979 if (era.untilYearTiny() < yearTiny)
return -1;
980 if (era.untilYearTiny() > yearTiny)
return 1;
981 if (era.untilMonth() < month)
return -1;
982 if (era.untilMonth() > month)
return 1;
983 if (era.untilDay() > 1)
return 1;
985 if (era.untilTimeMinutes() > 0)
return 1;
1001 prev.untilYearTiny(), prev.untilMonth(), prev.untilDay(),
1002 (int16_t) prev.untilTimeMinutes(), prev.untilTimeSuffix()
1005 startYm.yearTiny, startYm.month, 1, 0,
1008 if (startDate < lowerBound) {
1009 startDate = lowerBound;
1013 era.untilYearTiny(), era.untilMonth(), era.untilDay(),
1014 (int16_t) era.untilTimeMinutes(), era.untilTimeSuffix()
1017 untilYm.yearTiny, untilYm.month, 1, 0,
1020 if (upperBound < untilDate) {
1021 untilDate = upperBound;
1024 return {startDate, untilDate, era};
1031 static void findTransitions(
1034 uint8_t numMatches) {
1035 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1036 logging::printf(
"findTransitions()\n");
1038 for (uint8_t i = 0; i < numMatches; i++) {
1039 findTransitionsForMatch(transitionStorage, &matches[i]);
1044 static void findTransitionsForMatch(
1047 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1048 logging::printf(
"findTransitionsForMatch()\n");
1051 if (policy.isNull()) {
1052 findTransitionsFromSimpleMatch(transitionStorage, match);
1054 findTransitionsFromNamedMatch(transitionStorage, match);
1058 static void findTransitionsFromSimpleMatch(
1061 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1062 logging::printf(
"findTransitionsFromSimpleMatch()\n");
1065 createTransitionForYear(freeTransition, 0 ,
1070 static void findTransitionsFromNamedMatch(
1073 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1074 logging::printf(
"findTransitionsFromNamedMatch()\n");
1077 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1078 match->log(); logging::printf(
"\n");
1080 findCandidateTransitions(transitionStorage, match);
1081 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1082 transitionStorage.
log(); logging::printf(
"\n");
1085 transitionStorage.getCandidatePoolBegin(),
1086 transitionStorage.getCandidatePoolEnd());
1087 selectActiveTransitions(transitionStorage, match);
1088 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1089 transitionStorage.
log(); logging::printf(
"\n");
1093 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1094 transitionStorage.
log(); logging::printf(
"\n");
1098 static void findCandidateTransitions(
1101 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1102 logging::printf(
"findCandidateTransitions(): ");
1104 logging::printf(
"\n");
1107 uint8_t numRules = policy.numRules();
1112 (*prior)->
active =
false;
1113 for (uint8_t r = 0; r < numRules; r++) {
1117 int8_t interiorYears[kMaxInteriorYears];
1118 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1119 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1120 for (uint8_t y = 0; y < numYears; y++) {
1121 int8_t year = interiorYears[y];
1123 createTransitionForYear(t, year, rule, match);
1124 int8_t status = compareTransitionToMatchFuzzy(t, match);
1126 setAsPriorTransition(transitionStorage, t);
1127 }
else if (status == 1) {
1133 int8_t priorYear = getMostRecentPriorYear(
1134 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1136 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1138 "findCandidateTransitions(): priorYear: %d\n", priorYear);
1141 createTransitionForYear(t, priorYear, rule, match);
1142 setAsPriorTransition(transitionStorage, t);
1148 if ((*prior)->active) {
1149 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1151 "findCandidateTransitions(): adding prior to Candidate pool\n");
1161 static uint8_t calcInteriorYears(int8_t* interiorYears,
1162 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1163 int8_t startYear, int8_t endYear) {
1165 for (int8_t year = startYear; year <= endYear; year++) {
1166 if (fromYear <= year && year <= toYear) {
1167 interiorYears[i] = year;
1169 if (i >= maxInteriorYears)
break;
1189 if (rule.isNotNull()) {
1193 char letter = rule.letter();
1196 if (letter !=
'-') {
1216 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1217 int8_t startYear, int8_t ) {
1218 if (fromYear < startYear) {
1219 if (toYear < startYear) {
1222 return startYear - 1;
1233 rule.onDayOfMonth());
1234 return {yearTiny, monthDay.month, monthDay.day,
1235 (int16_t) rule.atTimeMinutes(), rule.atTimeSuffix()};
1248 static int8_t compareTransitionToMatchFuzzy(
1253 int16_t matchStartMonths = match->
startDateTime.yearTiny * 12
1255 if (ttMonths < matchStartMonths - 1)
return -1;
1257 int16_t matchUntilMonths = match->
untilDateTime.yearTiny * 12
1259 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1265 static void setAsPriorTransition(
1268 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1269 logging::printf(
"setAsPriorTransition()\n");
1291 static void fixTransitionTimes(
1293 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1294 logging::printf(
"fixTransitionTimes(): #transitions: %d;\n",
1295 (
int) (end - begin));
1303 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1304 logging::printf(
"fixTransitionTimes(): LOOP\n");
1306 logging::printf(
"\n");
1313 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1314 logging::printf(
"fixTransitionTimes(): END\n");
1325 int16_t offsetMinutes, int16_t deltaMinutes) {
1326 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1327 logging::printf(
"expandDateTuple()\n");
1331 *ttu = {tt->yearTiny, tt->month, tt->day,
1332 (int16_t) (tt->minutes - offsetMinutes),
1334 *tt = {tt->yearTiny, tt->month, tt->day,
1335 (int16_t) (tt->minutes + deltaMinutes),
1339 *tts = {tt->yearTiny, tt->month, tt->day,
1340 (int16_t) (tt->minutes + offsetMinutes),
1342 *tt = {tt->yearTiny, tt->month, tt->day,
1343 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1348 *tts = {tt->yearTiny, tt->month, tt->day,
1349 (int16_t) (tt->minutes - deltaMinutes),
1351 *ttu = {tt->yearTiny, tt->month, tt->day,
1352 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1356 normalizeDateTuple(tt);
1357 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1358 logging::printf(
"expandDateTuple(): normalizeDateTuple(tt): ");
1360 logging::printf(
"\n");
1363 normalizeDateTuple(tts);
1364 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1365 logging::printf(
"expandDateTuple(): normalizeDateTuple(tts): ");
1367 logging::printf(
"\n");
1370 normalizeDateTuple(ttu);
1371 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1372 logging::printf(
"expandDateTuple(): normalizeDateTuple(ttu): ");
1374 logging::printf(
"\n");
1383 const int16_t kOneDayAsMinutes = 60 * 24;
1384 if (dt->minutes <= -kOneDayAsMinutes) {
1386 dt->yearTiny, dt->month, dt->day);
1387 local_date_mutation::decrementOneDay(ld);
1389 dt->month = ld.
month();
1391 dt->minutes += kOneDayAsMinutes;
1392 }
else if (kOneDayAsMinutes <= dt->minutes) {
1394 dt->yearTiny, dt->month, dt->day);
1395 local_date_mutation::incrementOneDay(ld);
1397 dt->month = ld.
month();
1399 dt->minutes -= kOneDayAsMinutes;
1409 static void selectActiveTransitions(
1414 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1415 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1416 (
int) (end - begin));
1421 processActiveTransition(match, transition, &prior);
1427 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1429 "selectActiveTransitions(): found latest prior\n");
1443 static void processActiveTransition(
1447 int8_t status = compareTransitionToMatch(transition, match);
1449 transition->
active =
false;
1450 }
else if (status == 1) {
1451 transition->
active =
true;
1452 }
else if (status == 0) {
1454 (*prior)->active =
false;
1456 transition->
active =
true;
1457 (*prior) = transition;
1461 (*prior)->
active =
false;
1462 transition->
active =
true;
1463 (*prior) = transition;
1466 transition->
active =
true;
1467 (*prior) = transition;
1486 static int8_t compareTransitionToMatch(
1494 }
else if (matchStart.suffix ==
1500 if (*transitionTime < matchStart)
return -1;
1501 if (*transitionTime == matchStart)
return 0;
1506 }
else if (matchUntil.suffix ==
1512 if (*transitionTime < matchUntil)
return 1;
1521 static void generateStartUntilTimes(
1523 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1525 "generateStartUntilTimes(): #transitions: %d;\n",
1526 (
int) (end - begin));
1530 bool isAfterFirst =
false;
1544 int16_t minutes = tt.minutes + (
1562 const acetime_t offsetSeconds = (acetime_t) 60
1565 st.yearTiny, st.month, st.day);
1569 isAfterFirst =
true;
1576 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1584 static void calcAbbreviations(
1586 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1587 logging::printf(
"calcAbbreviations(): #transitions: %d;\n",
1588 (
int) (end - begin));
1592 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1594 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1610 static void createAbbreviation(
char* dest, uint8_t destSize,
1611 const char* format, uint16_t deltaMinutes,
const char* letterString) {
1613 if (strchr(format,
'%') !=
nullptr) {
1615 if (letterString ==
nullptr) {
1616 strncpy(dest, format, destSize - 1);
1617 dest[destSize - 1] =
'\0';
1619 copyAndReplace(dest, destSize, format,
'%', letterString);
1623 const char* slashPos = strchr(format,
'/');
1624 if (slashPos !=
nullptr) {
1625 if (deltaMinutes == 0) {
1626 uint8_t headLength = (slashPos - format);
1627 if (headLength >= destSize) headLength = destSize - 1;
1628 memcpy(dest, format, headLength);
1629 dest[headLength] =
'\0';
1631 uint8_t tailLength = strlen(slashPos+1);
1632 if (tailLength >= destSize) tailLength = destSize - 1;
1633 memcpy(dest, slashPos+1, tailLength);
1634 dest[tailLength] =
'\0';
1638 strncpy(dest, format, destSize);
1639 dest[destSize - 1] =
'\0';
1649 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
1650 char oldChar,
const char* newString) {
1651 while (*src !=
'\0' && dstSize > 0) {
1652 if (*src == oldChar) {
1653 while (*newString !=
'\0' && dstSize > 0) {
1654 *dst++ = *newString++;
1672 mutable int16_t mYear = 0;
1673 mutable bool mIsFilled =
false;
1675 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.
static const uint8_t kSuffixS
Represents 's' or standard time.
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 ZoneRule.
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...
int16_t deltaMinutes
The DST delta minutes.
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).
Data broker for accessing ZonePolicy.
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.
void resetTransitionHighWater()
Reset the TransitionStorage high water mark.
Data broker for accessing ZoneInfo.
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...
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.
static TimeOffset forMinutes(int16_t minutes)
Create TimeOffset from minutes from 00:00.
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.
static const uint8_t kSuffixW
Represents 'w' or wall time.
OffsetDateTime getOffsetDateTime(const LocalDateTime &ldt) const override
Return the best estimate of the OffsetDateTime at the given LocalDateTime for the timezone of the cur...
Data broker for accessing ZoneEra.
int16_t offsetMinutes
The base offset minutes, not the total effective UTC offset.
ZoneRuleBroker rule
The Zone transition rule that matched for the the given year.
static const uint8_t kSuffixU
Represents 'u' or UTC time.
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...