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"
15 #include "common/logging.h"
16 #include "TimeOffset.h"
17 #include "LocalDate.h"
18 #include "OffsetDateTime.h"
19 #include "ZoneProcessor.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_setZoneKey;
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;
65 DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
66 yearTiny(y), month(mon), day(d), suffix(mod), minutes(min) {}
76 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
77 char c =
"wsu"[(suffix>>4)];
78 logging::printf(
"DateTuple(%04d-%02u-%02uT%d'%c')",
85 inline bool operator<(
const DateTuple& a,
const DateTuple& b) {
86 if (a.yearTiny < b.yearTiny)
return true;
87 if (a.yearTiny > b.yearTiny)
return false;
88 if (a.month < b.month)
return true;
89 if (a.month > b.month)
return false;
90 if (a.day < b.day)
return true;
91 if (a.day > b.day)
return false;
92 if (a.minutes < b.minutes)
return true;
93 if (a.minutes > b.minutes)
return false;
97 inline bool operator>=(
const DateTuple& a,
const DateTuple& b) {
101 inline bool operator<=(
const DateTuple& a,
const DateTuple& b) {
105 inline bool operator>(
const DateTuple& a,
const DateTuple& b) {
110 inline bool operator==(
const DateTuple& a,
const DateTuple& b) {
111 return a.yearTiny == b.yearTiny
112 && a.month == b.month
114 && a.minutes == b.minutes
115 && a.suffix == b.suffix;
130 template<
typename ZEB>
142 logging::printf(
"ZoneMatch(");
145 logging::printf(
"; Era: %snull", (
era.isNull()) ?
"" :
"!");
146 logging::printf(
")");
179 template <
typename ZEB,
typename ZPB,
typename ZRB>
272 const char* format()
const {
273 return match->era.format();
297 const ZPB policy =
match->era.zonePolicy();
298 uint8_t numLetters = policy.numLetters();
299 if (
letter >= numLetters) {
307 return policy.letter(
letter);
312 logging::printf(
"Transition(");
313 if (
sizeof(acetime_t) <=
sizeof(
int)) {
318 logging::printf(
"; match: %snull", (
match) ?
"!" :
"");
319 logging::printf(
"; era: %snull",
324 if (!
rule.isNull()) {
325 logging::printf(
"; R.fY: %d",
rule.fromYearTiny());
326 logging::printf(
"; R.tY: %d",
rule.toYearTiny());
327 logging::printf(
"; R.M: %d",
rule.inMonth());
328 logging::printf(
"; R.dow: %d",
rule.onDayOfWeek());
329 logging::printf(
"; R.dom: %d",
rule.onDayOfMonth());
365 template<u
int8_t SIZE,
typename ZEB,
typename ZPB,
typename ZRB>
379 for (uint8_t i = 0; i < SIZE; i++) {
380 mTransitions[i] = &mPool[i];
383 mIndexCandidates = 0;
389 return mTransitions[mIndexPrior];
410 mIndexCandidates = mIndexPrior;
411 mIndexFree = mIndexPrior;
415 return &mTransitions[mIndexCandidates];
418 return &mTransitions[mIndexFree];
422 return &mTransitions[0];
425 return &mTransitions[mIndexFree];
436 if (mIndexFree > mHighWater) {
437 mHighWater = mIndexFree;
440 if (mIndexFree < SIZE) {
441 return mTransitions[mIndexFree];
443 return mTransitions[SIZE - 1];
455 if (mIndexFree >= SIZE)
return;
457 mIndexPrior = mIndexFree;
458 mIndexCandidates = mIndexFree;
469 return &mTransitions[mIndexPrior];
474 swap(&mTransitions[mIndexPrior], &mTransitions[mIndexFree]);
493 if (mIndexFree >= SIZE)
return;
494 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
498 mTransitions[i] = prev;
499 mTransitions[i - 1] = curr;
509 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
510 logging::printf(
"addActiveCandidatesToActivePool()\n");
512 uint8_t iActive = mIndexPrior;
513 uint8_t iCandidate = mIndexCandidates;
514 for (; iCandidate < mIndexFree; iCandidate++) {
515 if (mTransitions[iCandidate]->active) {
516 if (iActive != iCandidate) {
517 swap(&mTransitions[iActive], &mTransitions[iCandidate]);
522 mIndexPrior = iActive;
523 mIndexCandidates = iActive;
524 mIndexFree = iActive;
536 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
537 logging::printf(
"findTransition(): mIndexFree: %d\n", mIndexFree);
541 for (uint8_t i = 0; i < mIndexFree; i++) {
542 const Transition* candidate = mTransitions[i];
574 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
576 "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
586 for (uint8_t i = 0; i < mIndexFree; i++) {
587 const Transition* candidate = mTransitions[i];
596 logging::printf(
"TransitionStorage:\n");
597 logging::printf(
" mIndexPrior: %d\n", mIndexPrior);
598 logging::printf(
" mIndexCandidates: %d\n", mIndexCandidates);
599 logging::printf(
" mIndexFree: %d\n", mIndexFree);
600 if (mIndexPrior != 0) {
601 logging::printf(
" Actives:\n");
602 for (uint8_t i = 0; i < mIndexPrior; i++) {
603 mTransitions[i]->
log();
604 logging::printf(
"\n");
607 if (mIndexPrior != mIndexCandidates) {
608 logging::printf(
" Prior: ");
609 mTransitions[mIndexPrior]->
log();
610 logging::printf(
"\n");
612 if (mIndexCandidates != mIndexFree) {
613 logging::printf(
" Candidates:\n");
614 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
615 mTransitions[i]->
log();
616 logging::printf(
"\n");
632 friend class ::TransitionStorageTest_getFreeAgent;
633 friend class ::TransitionStorageTest_getFreeAgent2;
634 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
635 friend class ::TransitionStorageTest_reservePrior;
636 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
637 friend class ::TransitionStorageTest_setFreeAgentAsPrior;
638 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
639 friend class ::TransitionStorageTest_findTransitionForDateTime;
640 friend class ::TransitionStorageTest_resetCandidatePool;
644 return mTransitions[i];
650 uint8_t mIndexCandidates;
654 uint8_t mHighWater = 0;
662 inline void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
663 char oldChar,
const char* newString) {
664 while (*src !=
'\0' && dstSize > 0) {
665 if (*src == oldChar) {
666 while (*newString !=
'\0' && dstSize > 0) {
667 *dst++ = *newString++;
716 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
739 uint32_t
getZoneId()
const override {
return mZoneInfoBroker.zoneId(); }
742 bool success = init(epochSeconds);
744 const Transition* transition = findTransition(epochSeconds);
752 bool success = init(epochSeconds);
754 const Transition* transition = findTransition(epochSeconds);
758 const char*
getAbbrev(acetime_t epochSeconds)
const override {
759 bool success = init(epochSeconds);
760 if (!success)
return "";
761 const Transition* transition = findTransition(epochSeconds);
762 return transition->
abbrev;
767 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
768 logging::printf(
"getOffsetDateTime(): ldt=");
769 ldt.
printTo(SERIAL_PORT_MONITOR);
770 SERIAL_PORT_MONITOR.println();
778 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
779 logging::printf(
"getOffsetDateTime(): match transition=");
781 logging::printf(
"\n");
783 offset = (transition)
795 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
796 logging::printf(
"getOffsetDateTime(): odt=");
797 odt.printTo(SERIAL_PORT_MONITOR);
798 SERIAL_PORT_MONITOR.println();
807 acetime_t epochSeconds = odt.toEpochSeconds();
810 offset = (transition)
815 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
816 logging::printf(
"getOffsetDateTime(): normalized(odt)=");
817 odt.printTo(SERIAL_PORT_MONITOR);
818 SERIAL_PORT_MONITOR.println();
824 mZoneInfoBroker.printNameTo(printer);
828 mZoneInfoBroker.printShortNameTo(printer);
833 logging::printf(
"ExtendedZoneProcessor:\n");
834 logging::printf(
" mYear: %d\n", mYear);
835 logging::printf(
" mNumMatches: %d\n", mNumMatches);
836 for (
int i = 0; i < mNumMatches; i++) {
837 logging::printf(
" Match %d: ", i);
839 logging::printf(
"\n");
841 mTransitionStorage.
log();
855 if (mZoneInfoBroker.equals(zoneKey))
return;
857 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
865 return mZoneInfoBroker.equals(zoneKey);
868 void setBrokerFactory(
const BF* brokerFactory) {
869 mBrokerFactory = brokerFactory;
882 const BF* brokerFactory,
886 mBrokerFactory(brokerFactory)
892 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
893 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
894 friend class ::ExtendedZoneProcessorTest_createMatch;
895 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
896 friend class ::ExtendedZoneProcessorTest_findMatches_named;
897 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
898 friend class ::ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
899 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
900 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
901 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
902 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
903 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
904 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
905 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
906 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
907 friend class ::ExtendedZoneProcessorTest_processActiveTransition;
908 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
909 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
910 friend class ::ExtendedZoneProcessorTest_setZoneKey;
911 friend class ::TransitionStorageTest_findTransitionForDateTime;
923 static const uint8_t kMaxMatches = 4;
929 static const uint8_t kMaxInteriorYears = 4;
932 return mZoneInfoBroker.equals(
940 const Transition* findTransition(acetime_t epochSeconds)
const {
945 bool init(acetime_t epochSeconds)
const {
954 bool init(
const LocalDate& ld)
const {
955 int16_t year = ld.year();
956 if (isFilled(year))
return true;
957 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
958 logging::printf(
"init(): %d\n", year);
963 mTransitionStorage.
init();
965 if (year < mZoneInfoBroker.zoneContext()->startYear - 1
966 || mZoneInfoBroker.zoneContext()->untilYear < year) {
967 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
968 logging::printf(
"init(): Year %d out of valid range [%d, %d)\n",
970 mZoneInfoBroker.zoneContext()->startYear,
971 mZoneInfoBroker.zoneContext()->untilYear);
976 extended::YearMonthTuple startYm = {
978 extended::YearMonthTuple untilYm = {
981 mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
983 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
984 findTransitions(mTransitionStorage, mMatches, mNumMatches);
985 Transition** begin = mTransitionStorage.getActivePoolBegin();
986 Transition** end = mTransitionStorage.getActivePoolEnd();
987 fixTransitionTimes(begin, end);
988 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
989 generateStartUntilTimes(begin, end);
990 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
991 calcAbbreviations(begin, end);
992 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
999 bool isFilled(int16_t year)
const {
1000 return mIsFilled && (year == mYear);
1010 static uint8_t findMatches(
const ZIB& zoneInfo,
1011 const extended::YearMonthTuple& startYm,
1012 const extended::YearMonthTuple& untilYm,
1013 ZoneMatch* matches, uint8_t maxMatches) {
1014 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1015 logging::printf(
"findMatches()\n");
1019 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
1020 const ZEB era = zoneInfo.era(iEra);
1021 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
1022 if (iMatch < maxMatches) {
1023 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
1043 static bool eraOverlapsInterval(
1046 const extended::YearMonthTuple& startYm,
1047 const extended::YearMonthTuple& untilYm) {
1048 return (prev.isNull() || compareEraToYearMonth(
1049 prev, untilYm.yearTiny, untilYm.month) < 0)
1050 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1054 static int8_t compareEraToYearMonth(
const ZEB& era,
1055 int8_t yearTiny, uint8_t month) {
1056 if (era.untilYearTiny() < yearTiny)
return -1;
1057 if (era.untilYearTiny() > yearTiny)
return 1;
1058 if (era.untilMonth() < month)
return -1;
1059 if (era.untilMonth() > month)
return 1;
1060 if (era.untilDay() > 1)
return 1;
1062 if (era.untilTimeMinutes() > 0)
return 1;
1075 const extended::YearMonthTuple& startYm,
1076 const extended::YearMonthTuple& untilYm) {
1078 extended::DateTuple startDate = prev.isNull()
1079 ? extended::DateTuple{
1083 : extended::DateTuple{
1084 prev.untilYearTiny(), prev.untilMonth(), prev.untilDay(),
1085 (int16_t) prev.untilTimeMinutes(), prev.untilTimeSuffix()
1087 extended::DateTuple lowerBound = {
1088 startYm.yearTiny, startYm.month, 1, 0,
1091 if (startDate < lowerBound) {
1092 startDate = lowerBound;
1095 extended::DateTuple untilDate = {
1096 era.untilYearTiny(), era.untilMonth(), era.untilDay(),
1097 (int16_t) era.untilTimeMinutes(), era.untilTimeSuffix()
1099 extended::DateTuple upperBound = {
1100 untilYm.yearTiny, untilYm.month, 1, 0,
1103 if (upperBound < untilDate) {
1104 untilDate = upperBound;
1107 return {startDate, untilDate, era};
1114 static void findTransitions(
1117 uint8_t numMatches) {
1118 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1119 logging::printf(
"findTransitions()\n");
1121 for (uint8_t i = 0; i < numMatches; i++) {
1122 findTransitionsForMatch(transitionStorage, &matches[i]);
1127 static void findTransitionsForMatch(
1130 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1131 logging::printf(
"findTransitionsForMatch()\n");
1133 const ZPB policy = match->era.zonePolicy();
1134 if (policy.isNull()) {
1135 findTransitionsFromSimpleMatch(transitionStorage, match);
1137 findTransitionsFromNamedMatch(transitionStorage, match);
1141 static void findTransitionsFromSimpleMatch(
1144 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1145 logging::printf(
"findTransitionsFromSimpleMatch()\n");
1147 Transition* freeTransition = transitionStorage.getFreeAgent();
1148 createTransitionForYear(freeTransition, 0 ,
1150 transitionStorage.addFreeAgentToActivePool();
1153 static void findTransitionsFromNamedMatch(
1156 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1157 logging::printf(
"findTransitionsFromNamedMatch()\n");
1159 transitionStorage.resetCandidatePool();
1160 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1161 match->log(); logging::printf(
"\n");
1163 findCandidateTransitions(transitionStorage, match);
1164 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1165 transitionStorage.log(); logging::printf(
"\n");
1168 transitionStorage.getCandidatePoolBegin(),
1169 transitionStorage.getCandidatePoolEnd());
1170 selectActiveTransitions(transitionStorage, match);
1171 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1172 transitionStorage.log(); logging::printf(
"\n");
1175 transitionStorage.addActiveCandidatesToActivePool();
1176 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1177 transitionStorage.log(); logging::printf(
"\n");
1181 static void findCandidateTransitions(
1184 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1185 logging::printf(
"findCandidateTransitions(): ");
1187 logging::printf(
"\n");
1189 const ZPB policy = match->era.zonePolicy();
1190 uint8_t numRules = policy.numRules();
1191 int8_t startY = match->startDateTime.yearTiny;
1192 int8_t endY = match->untilDateTime.yearTiny;
1194 Transition** prior = transitionStorage.reservePrior();
1195 (*prior)->active =
false;
1196 for (uint8_t r = 0; r < numRules; r++) {
1197 const ZRB rule = policy.rule(r);
1200 int8_t interiorYears[kMaxInteriorYears];
1201 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1202 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1203 for (uint8_t y = 0; y < numYears; y++) {
1204 int8_t year = interiorYears[y];
1205 Transition* t = transitionStorage.getFreeAgent();
1206 createTransitionForYear(t, year, rule, match);
1207 int8_t status = compareTransitionToMatchFuzzy(t, match);
1209 setAsPriorTransition(transitionStorage, t);
1210 }
else if (status == 1) {
1211 transitionStorage.addFreeAgentToCandidatePool();
1216 int8_t priorYear = getMostRecentPriorYear(
1217 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1219 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1221 "findCandidateTransitions(): priorYear: %d\n", priorYear);
1223 Transition* t = transitionStorage.getFreeAgent();
1224 createTransitionForYear(t, priorYear, rule, match);
1225 setAsPriorTransition(transitionStorage, t);
1231 if ((*prior)->active) {
1232 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1234 "findCandidateTransitions(): adding prior to Candidate pool\n");
1236 transitionStorage.addPriorToCandidatePool();
1244 static uint8_t calcInteriorYears(int8_t* interiorYears,
1245 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1246 int8_t startYear, int8_t endYear) {
1248 for (int8_t year = startYear; year <= endYear; year++) {
1249 if (fromYear <= year && year <= toYear) {
1250 interiorYears[i] = year;
1252 if (i >= maxInteriorYears)
break;
1264 static void createTransitionForYear(
Transition* t, int8_t year,
1265 const ZRB& rule,
const ZoneMatch* match) {
1268 t->offsetMinutes = match->era.offsetMinutes();
1269 t->letterBuf[0] =
'\0';
1271 if (! rule.isNull()) {
1272 t->transitionTime = getTransitionTime(year, rule);
1273 t->deltaMinutes = rule.deltaMinutes();
1275 char letter = rule.letter();
1278 if (letter !=
'-') {
1279 t->letterBuf[0] = letter;
1280 t->letterBuf[1] =
'\0';
1288 t->transitionTime = match->startDateTime;
1289 t->deltaMinutes = match->era.deltaMinutes();
1298 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1299 int8_t startYear, int8_t ) {
1300 if (fromYear < startYear) {
1301 if (toYear < startYear) {
1304 return startYear - 1;
1311 static extended::DateTuple getTransitionTime(
1312 int8_t yearTiny,
const ZRB& rule) {
1313 internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1315 rule.onDayOfMonth());
1316 return {yearTiny, monthDay.month, monthDay.day,
1317 (int16_t) rule.atTimeMinutes(), rule.atTimeSuffix()};
1330 static int8_t compareTransitionToMatchFuzzy(
1332 int16_t ttMonths = t->transitionTime.yearTiny * 12
1333 + t->transitionTime.month;
1335 int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1336 + match->startDateTime.month;
1337 if (ttMonths < matchStartMonths - 1)
return -1;
1339 int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1340 + match->untilDateTime.month;
1341 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1347 static void setAsPriorTransition(
1350 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1351 logging::printf(
"setAsPriorTransition()\n");
1353 Transition* prior = transitionStorage.getPrior();
1354 if (prior->active) {
1355 if (prior->transitionTime < t->transitionTime) {
1357 transitionStorage.setFreeAgentAsPrior();
1361 transitionStorage.setFreeAgentAsPrior();
1374 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1375 logging::printf(
"fixTransitionTimes(): #transitions: %d;\n",
1376 (
int) (end - begin));
1382 for (
Transition** iter = begin; iter != end; ++iter) {
1384 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1385 logging::printf(
"fixTransitionTimes(): LOOP\n");
1387 logging::printf(
"\n");
1389 expandDateTuple(&curr->transitionTime,
1390 &curr->transitionTimeS, &curr->transitionTimeU,
1391 prev->offsetMinutes, prev->deltaMinutes);
1394 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1395 logging::printf(
"fixTransitionTimes(): END\n");
1404 static void expandDateTuple(extended::DateTuple* tt,
1405 extended::DateTuple* tts, extended::DateTuple* ttu,
1406 int16_t offsetMinutes, int16_t deltaMinutes) {
1407 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1408 logging::printf(
"expandDateTuple()\n");
1412 *ttu = {tt->yearTiny, tt->month, tt->day,
1413 (int16_t) (tt->minutes - offsetMinutes),
1415 *tt = {tt->yearTiny, tt->month, tt->day,
1416 (int16_t) (tt->minutes + deltaMinutes),
1420 *tts = {tt->yearTiny, tt->month, tt->day,
1421 (int16_t) (tt->minutes + offsetMinutes),
1423 *tt = {tt->yearTiny, tt->month, tt->day,
1424 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1429 *tts = {tt->yearTiny, tt->month, tt->day,
1430 (int16_t) (tt->minutes - deltaMinutes),
1432 *ttu = {tt->yearTiny, tt->month, tt->day,
1433 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1437 normalizeDateTuple(tt);
1438 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1439 logging::printf(
"expandDateTuple(): normalizeDateTuple(tt): ");
1441 logging::printf(
"\n");
1444 normalizeDateTuple(tts);
1445 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1446 logging::printf(
"expandDateTuple(): normalizeDateTuple(tts): ");
1448 logging::printf(
"\n");
1451 normalizeDateTuple(ttu);
1452 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1453 logging::printf(
"expandDateTuple(): normalizeDateTuple(ttu): ");
1455 logging::printf(
"\n");
1463 static void normalizeDateTuple(extended::DateTuple* dt) {
1464 const int16_t kOneDayAsMinutes = 60 * 24;
1465 if (dt->minutes <= -kOneDayAsMinutes) {
1467 dt->yearTiny, dt->month, dt->day);
1468 local_date_mutation::decrementOneDay(ld);
1469 dt->yearTiny = ld.yearTiny();
1470 dt->month = ld.month();
1472 dt->minutes += kOneDayAsMinutes;
1473 }
else if (kOneDayAsMinutes <= dt->minutes) {
1475 dt->yearTiny, dt->month, dt->day);
1476 local_date_mutation::incrementOneDay(ld);
1477 dt->yearTiny = ld.yearTiny();
1478 dt->month = ld.month();
1480 dt->minutes -= kOneDayAsMinutes;
1490 static void selectActiveTransitions(
1493 Transition** begin = transitionStorage.getCandidatePoolBegin();
1494 Transition** end = transitionStorage.getCandidatePoolEnd();
1495 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1496 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1497 (
int) (end - begin));
1500 for (
Transition** iter = begin; iter != end; ++iter) {
1502 processActiveTransition(match, transition, &prior);
1508 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1510 "selectActiveTransitions(): found latest prior\n");
1512 prior->originalTransitionTime = prior->transitionTime;
1513 prior->transitionTime = match->startDateTime;
1524 static void processActiveTransition(
1528 int8_t status = compareTransitionToMatch(transition, match);
1530 transition->active =
false;
1531 }
else if (status == 1) {
1532 transition->active =
true;
1533 }
else if (status == 0) {
1535 (*prior)->active =
false;
1537 transition->active =
true;
1538 (*prior) = transition;
1541 if ((*prior)->transitionTime < transition->transitionTime) {
1542 (*prior)->active =
false;
1543 transition->active =
true;
1544 (*prior) = transition;
1547 transition->active =
true;
1548 (*prior) = transition;
1567 static int8_t compareTransitionToMatch(
1570 const extended::DateTuple* transitionTime;
1572 const extended::DateTuple& matchStart = match->startDateTime;
1574 transitionTime = &transition->transitionTimeS;
1576 transitionTime = &transition->transitionTimeU;
1578 transitionTime = &transition->transitionTime;
1580 if (*transitionTime < matchStart)
return -1;
1581 if (*transitionTime == matchStart)
return 0;
1583 const extended::DateTuple& matchUntil = match->untilDateTime;
1585 transitionTime = &transition->transitionTimeS;
1586 }
else if (matchUntil.suffix ==
1588 transitionTime = &transition->transitionTimeU;
1590 transitionTime = &transition->transitionTime;
1592 if (*transitionTime < matchUntil)
return 1;
1602 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1604 "generateStartUntilTimes(): #transitions: %d;\n",
1605 (
int) (end - begin));
1609 bool isAfterFirst =
false;
1611 for (
Transition** iter = begin; iter != end; ++iter) {
1615 const extended::DateTuple& tt = t->transitionTime;
1617 prev->untilDateTime = tt;
1623 int16_t minutes = tt.minutes + (
1624 - prev->offsetMinutes - prev->deltaMinutes
1625 + t->offsetMinutes + t->deltaMinutes);
1626 t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
1628 normalizeDateTuple(&t->startDateTime);
1640 const extended::DateTuple& st = t->startDateTime;
1641 const acetime_t offsetSeconds = (acetime_t) 60
1642 * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
1644 st.yearTiny, st.month, st.day);
1645 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1648 isAfterFirst =
true;
1652 extended::DateTuple untilTime = prev->match->untilDateTime;
1653 extended::DateTuple untilTimeS;
1654 extended::DateTuple untilTimeU;
1655 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1656 prev->offsetMinutes, prev->deltaMinutes);
1657 prev->untilDateTime = untilTime;
1664 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1665 logging::printf(
"calcAbbreviations(): #transitions: %d;\n",
1666 (
int) (end - begin));
1668 for (
Transition** iter = begin; iter != end; ++iter) {
1670 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1672 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1673 t->format(), t->deltaMinutes, t->letter());
1675 createAbbreviation(t->abbrev, internal::kAbbrevSize,
1676 t->format(), t->deltaMinutes, t->letter());
1688 static void createAbbreviation(
char* dest, uint8_t destSize,
1689 const char* format, uint16_t deltaMinutes,
const char* letterString) {
1691 if (strchr(format,
'%') !=
nullptr) {
1693 if (letterString ==
nullptr) {
1694 strncpy(dest, format, destSize - 1);
1695 dest[destSize - 1] =
'\0';
1697 extended::copyAndReplace(dest, destSize, format,
'%', letterString);
1701 const char* slashPos = strchr(format,
'/');
1702 if (slashPos !=
nullptr) {
1703 if (deltaMinutes == 0) {
1704 uint8_t headLength = (slashPos - format);
1705 if (headLength >= destSize) headLength = destSize - 1;
1706 memcpy(dest, format, headLength);
1707 dest[headLength] =
'\0';
1709 uint8_t tailLength = strlen(slashPos+1);
1710 if (tailLength >= destSize) tailLength = destSize - 1;
1711 memcpy(dest, slashPos+1, tailLength);
1712 dest[tailLength] =
'\0';
1716 strncpy(dest, format, destSize);
1717 dest[destSize - 1] =
'\0';
1722 const BF* mBrokerFactory;
1723 ZIB mZoneInfoBroker;
1725 mutable int16_t mYear = 0;
1726 mutable bool mIsFilled =
false;
1728 mutable uint8_t mNumMatches = 0;
1729 mutable ZoneMatch mMatches[kMaxMatches];
1739 extended::BrokerFactory,
1740 extended::ZoneInfoBroker,
1741 extended::ZoneEraBroker,
1742 extended::ZonePolicyBroker,
1743 extended::ZoneRuleBroker> {
1751 extended::BrokerFactory,
1752 extended::ZoneInfoBroker,
1753 extended::ZoneEraBroker,
1754 extended::ZonePolicyBroker,
1755 extended::ZoneRuleBroker>(