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 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
24 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG 0
27 class ExtendedZoneProcessorTest_compareEraToYearMonth;
28 class ExtendedZoneProcessorTest_compareEraToYearMonth2;
29 class ExtendedZoneProcessorTest_createMatch;
30 class ExtendedZoneProcessorTest_findMatches_simple;
31 class ExtendedZoneProcessorTest_findMatches_named;
32 class ExtendedZoneProcessorTest_findCandidateTransitions;
33 class ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
34 class ExtendedZoneProcessorTest_getTransitionTime;
35 class ExtendedZoneProcessorTest_createTransitionForYear;
36 class ExtendedZoneProcessorTest_normalizeDateTuple;
37 class ExtendedZoneProcessorTest_expandDateTuple;
38 class ExtendedZoneProcessorTest_calcInteriorYears;
39 class ExtendedZoneProcessorTest_getMostRecentPriorYear;
40 class ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
41 class ExtendedZoneProcessorTest_compareTransitionToMatch;
42 class ExtendedZoneProcessorTest_processTransitionMatchStatus;
43 class ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
44 class ExtendedZoneProcessorTest_createAbbreviation;
45 class ExtendedZoneProcessorTest_setZoneKey;
46 class TransitionStorageTest_getFreeAgent;
47 class TransitionStorageTest_getFreeAgent2;
48 class TransitionStorageTest_addFreeAgentToActivePool;
49 class TransitionStorageTest_reservePrior;
50 class TransitionStorageTest_addFreeAgentToCandidatePool;
51 class TransitionStorageTest_setFreeAgentAsPriorIfValid;
52 class TransitionStorageTest_addActiveCandidatesToActivePool;
53 class TransitionStorageTest_findTransitionForDateTime;
54 class TransitionStorageTest_resetCandidatePool;
55 class TransitionValidation;
69 DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
70 yearTiny(y), month(mon), day(d), suffix(mod), minutes(min) {}
80 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
81 int hour = minutes / 60;
82 int minute = minutes - hour * 60;
83 char c =
"wsu"[(suffix>>4)];
84 logging::printf(
"%04d-%02u-%02uT%02d:%02d%c",
91 inline bool operator<(
const DateTuple& a,
const DateTuple& b) {
92 if (a.yearTiny < b.yearTiny)
return true;
93 if (a.yearTiny > b.yearTiny)
return false;
94 if (a.month < b.month)
return true;
95 if (a.month > b.month)
return false;
96 if (a.day < b.day)
return true;
97 if (a.day > b.day)
return false;
98 if (a.minutes < b.minutes)
return true;
99 if (a.minutes > b.minutes)
return false;
103 inline bool operator>=(
const DateTuple& a,
const DateTuple& b) {
107 inline bool operator<=(
const DateTuple& a,
const DateTuple& b) {
111 inline bool operator>(
const DateTuple& a,
const DateTuple& b) {
116 inline bool operator==(
const DateTuple& a,
const DateTuple& b) {
117 return a.yearTiny == b.yearTiny
118 && a.month == b.month
120 && a.minutes == b.minutes
121 && a.suffix == b.suffix;
136 template<
typename ZEB>
160 logging::printf(
"MatchingEra(");
163 logging::printf(
"; era=%c", (
era.isNull()) ?
'-' :
'*');
164 logging::printf(
"; prevMatch=%c", (
prevMatch) ?
'*' :
'-');
165 logging::printf(
")");
170 template <
typename T>
171 void swap(T& a, T& b) {
181 enum class MatchStatus : uint8_t {
189 inline bool isMatchStatusActive(MatchStatus status) {
190 return status == MatchStatus::kExactMatch
191 || status == MatchStatus::kWithinMatch
192 || status == MatchStatus::kPrior;
224 template <
typename ZEB,
typename ZPB,
typename ZRB>
274 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
321 const char* format()
const {
322 return match->era.format();
346 const ZPB policy =
match->era.zonePolicy();
347 uint8_t numLetters = policy.numLetters();
348 if (
letter >= numLetters) {
356 return policy.letter(
letter);
361 logging::printf(
"Transition(");
362 if (
sizeof(acetime_t) <=
sizeof(
int)) {
368 logging::printf(
"; UTC");
375 logging::printf(
"; rule=-");
377 logging::printf(
"; rule=");
378 logging::printf(
"[%d,%d]",
393 uint8_t hour = minutes / 60;
394 uint8_t minute = minutes - hour * 60;
395 logging::printf(
"%c%02u:%02u", sign, (
unsigned) hour, (
unsigned) minute);
430 template<u
int8_t SIZE,
typename ZEB,
typename ZPB,
typename ZRB>
444 for (uint8_t i = 0; i < SIZE; i++) {
445 mTransitions[i] = &mPool[i];
448 mIndexCandidates = 0;
454 return mTransitions[mIndexPrior];
466 mIndexCandidates = mIndexPrior;
467 mIndexFree = mIndexPrior;
471 return &mTransitions[mIndexCandidates];
474 return &mTransitions[mIndexFree];
478 return &mTransitions[0];
481 return &mTransitions[mIndexFree];
492 if (mIndexFree > mHighWater) {
493 mHighWater = mIndexFree;
496 if (mIndexFree < SIZE) {
497 return mTransitions[mIndexFree];
499 return mTransitions[SIZE - 1];
511 if (mIndexFree >= SIZE)
return;
513 mIndexPrior = mIndexFree;
514 mIndexCandidates = mIndexFree;
527 return &mTransitions[mIndexPrior];
538 swap(mTransitions[mIndexPrior], mTransitions[mIndexFree]);
558 if (mIndexFree >= SIZE)
return;
567 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
571 mTransitions[i] = prev;
572 mTransitions[i - 1] = curr;
584 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
585 logging::printf(
"addActiveCandidatesToActivePool()\n");
589 uint8_t iActive = mIndexPrior;
590 uint8_t iCandidate = mIndexCandidates;
591 for (; iCandidate < mIndexFree; iCandidate++) {
592 if (isMatchStatusActive(mTransitions[iCandidate]->matchStatus)) {
593 if (iActive != iCandidate) {
596 swap(mTransitions[iActive], mTransitions[iCandidate]);
602 mIndexPrior = iActive;
603 mIndexCandidates = iActive;
604 mIndexFree = iActive;
606 return mTransitions[iActive - 1];
618 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
619 logging::printf(
"findTransition(): mIndexFree: %d\n", mIndexFree);
623 for (uint8_t i = 0; i < mIndexFree; i++) {
624 const Transition* candidate = mTransitions[i];
656 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
658 "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
672 for (uint8_t i = 0; i < mIndexFree; i++) {
673 const Transition* candidate = mTransitions[i];
682 logging::printf(
"TransitionStorage: ");
683 logging::printf(
"nActives=%d", mIndexPrior);
684 logging::printf(
", nPrior=%d", mIndexCandidates - mIndexPrior);
685 logging::printf(
", nCandidates=%d", mIndexFree - mIndexCandidates);
686 logging::printf(
", nFree=%d", SIZE - mIndexFree);
687 logging::printf(
"\n");
689 if (mIndexPrior != 0) {
690 logging::printf(
" Actives:\n");
691 for (uint8_t i = 0; i < mIndexPrior; i++) {
692 logging::printf(
" ");
693 mTransitions[i]->
log();
694 logging::printf(
"\n");
697 if (mIndexPrior != mIndexCandidates) {
698 logging::printf(
" Prior: \n");
699 logging::printf(
" ");
700 mTransitions[mIndexPrior]->
log();
701 logging::printf(
"\n");
703 if (mIndexCandidates != mIndexFree) {
704 logging::printf(
" Candidates:\n");
705 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
706 logging::printf(
" ");
707 mTransitions[i]->
log();
708 logging::printf(
"\n");
724 friend class ::TransitionStorageTest_getFreeAgent;
725 friend class ::TransitionStorageTest_getFreeAgent2;
726 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
727 friend class ::TransitionStorageTest_reservePrior;
728 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
729 friend class ::TransitionStorageTest_setFreeAgentAsPriorIfValid;
730 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
731 friend class ::TransitionStorageTest_findTransitionForDateTime;
732 friend class ::TransitionStorageTest_resetCandidatePool;
736 return mTransitions[i];
742 uint8_t mIndexCandidates;
746 uint8_t mHighWater = 0;
782 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
805 uint32_t
getZoneId()
const override {
return mZoneInfoBroker.zoneId(); }
810 const Transition* transition = findTransition(epochSeconds);
820 const Transition* transition = findTransition(epochSeconds);
824 const char*
getAbbrev(acetime_t epochSeconds)
const override {
826 if (!success)
return "";
827 const Transition* transition = findTransition(epochSeconds);
828 return transition->
abbrev;
833 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
834 logging::printf(
"getOffsetDateTime(): ldt=");
835 ldt.
printTo(SERIAL_PORT_MONITOR);
836 SERIAL_PORT_MONITOR.println();
844 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
845 logging::printf(
"getOffsetDateTime(): match transition=");
847 logging::printf(
"\n");
849 offset = (transition)
861 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
862 logging::printf(
"getOffsetDateTime(): odt=");
863 odt.printTo(SERIAL_PORT_MONITOR);
864 SERIAL_PORT_MONITOR.println();
873 acetime_t epochSeconds = odt.toEpochSeconds();
876 offset = (transition)
881 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
882 logging::printf(
"getOffsetDateTime(): normalized(odt)=");
883 odt.printTo(SERIAL_PORT_MONITOR);
884 SERIAL_PORT_MONITOR.println();
890 mZoneInfoBroker.printNameTo(printer);
894 mZoneInfoBroker.printShortNameTo(printer);
899 logging::printf(
"ExtendedZoneProcessor\n");
900 logging::printf(
" mYear: %d\n", mYear);
901 logging::printf(
" mNumMatches: %d\n", mNumMatches);
902 for (
int i = 0; i < mNumMatches; i++) {
903 logging::printf(
" Match %d: ", i);
905 logging::printf(
"\n");
907 mTransitionStorage.
log();
921 if (mZoneInfoBroker.equals(zoneKey))
return;
923 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
931 return mZoneInfoBroker.equals(zoneKey);
941 mBrokerFactory = brokerFactory;
960 if (isFilled(year))
return true;
961 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
962 logging::printf(
"initForYear(): %d\n", year);
967 mTransitionStorage.
init();
969 if (year < mZoneInfoBroker.zoneContext()->startYear - 1
970 || mZoneInfoBroker.zoneContext()->untilYear < year) {
971 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
972 logging::printf(
"init(): Year %d out of valid range [%d, %d)\n",
974 mZoneInfoBroker.zoneContext()->startYear,
975 mZoneInfoBroker.zoneContext()->untilYear);
987 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
988 logging::printf(
"==== Step 1: findMatches()\n");
990 mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
992 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
995 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
996 logging::printf(
"==== Step 2: createTransitions()\n");
998 createTransitions(mTransitionStorage, mMatches, mNumMatches);
999 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1002 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1003 logging::printf(
"==== Step 3: fixTransitionTimes()\n");
1005 Transition** begin = mTransitionStorage.getActivePoolBegin();
1006 Transition** end = mTransitionStorage.getActivePoolEnd();
1007 fixTransitionTimes(begin, end);
1008 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1011 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1012 logging::printf(
"==== Step 4: generateStartUntilTimes()\n");
1014 generateStartUntilTimes(begin, end);
1015 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1018 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1019 logging::printf(
"==== Step 5: calcAbbreviations()\n");
1021 calcAbbreviations(begin, end);
1022 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1037 const BF* brokerFactory,
1041 mBrokerFactory(brokerFactory)
1047 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
1048 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
1049 friend class ::ExtendedZoneProcessorTest_createMatch;
1050 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
1051 friend class ::ExtendedZoneProcessorTest_findMatches_named;
1052 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
1053 friend class ::ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
1054 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
1055 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
1056 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
1057 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
1058 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
1059 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
1060 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
1061 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
1062 friend class ::ExtendedZoneProcessorTest_processTransitionMatchStatus;
1063 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
1064 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
1065 friend class ::ExtendedZoneProcessorTest_setZoneKey;
1066 friend class ::TransitionStorageTest_findTransitionForDateTime;
1067 friend class ::TransitionValidation;
1079 static const uint8_t kMaxMatches = 4;
1085 static const uint8_t kMaxInteriorYears = 4;
1088 return mZoneInfoBroker.equals(
1096 const Transition* findTransition(acetime_t epochSeconds)
const {
1101 bool isFilled(int16_t year)
const {
1102 return mIsFilled && (year == mYear);
1112 static uint8_t findMatches(
1113 const ZIB& zoneInfo,
1114 const extended::YearMonthTuple& startYm,
1115 const extended::YearMonthTuple& untilYm,
1119 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1120 logging::printf(
"findMatches()\n");
1124 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
1125 const ZEB era = zoneInfo.era(iEra);
1126 if (eraOverlapsInterval(prevMatch, era, startYm, untilYm)) {
1127 if (iMatch < maxMatches) {
1128 matches[iMatch] = createMatch(prevMatch, era, startYm, untilYm);
1129 prevMatch = &matches[iMatch];
1148 static bool eraOverlapsInterval(
1151 const extended::YearMonthTuple& startYm,
1152 const extended::YearMonthTuple& untilYm) {
1153 return (prevMatch ==
nullptr || compareEraToYearMonth(
1154 prevMatch->era, untilYm.yearTiny, untilYm.month) < 0)
1155 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1159 static int8_t compareEraToYearMonth(
const ZEB& era,
1160 int8_t yearTiny, uint8_t month) {
1161 if (era.untilYearTiny() < yearTiny)
return -1;
1162 if (era.untilYearTiny() > yearTiny)
return 1;
1163 if (era.untilMonth() < month)
return -1;
1164 if (era.untilMonth() > month)
return 1;
1165 if (era.untilDay() > 1)
return 1;
1167 if (era.untilTimeMinutes() > 0)
return 1;
1180 const extended::YearMonthTuple& startYm,
1181 const extended::YearMonthTuple& untilYm) {
1185 extended::DateTuple startDate = (prevMatch ==
nullptr)
1186 ? extended::DateTuple{
1193 : extended::DateTuple{
1194 prevMatch->era.untilYearTiny(),
1195 prevMatch->era.untilMonth(),
1196 prevMatch->era.untilDay(),
1197 (int16_t) prevMatch->era.untilTimeMinutes(),
1198 prevMatch->era.untilTimeSuffix()
1200 extended::DateTuple lowerBound{
1207 if (startDate < lowerBound) {
1208 startDate = lowerBound;
1211 extended::DateTuple untilDate{
1212 era.untilYearTiny(),
1215 (int16_t) era.untilTimeMinutes(),
1216 era.untilTimeSuffix()
1218 extended::DateTuple upperBound{
1225 if (upperBound < untilDate) {
1226 untilDate = upperBound;
1229 return {startDate, untilDate, era, prevMatch, 0, 0};
1236 static void createTransitions(
1239 uint8_t numMatches) {
1240 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1241 logging::printf(
"createTransitions()\n");
1244 for (uint8_t i = 0; i < numMatches; i++) {
1245 createTransitionsForMatch(transitionStorage, &matches[i]);
1250 static void createTransitionsForMatch(
1253 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1254 logging::printf(
"== createTransitionsForMatch()\n");
1256 const ZPB policy = match->era.zonePolicy();
1257 if (policy.isNull()) {
1258 createTransitionsFromSimpleMatch(transitionStorage, match);
1260 createTransitionsFromNamedMatch(transitionStorage, match);
1264 static void createTransitionsFromSimpleMatch(
1267 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1268 logging::printf(
"== createTransitionsFromSimpleMatch()\n");
1271 Transition* freeTransition = transitionStorage.getFreeAgent();
1272 createTransitionForYear(freeTransition, 0 ,
1274 freeTransition->matchStatus = extended::MatchStatus::kExactMatch;
1275 match->lastOffsetMinutes = freeTransition->offsetMinutes;
1276 match->lastDeltaMinutes = freeTransition->deltaMinutes;
1277 transitionStorage.addFreeAgentToActivePool();
1278 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1279 freeTransition->log();
1280 logging::printf(
"\n");
1284 static void createTransitionsFromNamedMatch(
1287 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1288 logging::printf(
"== createTransitionsFromNamedMatch()\n");
1291 transitionStorage.resetCandidatePool();
1292 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1293 match->log(); logging::printf(
"\n");
1297 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1298 logging::printf(
"---- Pass 1: findCandidateTransitions()\n");
1300 findCandidateTransitions(transitionStorage, match);
1301 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1302 transitionStorage.log();
1307 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1308 logging::printf(
"---- Pass 2: fixTransitionTimes()\n");
1311 transitionStorage.getCandidatePoolBegin(),
1312 transitionStorage.getCandidatePoolEnd());
1316 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1317 logging::printf(
"---- Pass 3: selectActiveTransitions()\n");
1319 selectActiveTransitions(
1320 transitionStorage.getCandidatePoolBegin(),
1321 transitionStorage.getCandidatePoolEnd());
1322 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1323 transitionStorage.log();
1326 transitionStorage.addActiveCandidatesToActivePool();
1327 match->lastOffsetMinutes = lastTransition->offsetMinutes;
1328 match->lastDeltaMinutes = lastTransition->deltaMinutes;
1329 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1330 transitionStorage.log();
1334 static void findCandidateTransitions(
1337 using extended::MatchStatus;
1339 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1340 logging::printf(
"findCandidateTransitions(): \n");
1342 logging::printf(
"\n");
1344 const ZPB policy = match->era.zonePolicy();
1345 uint8_t numRules = policy.numRules();
1346 int8_t startY = match->startDateTime.yearTiny;
1347 int8_t endY = match->untilDateTime.yearTiny;
1349 Transition** prior = transitionStorage.reservePrior();
1350 (*prior)->isValidPrior =
false;
1351 for (uint8_t r = 0; r < numRules; r++) {
1352 const ZRB rule = policy.rule(r);
1355 int8_t interiorYears[kMaxInteriorYears];
1356 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1357 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1358 for (uint8_t y = 0; y < numYears; y++) {
1359 int8_t year = interiorYears[y];
1360 Transition* t = transitionStorage.getFreeAgent();
1361 createTransitionForYear(t, year, rule, match);
1362 MatchStatus status = compareTransitionToMatchFuzzy(t, match);
1363 if (status == MatchStatus::kPrior) {
1364 transitionStorage.setFreeAgentAsPriorIfValid();
1365 }
else if (status == MatchStatus::kWithinMatch) {
1366 transitionStorage.addFreeAgentToCandidatePool();
1374 int8_t priorYear = getMostRecentPriorYear(
1375 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1377 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1379 "findCandidateTransitions(): priorYear: %d\n",
1382 Transition* t = transitionStorage.getFreeAgent();
1383 createTransitionForYear(t, priorYear, rule, match);
1384 transitionStorage.setFreeAgentAsPriorIfValid();
1390 if ((*prior)->isValidPrior) {
1391 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1393 "findCandidateTransitions(): adding prior to Candidate pool\n");
1395 logging::printf(
"\n");
1397 transitionStorage.addPriorToCandidatePool();
1420 static uint8_t calcInteriorYears(
1421 int8_t* interiorYears,
1422 uint8_t maxInteriorYears,
1423 int8_t fromYear, int8_t toYear,
1424 int8_t startYear, int8_t endYear) {
1426 for (int8_t year = startYear; year <= endYear; year++) {
1427 if (fromYear <= year && year <= toYear) {
1428 interiorYears[i] = year;
1430 if (i >= maxInteriorYears)
break;
1442 static void createTransitionForYear(
1449 t->offsetMinutes = match->era.offsetMinutes();
1450 t->letterBuf[0] =
'\0';
1452 if (! rule.isNull()) {
1453 t->transitionTime = getTransitionTime(year, rule);
1454 t->deltaMinutes = rule.deltaMinutes();
1456 char letter = rule.letter();
1459 if (letter !=
'-') {
1460 t->letterBuf[0] = letter;
1461 t->letterBuf[1] =
'\0';
1471 t->transitionTime = match->startDateTime;
1472 t->deltaMinutes = match->era.deltaMinutes();
1488 static int8_t getMostRecentPriorYear(
1489 int8_t fromYear, int8_t toYear,
1490 int8_t startYear, int8_t endYear) {
1494 if (fromYear < startYear) {
1495 if (toYear < startYear) {
1498 return startYear - 1;
1509 static extended::DateTuple getTransitionTime(
1510 int8_t yearTiny,
const ZRB& rule) {
1512 internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1516 rule.onDayOfMonth());
1521 (int16_t) rule.atTimeMinutes(),
1536 static extended::MatchStatus compareTransitionToMatchFuzzy(
1538 using extended::MatchStatus;
1540 int16_t ttMonths = t->transitionTime.yearTiny * 12
1541 + t->transitionTime.month;
1543 int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1544 + match->startDateTime.month;
1545 if (ttMonths < matchStartMonths - 1)
return MatchStatus::kPrior;
1547 int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1548 + match->untilDateTime.month;
1549 if (matchUntilMonths + 2 <= ttMonths)
return MatchStatus::kFarFuture;
1551 return MatchStatus::kWithinMatch;
1563 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1564 logging::printf(
"fixTransitionTimes(): START; #transitions=%d\n",
1565 (
int) (end - begin));
1566 printTransitions(begin, end);
1572 for (
Transition** iter = begin; iter != end; ++iter) {
1575 &curr->transitionTime,
1576 prev->offsetMinutes,
1578 &curr->transitionTime,
1579 &curr->transitionTimeS,
1580 &curr->transitionTimeU);
1583 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1584 logging::printf(
"fixTransitionTimes(): FIXED\n");
1585 printTransitions(begin, end);
1586 logging::printf(
"fixTransitionTimes(): END\n");
1590 #ifdef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1592 for (
Transition** iter = begin; iter != end; ++iter) {
1594 logging::printf(
"\n");
1604 static void expandDateTuple(
1605 const extended::DateTuple* tt,
1606 int16_t offsetMinutes,
1607 int16_t deltaMinutes,
1608 extended::DateTuple* ttw,
1609 extended::DateTuple* tts,
1610 extended::DateTuple* ttu) {
1614 *ttu = {tt->yearTiny, tt->month, tt->day,
1615 (int16_t) (tt->minutes - offsetMinutes),
1617 *ttw = {tt->yearTiny, tt->month, tt->day,
1618 (int16_t) (tt->minutes + deltaMinutes),
1622 *tts = {tt->yearTiny, tt->month, tt->day,
1623 (int16_t) (tt->minutes + offsetMinutes),
1625 *ttw = {tt->yearTiny, tt->month, tt->day,
1626 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1632 *tts = {tt->yearTiny, tt->month, tt->day,
1633 (int16_t) (tt->minutes - deltaMinutes),
1635 *ttu = {tt->yearTiny, tt->month, tt->day,
1636 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1640 normalizeDateTuple(ttw);
1641 normalizeDateTuple(tts);
1642 normalizeDateTuple(ttu);
1649 static void normalizeDateTuple(extended::DateTuple* dt) {
1650 const int16_t kOneDayAsMinutes = 60 * 24;
1651 if (dt->minutes <= -kOneDayAsMinutes) {
1653 dt->yearTiny, dt->month, dt->day);
1654 local_date_mutation::decrementOneDay(ld);
1655 dt->yearTiny = ld.yearTiny();
1656 dt->month = ld.month();
1658 dt->minutes += kOneDayAsMinutes;
1659 }
else if (kOneDayAsMinutes <= dt->minutes) {
1661 dt->yearTiny, dt->month, dt->day);
1662 local_date_mutation::incrementOneDay(ld);
1663 dt->yearTiny = ld.yearTiny();
1664 dt->month = ld.month();
1666 dt->minutes -= kOneDayAsMinutes;
1677 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1678 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1679 (
int) (end - begin));
1683 for (
Transition** iter = begin; iter != end; ++iter) {
1685 processTransitionMatchStatus(transition, &prior);
1691 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1693 "selectActiveTransitions(): found latest prior\n");
1695 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1696 prior->originalTransitionTime = prior->transitionTime;
1698 prior->transitionTime = prior->match->startDateTime;
1708 static void processTransitionMatchStatus(
1711 using extended::MatchStatus;
1713 MatchStatus status = compareTransitionToMatch(
1714 transition, transition->match);
1715 transition->matchStatus = status;
1717 if (status == MatchStatus::kExactMatch) {
1719 (*prior)->matchStatus = MatchStatus::kFarPast;
1721 (*prior) = transition;
1722 }
else if (status == MatchStatus::kPrior) {
1724 if ((*prior)->transitionTimeU <= transition->transitionTimeU) {
1725 (*prior)->matchStatus = MatchStatus::kFarPast;
1726 (*prior) = transition;
1728 transition->matchStatus = MatchStatus::kFarPast;
1731 (*prior) = transition;
1744 static extended::MatchStatus compareTransitionToMatch(
1749 int16_t prevMatchOffsetMinutes;
1750 int16_t prevMatchDeltaMinutes;
1751 if (match->prevMatch) {
1752 prevMatchOffsetMinutes = match->prevMatch->lastOffsetMinutes;
1753 prevMatchDeltaMinutes = match->prevMatch->lastDeltaMinutes;
1755 prevMatchOffsetMinutes = match->era.offsetMinutes();
1756 prevMatchDeltaMinutes = 0;
1760 extended::DateTuple stw;
1761 extended::DateTuple sts;
1762 extended::DateTuple stu;
1764 &match->startDateTime,
1765 prevMatchOffsetMinutes,
1766 prevMatchDeltaMinutes,
1772 const extended::DateTuple& ttw = transition->transitionTime;
1773 const extended::DateTuple& tts = transition->transitionTimeS;
1774 const extended::DateTuple& ttu = transition->transitionTimeU;
1779 if (ttw == stw || tts == sts || ttu == stu) {
1780 return extended::MatchStatus::kExactMatch;
1784 return extended::MatchStatus::kPrior;
1792 const extended::DateTuple& matchUntil = match->untilDateTime;
1793 const extended::DateTuple* transitionTime;
1795 transitionTime = &tts;
1797 transitionTime = &ttu;
1799 transitionTime = &ttw;
1801 if (*transitionTime < matchUntil) {
1802 return extended::MatchStatus::kWithinMatch;
1804 return extended::MatchStatus::kFarFuture;
1813 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1815 "generateStartUntilTimes(): #transitions=%d\n",
1816 (
int) (end - begin));
1820 bool isAfterFirst =
false;
1822 for (
Transition** iter = begin; iter != end; ++iter) {
1826 const extended::DateTuple& tt = t->transitionTime;
1828 prev->untilDateTime = tt;
1834 int16_t minutes = tt.minutes + (
1835 - prev->offsetMinutes - prev->deltaMinutes
1836 + t->offsetMinutes + t->deltaMinutes);
1837 t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
1839 normalizeDateTuple(&t->startDateTime);
1851 const extended::DateTuple& st = t->startDateTime;
1852 const acetime_t offsetSeconds = (acetime_t) 60
1853 * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
1855 st.yearTiny, st.month, st.day);
1856 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1859 isAfterFirst =
true;
1863 extended::DateTuple untilTimeW;
1864 extended::DateTuple untilTimeS;
1865 extended::DateTuple untilTimeU;
1867 &prev->match->untilDateTime,
1868 prev->offsetMinutes,
1873 prev->untilDateTime = untilTimeW;
1880 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1881 logging::printf(
"calcAbbreviations(): #transitions: %d\n",
1882 (
int) (end - begin));
1884 for (
Transition** iter = begin; iter != end; ++iter) {
1886 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1888 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1889 t->format(), t->deltaMinutes, t->letter());
1893 internal::kAbbrevSize,
1908 static void createAbbreviation(
1912 uint16_t deltaMinutes,
1913 const char* letterString) {
1916 if (strchr(format,
'%') !=
nullptr) {
1918 if (letterString ==
nullptr) {
1919 strncpy(dest, format, destSize - 1);
1920 dest[destSize - 1] =
'\0';
1922 ace_common::copyReplaceString(
1923 dest, destSize, format,
'%', letterString);
1927 const char* slashPos = strchr(format,
'/');
1928 if (slashPos !=
nullptr) {
1929 if (deltaMinutes == 0) {
1930 uint8_t headLength = (slashPos - format);
1931 if (headLength >= destSize) headLength = destSize - 1;
1932 memcpy(dest, format, headLength);
1933 dest[headLength] =
'\0';
1935 uint8_t tailLength = strlen(slashPos+1);
1936 if (tailLength >= destSize) tailLength = destSize - 1;
1937 memcpy(dest, slashPos+1, tailLength);
1938 dest[tailLength] =
'\0';
1942 strncpy(dest, format, destSize);
1943 dest[destSize - 1] =
'\0';
1948 const BF* mBrokerFactory;
1949 ZIB mZoneInfoBroker;
1951 mutable int16_t mYear = 0;
1952 mutable bool mIsFilled =
false;
1954 mutable uint8_t mNumMatches = 0;
1965 extended::BrokerFactory,
1966 extended::ZoneInfoBroker,
1967 extended::ZoneEraBroker,
1968 extended::ZonePolicyBroker,
1969 extended::ZoneRuleBroker> {
1977 extended::BrokerFactory,
1978 extended::ZoneInfoBroker,
1979 extended::ZoneEraBroker,
1980 extended::ZonePolicyBroker,
1981 extended::ZoneRuleBroker>(