6 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
7 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
11 #include <AceCommon.h>
13 #include "internal/ZonePolicy.h"
14 #include "internal/ZoneInfo.h"
16 #include "common/logging.h"
17 #include "TimeOffset.h"
18 #include "LocalDate.h"
19 #include "OffsetDateTime.h"
20 #include "ZoneProcessor.h"
21 #include "local_date_mutation.h"
23 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG 0
25 class ExtendedZoneProcessorTest_compareEraToYearMonth;
26 class ExtendedZoneProcessorTest_compareEraToYearMonth2;
27 class ExtendedZoneProcessorTest_createMatch;
28 class ExtendedZoneProcessorTest_findMatches_simple;
29 class ExtendedZoneProcessorTest_findMatches_named;
30 class ExtendedZoneProcessorTest_findCandidateTransitions;
31 class ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
32 class ExtendedZoneProcessorTest_getTransitionTime;
33 class ExtendedZoneProcessorTest_createTransitionForYear;
34 class ExtendedZoneProcessorTest_normalizeDateTuple;
35 class ExtendedZoneProcessorTest_expandDateTuple;
36 class ExtendedZoneProcessorTest_calcInteriorYears;
37 class ExtendedZoneProcessorTest_getMostRecentPriorYear;
38 class ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
39 class ExtendedZoneProcessorTest_compareTransitionToMatch;
40 class ExtendedZoneProcessorTest_processActiveTransition;
41 class ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
42 class ExtendedZoneProcessorTest_createAbbreviation;
43 class ExtendedZoneProcessorTest_setZoneKey;
44 class TransitionStorageTest_getFreeAgent;
45 class TransitionStorageTest_getFreeAgent2;
46 class TransitionStorageTest_addFreeAgentToActivePool;
47 class TransitionStorageTest_reservePrior;
48 class TransitionStorageTest_addFreeAgentToCandidatePool;
49 class TransitionStorageTest_setFreeAgentAsPrior;
50 class TransitionStorageTest_addActiveCandidatesToActivePool;
51 class TransitionStorageTest_resetCandidatePool;
52 class TransitionStorageTest_findTransitionForDateTime;
66 DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
67 yearTiny(y), month(mon), day(d), suffix(mod), minutes(min) {}
77 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
78 char c =
"wsu"[(suffix>>4)];
79 logging::printf(
"DateTuple(%04d-%02u-%02uT%d'%c')",
86 inline bool operator<(
const DateTuple& a,
const DateTuple& b) {
87 if (a.yearTiny < b.yearTiny)
return true;
88 if (a.yearTiny > b.yearTiny)
return false;
89 if (a.month < b.month)
return true;
90 if (a.month > b.month)
return false;
91 if (a.day < b.day)
return true;
92 if (a.day > b.day)
return false;
93 if (a.minutes < b.minutes)
return true;
94 if (a.minutes > b.minutes)
return false;
98 inline bool operator>=(
const DateTuple& a,
const DateTuple& b) {
102 inline bool operator<=(
const DateTuple& a,
const DateTuple& b) {
106 inline bool operator>(
const DateTuple& a,
const DateTuple& b) {
111 inline bool operator==(
const DateTuple& a,
const DateTuple& b) {
112 return a.yearTiny == b.yearTiny
113 && a.month == b.month
115 && a.minutes == b.minutes
116 && a.suffix == b.suffix;
131 template<
typename ZEB>
143 logging::printf(
"ZoneMatch(");
146 logging::printf(
"; Era: %snull", (
era.isNull()) ?
"" :
"!");
147 logging::printf(
")");
180 template <
typename ZEB,
typename ZPB,
typename ZRB>
273 const char* format()
const {
274 return match->era.format();
298 const ZPB policy =
match->era.zonePolicy();
299 uint8_t numLetters = policy.numLetters();
300 if (
letter >= numLetters) {
308 return policy.letter(
letter);
313 logging::printf(
"Transition(");
314 if (
sizeof(acetime_t) <=
sizeof(
int)) {
319 logging::printf(
"; match: %snull", (
match) ?
"!" :
"");
320 logging::printf(
"; era: %snull",
325 if (!
rule.isNull()) {
326 logging::printf(
"; R.fY: %d",
rule.fromYearTiny());
327 logging::printf(
"; R.tY: %d",
rule.toYearTiny());
328 logging::printf(
"; R.M: %d",
rule.inMonth());
329 logging::printf(
"; R.dow: %d",
rule.onDayOfWeek());
330 logging::printf(
"; R.dom: %d",
rule.onDayOfMonth());
366 template<u
int8_t SIZE,
typename ZEB,
typename ZPB,
typename ZRB>
380 for (uint8_t i = 0; i < SIZE; i++) {
381 mTransitions[i] = &mPool[i];
384 mIndexCandidates = 0;
390 return mTransitions[mIndexPrior];
409 mIndexCandidates = mIndexPrior;
410 mIndexFree = mIndexPrior;
414 return &mTransitions[mIndexCandidates];
417 return &mTransitions[mIndexFree];
421 return &mTransitions[0];
424 return &mTransitions[mIndexFree];
435 if (mIndexFree > mHighWater) {
436 mHighWater = mIndexFree;
439 if (mIndexFree < SIZE) {
440 return mTransitions[mIndexFree];
442 return mTransitions[SIZE - 1];
454 if (mIndexFree >= SIZE)
return;
456 mIndexPrior = mIndexFree;
457 mIndexCandidates = mIndexFree;
468 return &mTransitions[mIndexPrior];
473 swap(&mTransitions[mIndexPrior], &mTransitions[mIndexFree]);
492 if (mIndexFree >= SIZE)
return;
493 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
497 mTransitions[i] = prev;
498 mTransitions[i - 1] = curr;
508 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
509 logging::printf(
"addActiveCandidatesToActivePool()\n");
511 uint8_t iActive = mIndexPrior;
512 uint8_t iCandidate = mIndexCandidates;
513 for (; iCandidate < mIndexFree; iCandidate++) {
514 if (mTransitions[iCandidate]->active) {
515 if (iActive != iCandidate) {
516 swap(&mTransitions[iActive], &mTransitions[iCandidate]);
521 mIndexPrior = iActive;
522 mIndexCandidates = iActive;
523 mIndexFree = iActive;
535 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
536 logging::printf(
"findTransition(): mIndexFree: %d\n", mIndexFree);
540 for (uint8_t i = 0; i < mIndexFree; i++) {
541 const Transition* candidate = mTransitions[i];
573 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
575 "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
585 for (uint8_t i = 0; i < mIndexFree; i++) {
586 const Transition* candidate = mTransitions[i];
595 logging::printf(
"TransitionStorage:\n");
596 logging::printf(
" mIndexPrior: %d\n", mIndexPrior);
597 logging::printf(
" mIndexCandidates: %d\n", mIndexCandidates);
598 logging::printf(
" mIndexFree: %d\n", mIndexFree);
599 if (mIndexPrior != 0) {
600 logging::printf(
" Actives:\n");
601 for (uint8_t i = 0; i < mIndexPrior; i++) {
602 mTransitions[i]->
log();
603 logging::printf(
"\n");
606 if (mIndexPrior != mIndexCandidates) {
607 logging::printf(
" Prior: ");
608 mTransitions[mIndexPrior]->
log();
609 logging::printf(
"\n");
611 if (mIndexCandidates != mIndexFree) {
612 logging::printf(
" Candidates:\n");
613 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
614 mTransitions[i]->
log();
615 logging::printf(
"\n");
631 friend class ::TransitionStorageTest_getFreeAgent;
632 friend class ::TransitionStorageTest_getFreeAgent2;
633 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
634 friend class ::TransitionStorageTest_reservePrior;
635 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
636 friend class ::TransitionStorageTest_setFreeAgentAsPrior;
637 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
638 friend class ::TransitionStorageTest_findTransitionForDateTime;
639 friend class ::TransitionStorageTest_resetCandidatePool;
643 return mTransitions[i];
649 uint8_t mIndexCandidates;
653 uint8_t mHighWater = 0;
689 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
712 uint32_t
getZoneId()
const override {
return mZoneInfoBroker.zoneId(); }
715 bool success = init(epochSeconds);
717 const Transition* transition = findTransition(epochSeconds);
725 bool success = init(epochSeconds);
727 const Transition* transition = findTransition(epochSeconds);
731 const char*
getAbbrev(acetime_t epochSeconds)
const override {
732 bool success = init(epochSeconds);
733 if (!success)
return "";
734 const Transition* transition = findTransition(epochSeconds);
735 return transition->
abbrev;
740 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
741 logging::printf(
"getOffsetDateTime(): ldt=");
742 ldt.
printTo(SERIAL_PORT_MONITOR);
743 SERIAL_PORT_MONITOR.println();
751 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
752 logging::printf(
"getOffsetDateTime(): match transition=");
754 logging::printf(
"\n");
756 offset = (transition)
768 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
769 logging::printf(
"getOffsetDateTime(): odt=");
770 odt.printTo(SERIAL_PORT_MONITOR);
771 SERIAL_PORT_MONITOR.println();
780 acetime_t epochSeconds = odt.toEpochSeconds();
783 offset = (transition)
788 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
789 logging::printf(
"getOffsetDateTime(): normalized(odt)=");
790 odt.printTo(SERIAL_PORT_MONITOR);
791 SERIAL_PORT_MONITOR.println();
797 mZoneInfoBroker.printNameTo(printer);
801 mZoneInfoBroker.printShortNameTo(printer);
806 logging::printf(
"ExtendedZoneProcessor:\n");
807 logging::printf(
" mYear: %d\n", mYear);
808 logging::printf(
" mNumMatches: %d\n", mNumMatches);
809 for (
int i = 0; i < mNumMatches; i++) {
810 logging::printf(
" Match %d: ", i);
812 logging::printf(
"\n");
814 mTransitionStorage.
log();
828 if (mZoneInfoBroker.equals(zoneKey))
return;
830 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
838 return mZoneInfoBroker.equals(zoneKey);
841 void setBrokerFactory(
const BF* brokerFactory) {
842 mBrokerFactory = brokerFactory;
855 const BF* brokerFactory,
859 mBrokerFactory(brokerFactory)
865 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
866 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
867 friend class ::ExtendedZoneProcessorTest_createMatch;
868 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
869 friend class ::ExtendedZoneProcessorTest_findMatches_named;
870 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
871 friend class ::ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
872 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
873 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
874 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
875 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
876 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
877 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
878 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
879 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
880 friend class ::ExtendedZoneProcessorTest_processActiveTransition;
881 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
882 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
883 friend class ::ExtendedZoneProcessorTest_setZoneKey;
884 friend class ::TransitionStorageTest_findTransitionForDateTime;
896 static const uint8_t kMaxMatches = 4;
902 static const uint8_t kMaxInteriorYears = 4;
905 return mZoneInfoBroker.equals(
913 const Transition* findTransition(acetime_t epochSeconds)
const {
918 bool init(acetime_t epochSeconds)
const {
927 bool init(
const LocalDate& ld)
const {
928 int16_t year = ld.year();
929 if (isFilled(year))
return true;
930 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
931 logging::printf(
"init(): %d\n", year);
936 mTransitionStorage.
init();
938 if (year < mZoneInfoBroker.zoneContext()->startYear - 1
939 || mZoneInfoBroker.zoneContext()->untilYear < year) {
940 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
941 logging::printf(
"init(): Year %d out of valid range [%d, %d)\n",
943 mZoneInfoBroker.zoneContext()->startYear,
944 mZoneInfoBroker.zoneContext()->untilYear);
949 extended::YearMonthTuple startYm = {
951 extended::YearMonthTuple untilYm = {
954 mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
956 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
957 findTransitions(mTransitionStorage, mMatches, mNumMatches);
958 Transition** begin = mTransitionStorage.getActivePoolBegin();
959 Transition** end = mTransitionStorage.getActivePoolEnd();
960 fixTransitionTimes(begin, end);
961 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
962 generateStartUntilTimes(begin, end);
963 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
964 calcAbbreviations(begin, end);
965 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
972 bool isFilled(int16_t year)
const {
973 return mIsFilled && (year == mYear);
983 static uint8_t findMatches(
const ZIB& zoneInfo,
984 const extended::YearMonthTuple& startYm,
985 const extended::YearMonthTuple& untilYm,
986 ZoneMatch* matches, uint8_t maxMatches) {
987 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
988 logging::printf(
"findMatches()\n");
992 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
993 const ZEB era = zoneInfo.era(iEra);
994 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
995 if (iMatch < maxMatches) {
996 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
1016 static bool eraOverlapsInterval(
1019 const extended::YearMonthTuple& startYm,
1020 const extended::YearMonthTuple& untilYm) {
1021 return (prev.isNull() || compareEraToYearMonth(
1022 prev, untilYm.yearTiny, untilYm.month) < 0)
1023 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1027 static int8_t compareEraToYearMonth(
const ZEB& era,
1028 int8_t yearTiny, uint8_t month) {
1029 if (era.untilYearTiny() < yearTiny)
return -1;
1030 if (era.untilYearTiny() > yearTiny)
return 1;
1031 if (era.untilMonth() < month)
return -1;
1032 if (era.untilMonth() > month)
return 1;
1033 if (era.untilDay() > 1)
return 1;
1035 if (era.untilTimeMinutes() > 0)
return 1;
1048 const extended::YearMonthTuple& startYm,
1049 const extended::YearMonthTuple& untilYm) {
1051 extended::DateTuple startDate = prev.isNull()
1052 ? extended::DateTuple{
1056 : extended::DateTuple{
1057 prev.untilYearTiny(), prev.untilMonth(), prev.untilDay(),
1058 (int16_t) prev.untilTimeMinutes(), prev.untilTimeSuffix()
1060 extended::DateTuple lowerBound = {
1061 startYm.yearTiny, startYm.month, 1, 0,
1064 if (startDate < lowerBound) {
1065 startDate = lowerBound;
1068 extended::DateTuple untilDate = {
1069 era.untilYearTiny(), era.untilMonth(), era.untilDay(),
1070 (int16_t) era.untilTimeMinutes(), era.untilTimeSuffix()
1072 extended::DateTuple upperBound = {
1073 untilYm.yearTiny, untilYm.month, 1, 0,
1076 if (upperBound < untilDate) {
1077 untilDate = upperBound;
1080 return {startDate, untilDate, era};
1087 static void findTransitions(
1090 uint8_t numMatches) {
1091 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1092 logging::printf(
"findTransitions()\n");
1094 for (uint8_t i = 0; i < numMatches; i++) {
1095 findTransitionsForMatch(transitionStorage, &matches[i]);
1100 static void findTransitionsForMatch(
1103 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1104 logging::printf(
"findTransitionsForMatch()\n");
1106 const ZPB policy = match->era.zonePolicy();
1107 if (policy.isNull()) {
1108 findTransitionsFromSimpleMatch(transitionStorage, match);
1110 findTransitionsFromNamedMatch(transitionStorage, match);
1114 static void findTransitionsFromSimpleMatch(
1117 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1118 logging::printf(
"findTransitionsFromSimpleMatch()\n");
1120 Transition* freeTransition = transitionStorage.getFreeAgent();
1121 createTransitionForYear(freeTransition, 0 ,
1123 transitionStorage.addFreeAgentToActivePool();
1126 static void findTransitionsFromNamedMatch(
1129 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1130 logging::printf(
"findTransitionsFromNamedMatch()\n");
1132 transitionStorage.resetCandidatePool();
1133 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1134 match->log(); logging::printf(
"\n");
1136 findCandidateTransitions(transitionStorage, match);
1137 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1138 transitionStorage.log(); logging::printf(
"\n");
1141 transitionStorage.getCandidatePoolBegin(),
1142 transitionStorage.getCandidatePoolEnd());
1143 selectActiveTransitions(transitionStorage, match);
1144 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1145 transitionStorage.log(); logging::printf(
"\n");
1148 transitionStorage.addActiveCandidatesToActivePool();
1149 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1150 transitionStorage.log(); logging::printf(
"\n");
1154 static void findCandidateTransitions(
1157 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1158 logging::printf(
"findCandidateTransitions(): ");
1160 logging::printf(
"\n");
1162 const ZPB policy = match->era.zonePolicy();
1163 uint8_t numRules = policy.numRules();
1164 int8_t startY = match->startDateTime.yearTiny;
1165 int8_t endY = match->untilDateTime.yearTiny;
1167 Transition** prior = transitionStorage.reservePrior();
1168 (*prior)->active =
false;
1169 for (uint8_t r = 0; r < numRules; r++) {
1170 const ZRB rule = policy.rule(r);
1173 int8_t interiorYears[kMaxInteriorYears];
1174 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1175 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1176 for (uint8_t y = 0; y < numYears; y++) {
1177 int8_t year = interiorYears[y];
1178 Transition* t = transitionStorage.getFreeAgent();
1179 createTransitionForYear(t, year, rule, match);
1180 int8_t status = compareTransitionToMatchFuzzy(t, match);
1182 setAsPriorTransition(transitionStorage, t);
1183 }
else if (status == 1) {
1184 transitionStorage.addFreeAgentToCandidatePool();
1189 int8_t priorYear = getMostRecentPriorYear(
1190 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1192 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1194 "findCandidateTransitions(): priorYear: %d\n", priorYear);
1196 Transition* t = transitionStorage.getFreeAgent();
1197 createTransitionForYear(t, priorYear, rule, match);
1198 setAsPriorTransition(transitionStorage, t);
1204 if ((*prior)->active) {
1205 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1207 "findCandidateTransitions(): adding prior to Candidate pool\n");
1209 transitionStorage.addPriorToCandidatePool();
1217 static uint8_t calcInteriorYears(int8_t* interiorYears,
1218 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1219 int8_t startYear, int8_t endYear) {
1221 for (int8_t year = startYear; year <= endYear; year++) {
1222 if (fromYear <= year && year <= toYear) {
1223 interiorYears[i] = year;
1225 if (i >= maxInteriorYears)
break;
1237 static void createTransitionForYear(
Transition* t, int8_t year,
1238 const ZRB& rule,
const ZoneMatch* match) {
1241 t->offsetMinutes = match->era.offsetMinutes();
1242 t->letterBuf[0] =
'\0';
1244 if (! rule.isNull()) {
1245 t->transitionTime = getTransitionTime(year, rule);
1246 t->deltaMinutes = rule.deltaMinutes();
1248 char letter = rule.letter();
1251 if (letter !=
'-') {
1252 t->letterBuf[0] = letter;
1253 t->letterBuf[1] =
'\0';
1261 t->transitionTime = match->startDateTime;
1262 t->deltaMinutes = match->era.deltaMinutes();
1271 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1272 int8_t startYear, int8_t ) {
1273 if (fromYear < startYear) {
1274 if (toYear < startYear) {
1277 return startYear - 1;
1284 static extended::DateTuple getTransitionTime(
1285 int8_t yearTiny,
const ZRB& rule) {
1286 internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1288 rule.onDayOfMonth());
1289 return {yearTiny, monthDay.month, monthDay.day,
1290 (int16_t) rule.atTimeMinutes(), rule.atTimeSuffix()};
1303 static int8_t compareTransitionToMatchFuzzy(
1305 int16_t ttMonths = t->transitionTime.yearTiny * 12
1306 + t->transitionTime.month;
1308 int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1309 + match->startDateTime.month;
1310 if (ttMonths < matchStartMonths - 1)
return -1;
1312 int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1313 + match->untilDateTime.month;
1314 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1320 static void setAsPriorTransition(
1323 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1324 logging::printf(
"setAsPriorTransition()\n");
1326 Transition* prior = transitionStorage.getPrior();
1327 if (prior->active) {
1328 if (prior->transitionTime < t->transitionTime) {
1330 transitionStorage.setFreeAgentAsPrior();
1334 transitionStorage.setFreeAgentAsPrior();
1347 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1348 logging::printf(
"fixTransitionTimes(): #transitions: %d;\n",
1349 (
int) (end - begin));
1355 for (
Transition** iter = begin; iter != end; ++iter) {
1357 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1358 logging::printf(
"fixTransitionTimes(): LOOP\n");
1360 logging::printf(
"\n");
1362 expandDateTuple(&curr->transitionTime,
1363 &curr->transitionTimeS, &curr->transitionTimeU,
1364 prev->offsetMinutes, prev->deltaMinutes);
1367 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1368 logging::printf(
"fixTransitionTimes(): END\n");
1377 static void expandDateTuple(extended::DateTuple* tt,
1378 extended::DateTuple* tts, extended::DateTuple* ttu,
1379 int16_t offsetMinutes, int16_t deltaMinutes) {
1380 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1381 logging::printf(
"expandDateTuple()\n");
1385 *ttu = {tt->yearTiny, tt->month, tt->day,
1386 (int16_t) (tt->minutes - offsetMinutes),
1388 *tt = {tt->yearTiny, tt->month, tt->day,
1389 (int16_t) (tt->minutes + deltaMinutes),
1393 *tts = {tt->yearTiny, tt->month, tt->day,
1394 (int16_t) (tt->minutes + offsetMinutes),
1396 *tt = {tt->yearTiny, tt->month, tt->day,
1397 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1402 *tts = {tt->yearTiny, tt->month, tt->day,
1403 (int16_t) (tt->minutes - deltaMinutes),
1405 *ttu = {tt->yearTiny, tt->month, tt->day,
1406 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1410 normalizeDateTuple(tt);
1411 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1412 logging::printf(
"expandDateTuple(): normalizeDateTuple(tt): ");
1414 logging::printf(
"\n");
1417 normalizeDateTuple(tts);
1418 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1419 logging::printf(
"expandDateTuple(): normalizeDateTuple(tts): ");
1421 logging::printf(
"\n");
1424 normalizeDateTuple(ttu);
1425 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1426 logging::printf(
"expandDateTuple(): normalizeDateTuple(ttu): ");
1428 logging::printf(
"\n");
1436 static void normalizeDateTuple(extended::DateTuple* dt) {
1437 const int16_t kOneDayAsMinutes = 60 * 24;
1438 if (dt->minutes <= -kOneDayAsMinutes) {
1440 dt->yearTiny, dt->month, dt->day);
1441 local_date_mutation::decrementOneDay(ld);
1442 dt->yearTiny = ld.yearTiny();
1443 dt->month = ld.month();
1445 dt->minutes += kOneDayAsMinutes;
1446 }
else if (kOneDayAsMinutes <= dt->minutes) {
1448 dt->yearTiny, dt->month, dt->day);
1449 local_date_mutation::incrementOneDay(ld);
1450 dt->yearTiny = ld.yearTiny();
1451 dt->month = ld.month();
1453 dt->minutes -= kOneDayAsMinutes;
1463 static void selectActiveTransitions(
1466 Transition** begin = transitionStorage.getCandidatePoolBegin();
1467 Transition** end = transitionStorage.getCandidatePoolEnd();
1468 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1469 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1470 (
int) (end - begin));
1473 for (
Transition** iter = begin; iter != end; ++iter) {
1475 processActiveTransition(match, transition, &prior);
1481 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1483 "selectActiveTransitions(): found latest prior\n");
1485 prior->originalTransitionTime = prior->transitionTime;
1486 prior->transitionTime = match->startDateTime;
1497 static void processActiveTransition(
1501 int8_t status = compareTransitionToMatch(transition, match);
1503 transition->active =
false;
1504 }
else if (status == 1) {
1505 transition->active =
true;
1506 }
else if (status == 0) {
1508 (*prior)->active =
false;
1510 transition->active =
true;
1511 (*prior) = transition;
1514 if ((*prior)->transitionTime < transition->transitionTime) {
1515 (*prior)->active =
false;
1516 transition->active =
true;
1517 (*prior) = transition;
1520 transition->active =
true;
1521 (*prior) = transition;
1540 static int8_t compareTransitionToMatch(
1543 const extended::DateTuple* transitionTime;
1545 const extended::DateTuple& matchStart = match->startDateTime;
1547 transitionTime = &transition->transitionTimeS;
1549 transitionTime = &transition->transitionTimeU;
1551 transitionTime = &transition->transitionTime;
1553 if (*transitionTime < matchStart)
return -1;
1554 if (*transitionTime == matchStart)
return 0;
1556 const extended::DateTuple& matchUntil = match->untilDateTime;
1558 transitionTime = &transition->transitionTimeS;
1559 }
else if (matchUntil.suffix ==
1561 transitionTime = &transition->transitionTimeU;
1563 transitionTime = &transition->transitionTime;
1565 if (*transitionTime < matchUntil)
return 1;
1575 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1577 "generateStartUntilTimes(): #transitions: %d;\n",
1578 (
int) (end - begin));
1582 bool isAfterFirst =
false;
1584 for (
Transition** iter = begin; iter != end; ++iter) {
1588 const extended::DateTuple& tt = t->transitionTime;
1590 prev->untilDateTime = tt;
1596 int16_t minutes = tt.minutes + (
1597 - prev->offsetMinutes - prev->deltaMinutes
1598 + t->offsetMinutes + t->deltaMinutes);
1599 t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
1601 normalizeDateTuple(&t->startDateTime);
1613 const extended::DateTuple& st = t->startDateTime;
1614 const acetime_t offsetSeconds = (acetime_t) 60
1615 * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
1617 st.yearTiny, st.month, st.day);
1618 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1621 isAfterFirst =
true;
1625 extended::DateTuple untilTime = prev->match->untilDateTime;
1626 extended::DateTuple untilTimeS;
1627 extended::DateTuple untilTimeU;
1628 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1629 prev->offsetMinutes, prev->deltaMinutes);
1630 prev->untilDateTime = untilTime;
1637 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1638 logging::printf(
"calcAbbreviations(): #transitions: %d;\n",
1639 (
int) (end - begin));
1641 for (
Transition** iter = begin; iter != end; ++iter) {
1643 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1645 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1646 t->format(), t->deltaMinutes, t->letter());
1648 createAbbreviation(t->abbrev, internal::kAbbrevSize,
1649 t->format(), t->deltaMinutes, t->letter());
1661 static void createAbbreviation(
char* dest, uint8_t destSize,
1662 const char* format, uint16_t deltaMinutes,
const char* letterString) {
1664 if (strchr(format,
'%') !=
nullptr) {
1666 if (letterString ==
nullptr) {
1667 strncpy(dest, format, destSize - 1);
1668 dest[destSize - 1] =
'\0';
1670 ace_common::copyReplaceString(
1671 dest, destSize, format,
'%', letterString);
1675 const char* slashPos = strchr(format,
'/');
1676 if (slashPos !=
nullptr) {
1677 if (deltaMinutes == 0) {
1678 uint8_t headLength = (slashPos - format);
1679 if (headLength >= destSize) headLength = destSize - 1;
1680 memcpy(dest, format, headLength);
1681 dest[headLength] =
'\0';
1683 uint8_t tailLength = strlen(slashPos+1);
1684 if (tailLength >= destSize) tailLength = destSize - 1;
1685 memcpy(dest, slashPos+1, tailLength);
1686 dest[tailLength] =
'\0';
1690 strncpy(dest, format, destSize);
1691 dest[destSize - 1] =
'\0';
1696 const BF* mBrokerFactory;
1697 ZIB mZoneInfoBroker;
1699 mutable int16_t mYear = 0;
1700 mutable bool mIsFilled =
false;
1702 mutable uint8_t mNumMatches = 0;
1703 mutable ZoneMatch mMatches[kMaxMatches];
1713 extended::BrokerFactory,
1714 extended::ZoneInfoBroker,
1715 extended::ZoneEraBroker,
1716 extended::ZonePolicyBroker,
1717 extended::ZoneRuleBroker> {
1725 extended::BrokerFactory,
1726 extended::ZoneInfoBroker,
1727 extended::ZoneEraBroker,
1728 extended::ZonePolicyBroker,
1729 extended::ZoneRuleBroker>(