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;
71 DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
72 yearTiny(y), month(mon), day(d), suffix(mod), minutes(min) {}
82 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
83 int hour = minutes / 60;
84 int minute = minutes - hour * 60;
85 char c =
"wsu"[(suffix>>4)];
86 logging::printf(
"%04d-%02u-%02uT%02d:%02d%c",
93 inline bool operator<(
const DateTuple& a,
const DateTuple& b) {
94 if (a.yearTiny < b.yearTiny)
return true;
95 if (a.yearTiny > b.yearTiny)
return false;
96 if (a.month < b.month)
return true;
97 if (a.month > b.month)
return false;
98 if (a.day < b.day)
return true;
99 if (a.day > b.day)
return false;
100 if (a.minutes < b.minutes)
return true;
101 if (a.minutes > b.minutes)
return false;
105 inline bool operator>=(
const DateTuple& a,
const DateTuple& b) {
109 inline bool operator<=(
const DateTuple& a,
const DateTuple& b) {
113 inline bool operator>(
const DateTuple& a,
const DateTuple& b) {
118 inline bool operator==(
const DateTuple& a,
const DateTuple& b) {
119 return a.yearTiny == b.yearTiny
120 && a.month == b.month
122 && a.minutes == b.minutes
123 && a.suffix == b.suffix;
127 inline void normalizeDateTuple(DateTuple* dt) {
128 const int16_t kOneDayAsMinutes = 60 * 24;
129 if (dt->minutes <= -kOneDayAsMinutes) {
131 dt->yearTiny, dt->month, dt->day);
132 local_date_mutation::decrementOneDay(ld);
133 dt->yearTiny = ld.yearTiny();
134 dt->month = ld.month();
136 dt->minutes += kOneDayAsMinutes;
137 }
else if (kOneDayAsMinutes <= dt->minutes) {
139 dt->yearTiny, dt->month, dt->day);
140 local_date_mutation::incrementOneDay(ld);
141 dt->yearTiny = ld.yearTiny();
142 dt->month = ld.month();
144 dt->minutes -= kOneDayAsMinutes;
151 inline acetime_t subtractDateTuple(
const DateTuple& a,
const DateTuple& b) {
154 int32_t epochSecondsA = epochDaysA * 86400 + a.minutes * 60;
158 int32_t epochSecondsB = epochDaysB * 86400 + b.minutes * 60;
160 return epochSecondsA - epochSecondsB;
177 template<
typename ZEB>
201 logging::printf(
"MatchingEra(");
204 logging::printf(
"; era=%c", (
era.isNull()) ?
'-' :
'*');
205 logging::printf(
"; prevMatch=%c", (
prevMatch) ?
'*' :
'-');
206 logging::printf(
")");
211 template <
typename T>
212 void swap(T& a, T& b) {
222 enum class MatchStatus : uint8_t {
230 inline bool isMatchStatusActive(MatchStatus status) {
231 return status == MatchStatus::kExactMatch
232 || status == MatchStatus::kWithinMatch
233 || status == MatchStatus::kPrior;
270 template <
typename ZEB,
typename ZPB,
typename ZRB>
321 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
368 const char* format()
const {
369 return match->era.format();
393 const ZPB policy =
match->era.zonePolicy();
394 uint8_t numLetters = policy.numLetters();
395 if (
letter >= numLetters) {
403 return policy.letter(
letter);
408 logging::printf(
"Transition(");
409 if (
sizeof(acetime_t) <=
sizeof(
int)) {
415 logging::printf(
"; UTC");
422 logging::printf(
"; rule=-");
424 logging::printf(
"; rule=");
425 logging::printf(
"[%d,%d]",
440 uint8_t hour = minutes / 60;
441 uint8_t minute = minutes - hour * 60;
442 logging::printf(
"%c%02u:%02u", sign, (
unsigned) hour, (
unsigned) minute);
451 template <
typename ZEB,
typename ZPB,
typename ZRB>
462 template <
typename ZEB,
typename ZPB,
typename ZRB>
464 static constexpr int8_t kStatusGap = 0;
465 static constexpr int8_t kStatusExact = 1;
466 static constexpr int8_t kStatusOverlap = 2;
504 template<u
int8_t SIZE,
typename ZEB,
typename ZPB,
typename ZRB>
537 for (uint8_t i = 0; i < SIZE; i++) {
538 mTransitions[i] = &mPool[i];
541 mIndexCandidates = 0;
547 return mTransitions[mIndexPrior];
559 mIndexCandidates = mIndexPrior;
560 mIndexFree = mIndexPrior;
564 return &mTransitions[mIndexCandidates];
567 return &mTransitions[mIndexFree];
571 return &mTransitions[0];
574 return &mTransitions[mIndexFree];
583 if (mIndexFree < SIZE) {
585 if (mIndexFree >= mAllocSize) {
586 mAllocSize = mIndexFree + 1;
588 return mTransitions[mIndexFree];
594 return mTransitions[SIZE - 1];
606 if (mIndexFree >= SIZE)
return;
608 mIndexPrior = mIndexFree;
609 mIndexCandidates = mIndexFree;
625 return &mTransitions[mIndexPrior];
631 Transition* prior = mTransitions[mIndexPrior];
636 swap(mTransitions[mIndexPrior], mTransitions[mIndexFree]);
656 if (mIndexFree >= SIZE)
return;
665 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
669 mTransitions[i] = prev;
670 mTransitions[i - 1] = curr;
682 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
683 logging::printf(
"addActiveCandidatesToActivePool()\n");
687 uint8_t iActive = mIndexPrior;
688 uint8_t iCandidate = mIndexCandidates;
689 for (; iCandidate < mIndexFree; iCandidate++) {
690 if (isMatchStatusActive(mTransitions[iCandidate]->matchStatus)) {
691 if (iActive != iCandidate) {
694 swap(mTransitions[iActive], mTransitions[iCandidate]);
700 mIndexPrior = iActive;
701 mIndexCandidates = iActive;
702 mIndexFree = iActive;
704 return mTransitions[iActive - 1];
717 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
719 "findTransitionForSeconds(): mIndexFree: %d\n", mIndexFree);
724 for (uint8_t i = 0; i < mIndexFree; i++) {
725 const Transition* candidate = mTransitions[i];
730 uint8_t fold = calculateFold(epochSeconds, match, prevMatch);
734 static uint8_t calculateFold(
735 acetime_t epochSeconds,
739 if (match ==
nullptr)
return 0;
740 if (prevMatch ==
nullptr)
return 0;
743 acetime_t overlapSeconds = subtractDateTuple(
744 prevMatch->untilDateTime, match->startDateTime);
745 if (overlapSeconds <= 0)
return 0;
746 acetime_t secondsFromTransitionStart =
747 epochSeconds - match->startEpochSeconds;
748 if (secondsFromTransitionStart >= overlapSeconds)
return 0;
774 int8_t searchStatus = TransitionResult::kStatusGap;
775 for (uint8_t i = 0; i < mIndexFree; i++) {
776 candidate = mTransitions[i];
780 bool isExactMatch = (startDateTime <= localDate)
781 && (localDate < untilDateTime);
785 if (searchStatus == TransitionResult::kStatusExact) {
786 searchStatus = TransitionResult::kStatusOverlap;
791 searchStatus = TransitionResult::kStatusExact;
793 }
else if (startDateTime > localDate) {
798 prevCandidate = candidate;
807 if (searchStatus == TransitionResult::kStatusExact) {
821 logging::printf(
"TransitionStorage: ");
822 logging::printf(
"nActives=%d", mIndexPrior);
823 logging::printf(
", nPrior=%d", mIndexCandidates - mIndexPrior);
824 logging::printf(
", nCandidates=%d", mIndexFree - mIndexCandidates);
825 logging::printf(
", nFree=%d", SIZE - mIndexFree);
826 logging::printf(
"\n");
828 if (mIndexPrior != 0) {
829 logging::printf(
" Actives:\n");
830 for (uint8_t i = 0; i < mIndexPrior; i++) {
831 logging::printf(
" ");
832 mTransitions[i]->
log();
833 logging::printf(
"\n");
836 if (mIndexPrior != mIndexCandidates) {
837 logging::printf(
" Prior: \n");
838 logging::printf(
" ");
839 mTransitions[mIndexPrior]->
log();
840 logging::printf(
"\n");
842 if (mIndexCandidates != mIndexFree) {
843 logging::printf(
" Candidates:\n");
844 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
845 logging::printf(
" ");
846 mTransitions[i]->
log();
847 logging::printf(
"\n");
863 friend class ::TransitionStorageTest_getFreeAgent;
864 friend class ::TransitionStorageTest_getFreeAgent2;
865 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
866 friend class ::TransitionStorageTest_reservePrior;
867 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
868 friend class ::TransitionStorageTest_setFreeAgentAsPriorIfValid;
869 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
870 friend class ::TransitionStorageTest_findTransitionForDateTime;
871 friend class ::TransitionStorageTest_resetCandidatePool;
875 return mTransitions[i];
881 uint8_t mIndexCandidates;
885 uint8_t mAllocSize = 0;
921 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
954 bool isLink()
const override {
return mZoneInfoBroker.isLink(); }
956 uint32_t
getZoneId(
bool followLink =
false)
const override {
957 ZIB zib = (
isLink() && followLink)
958 ? mZoneInfoBroker.targetZoneInfo()
968 const Transition* transition = matchingTransition.transition;
980 const Transition* transition = matchingTransition.transition;
986 const char*
getAbbrev(acetime_t epochSeconds)
const override {
988 if (!success)
return "";
991 const Transition* transition = matchingTransition.transition;
992 return (transition) ? transition->
abbrev :
"";
1026 bool needsNormalization =
false;
1028 if (result.searchStatus == TransitionResult::kStatusExact) {
1029 transition = result.transition0;
1031 if (result.transition0 ==
nullptr || result.transition1 ==
nullptr) {
1033 transition =
nullptr;
1035 needsNormalization =
1036 (result.searchStatus == TransitionResult::kStatusGap);
1037 transition = (ldt.
fold() == 0)
1038 ? result.transition0
1039 : result.transition1;
1051 if (needsNormalization) {
1052 acetime_t epochSeconds = odt.toEpochSeconds();
1060 ? result.transition1
1061 : result.transition0;
1071 odt.fold(1 - ldt.
fold());
1091 const Transition* transition = matchingTransition.transition;
1097 epochSeconds, timeOffset, matchingTransition.fold);
1100 void printNameTo(Print& printer,
bool followLink =
false)
const override {
1101 ZIB zib = (
isLink() && followLink)
1102 ? mZoneInfoBroker.targetZoneInfo()
1104 zib.printNameTo(printer);
1109 ZIB zib = (
isLink() && followLink)
1110 ? mZoneInfoBroker.targetZoneInfo()
1112 zib.printShortNameTo(printer);
1117 logging::printf(
"ExtendedZoneProcessor\n");
1118 logging::printf(
" mYear: %d\n", mYear);
1119 logging::printf(
" mNumMatches: %d\n", mNumMatches);
1120 for (
int i = 0; i < mNumMatches; i++) {
1121 logging::printf(
" Match %d: ", i);
1123 logging::printf(
"\n");
1125 mTransitionStorage.
log();
1139 if (mZoneInfoBroker.equals(zoneKey))
return;
1141 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
1149 return mZoneInfoBroker.equals(zoneKey);
1159 mBrokerFactory = brokerFactory;
1178 if (isFilled(year))
return true;
1179 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1180 logging::printf(
"initForYear(): %d\n", year);
1185 mTransitionStorage.
init();
1187 if (year < mZoneInfoBroker.zoneContext()->startYear - 1
1188 || mZoneInfoBroker.zoneContext()->untilYear < year) {
1189 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1191 "initForYear(): Year %d out of valid range [%d, %d)\n",
1193 mZoneInfoBroker.zoneContext()->startYear,
1194 mZoneInfoBroker.zoneContext()->untilYear);
1207 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1208 logging::printf(
"==== Step 1: findMatches()\n");
1210 mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
1212 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1215 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1216 logging::printf(
"==== Step 2: createTransitions()\n");
1218 createTransitions(mTransitionStorage, mMatches, mNumMatches);
1219 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1222 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1223 logging::printf(
"==== Step 3: fixTransitionTimes()\n");
1225 Transition** begin = mTransitionStorage.getActivePoolBegin();
1226 Transition** end = mTransitionStorage.getActivePoolEnd();
1227 fixTransitionTimes(begin, end);
1228 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1231 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1232 logging::printf(
"==== Step 4: generateStartUntilTimes()\n");
1234 generateStartUntilTimes(begin, end);
1235 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1238 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1239 logging::printf(
"==== Step 5: calcAbbreviations()\n");
1241 calcAbbreviations(begin, end);
1242 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1257 const BF* brokerFactory,
1261 mBrokerFactory(brokerFactory)
1267 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
1268 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
1269 friend class ::ExtendedZoneProcessorTest_createMatch;
1270 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
1271 friend class ::ExtendedZoneProcessorTest_findMatches_named;
1272 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
1273 friend class ::ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
1274 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
1275 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
1276 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
1277 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
1278 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
1279 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
1280 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
1281 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
1282 friend class ::ExtendedZoneProcessorTest_processTransitionMatchStatus;
1283 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
1284 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
1285 friend class ::ExtendedZoneProcessorTest_setZoneKey;
1286 friend class ::TransitionStorageTest_findTransitionForDateTime;
1287 friend class ::TransitionValidation;
1299 static const uint8_t kMaxMatches = 4;
1305 static const uint8_t kMaxInteriorYears = 4;
1308 return mZoneInfoBroker.equals(
1313 bool isFilled(int16_t year)
const {
1314 return mIsFilled && (year == mYear);
1324 static uint8_t findMatches(
1325 const ZIB& zoneInfo,
1326 const extended::YearMonthTuple& startYm,
1327 const extended::YearMonthTuple& untilYm,
1331 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1332 logging::printf(
"findMatches()\n");
1336 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
1337 const ZEB era = zoneInfo.era(iEra);
1338 if (eraOverlapsInterval(prevMatch, era, startYm, untilYm)) {
1339 if (iMatch < maxMatches) {
1340 matches[iMatch] = createMatch(prevMatch, era, startYm, untilYm);
1341 prevMatch = &matches[iMatch];
1360 static bool eraOverlapsInterval(
1363 const extended::YearMonthTuple& startYm,
1364 const extended::YearMonthTuple& untilYm) {
1365 return (prevMatch ==
nullptr || compareEraToYearMonth(
1366 prevMatch->era, untilYm.yearTiny, untilYm.month) < 0)
1367 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1371 static int8_t compareEraToYearMonth(
const ZEB& era,
1372 int8_t yearTiny, uint8_t month) {
1373 if (era.untilYearTiny() < yearTiny)
return -1;
1374 if (era.untilYearTiny() > yearTiny)
return 1;
1375 if (era.untilMonth() < month)
return -1;
1376 if (era.untilMonth() > month)
return 1;
1377 if (era.untilDay() > 1)
return 1;
1379 if (era.untilTimeMinutes() > 0)
return 1;
1392 const extended::YearMonthTuple& startYm,
1393 const extended::YearMonthTuple& untilYm) {
1397 extended::DateTuple startDate = (prevMatch ==
nullptr)
1398 ? extended::DateTuple{
1405 : extended::DateTuple{
1406 prevMatch->era.untilYearTiny(),
1407 prevMatch->era.untilMonth(),
1408 prevMatch->era.untilDay(),
1409 (int16_t) prevMatch->era.untilTimeMinutes(),
1410 prevMatch->era.untilTimeSuffix()
1412 extended::DateTuple lowerBound{
1419 if (startDate < lowerBound) {
1420 startDate = lowerBound;
1423 extended::DateTuple untilDate{
1424 era.untilYearTiny(),
1427 (int16_t) era.untilTimeMinutes(),
1428 era.untilTimeSuffix()
1430 extended::DateTuple upperBound{
1437 if (upperBound < untilDate) {
1438 untilDate = upperBound;
1441 return {startDate, untilDate, era, prevMatch, 0, 0};
1448 static void createTransitions(
1451 uint8_t numMatches) {
1452 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1453 logging::printf(
"createTransitions()\n");
1456 for (uint8_t i = 0; i < numMatches; i++) {
1457 createTransitionsForMatch(transitionStorage, &matches[i]);
1462 static void createTransitionsForMatch(
1465 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1466 logging::printf(
"== createTransitionsForMatch()\n");
1468 const ZPB policy = match->era.zonePolicy();
1469 if (policy.isNull()) {
1470 createTransitionsFromSimpleMatch(transitionStorage, match);
1472 createTransitionsFromNamedMatch(transitionStorage, match);
1476 static void createTransitionsFromSimpleMatch(
1479 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1480 logging::printf(
"== createTransitionsFromSimpleMatch()\n");
1483 Transition* freeTransition = transitionStorage.getFreeAgent();
1484 createTransitionForYear(freeTransition, 0 ,
1486 freeTransition->matchStatus = extended::MatchStatus::kExactMatch;
1487 match->lastOffsetMinutes = freeTransition->offsetMinutes;
1488 match->lastDeltaMinutes = freeTransition->deltaMinutes;
1489 transitionStorage.addFreeAgentToActivePool();
1490 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1491 freeTransition->log();
1492 logging::printf(
"\n");
1496 static void createTransitionsFromNamedMatch(
1499 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1500 logging::printf(
"== createTransitionsFromNamedMatch()\n");
1503 transitionStorage.resetCandidatePool();
1504 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1505 match->log(); logging::printf(
"\n");
1509 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1510 logging::printf(
"---- Pass 1: findCandidateTransitions()\n");
1512 findCandidateTransitions(transitionStorage, match);
1513 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1514 transitionStorage.log();
1519 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1520 logging::printf(
"---- Pass 2: fixTransitionTimes()\n");
1523 transitionStorage.getCandidatePoolBegin(),
1524 transitionStorage.getCandidatePoolEnd());
1528 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1529 logging::printf(
"---- Pass 3: selectActiveTransitions()\n");
1531 selectActiveTransitions(
1532 transitionStorage.getCandidatePoolBegin(),
1533 transitionStorage.getCandidatePoolEnd());
1534 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1535 transitionStorage.log();
1538 transitionStorage.addActiveCandidatesToActivePool();
1539 match->lastOffsetMinutes = lastTransition->offsetMinutes;
1540 match->lastDeltaMinutes = lastTransition->deltaMinutes;
1541 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1542 transitionStorage.log();
1546 static void findCandidateTransitions(
1549 using extended::MatchStatus;
1551 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1552 logging::printf(
"findCandidateTransitions(): \n");
1554 logging::printf(
"\n");
1556 const ZPB policy = match->era.zonePolicy();
1557 uint8_t numRules = policy.numRules();
1558 int8_t startY = match->startDateTime.yearTiny;
1559 int8_t endY = match->untilDateTime.yearTiny;
1564 Transition** prior = transitionStorage.reservePrior();
1565 (*prior)->isValidPrior =
false;
1566 for (uint8_t r = 0; r < numRules; r++) {
1567 const ZRB rule = policy.rule(r);
1570 int8_t interiorYears[kMaxInteriorYears];
1571 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1572 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1573 for (uint8_t y = 0; y < numYears; y++) {
1574 int8_t year = interiorYears[y];
1575 Transition* t = transitionStorage.getFreeAgent();
1576 createTransitionForYear(t, year, rule, match);
1577 MatchStatus status = compareTransitionToMatchFuzzy(t, match);
1578 if (status == MatchStatus::kPrior) {
1579 transitionStorage.setFreeAgentAsPriorIfValid();
1580 }
else if (status == MatchStatus::kWithinMatch) {
1581 transitionStorage.addFreeAgentToCandidatePool();
1589 int8_t priorYear = getMostRecentPriorYear(
1590 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1592 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1594 "findCandidateTransitions(): priorYear: %d\n",
1597 Transition* t = transitionStorage.getFreeAgent();
1598 createTransitionForYear(t, priorYear, rule, match);
1599 transitionStorage.setFreeAgentAsPriorIfValid();
1605 if ((*prior)->isValidPrior) {
1606 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1608 "findCandidateTransitions(): adding prior to Candidate pool\n");
1610 logging::printf(
"\n");
1612 transitionStorage.addPriorToCandidatePool();
1635 static uint8_t calcInteriorYears(
1636 int8_t* interiorYears,
1637 uint8_t maxInteriorYears,
1638 int8_t fromYear, int8_t toYear,
1639 int8_t startYear, int8_t endYear) {
1641 for (int8_t year = startYear; year <= endYear; year++) {
1642 if (fromYear <= year && year <= toYear) {
1643 interiorYears[i] = year;
1645 if (i >= maxInteriorYears)
break;
1657 static void createTransitionForYear(
1664 t->offsetMinutes = match->era.offsetMinutes();
1665 t->letterBuf[0] =
'\0';
1667 if (! rule.isNull()) {
1668 t->transitionTime = getTransitionTime(year, rule);
1669 t->deltaMinutes = rule.deltaMinutes();
1671 char letter = rule.letter();
1674 if (letter !=
'-') {
1675 t->letterBuf[0] = letter;
1676 t->letterBuf[1] =
'\0';
1686 t->transitionTime = match->startDateTime;
1687 t->deltaMinutes = match->era.deltaMinutes();
1703 static int8_t getMostRecentPriorYear(
1704 int8_t fromYear, int8_t toYear,
1705 int8_t startYear, int8_t endYear) {
1709 if (fromYear < startYear) {
1710 if (toYear < startYear) {
1713 return startYear - 1;
1724 static extended::DateTuple getTransitionTime(
1725 int8_t yearTiny,
const ZRB& rule) {
1727 internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1731 rule.onDayOfMonth());
1736 (int16_t) rule.atTimeMinutes(),
1751 static extended::MatchStatus compareTransitionToMatchFuzzy(
1753 using extended::MatchStatus;
1755 int16_t ttMonths = t->transitionTime.yearTiny * 12
1756 + t->transitionTime.month;
1758 int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1759 + match->startDateTime.month;
1760 if (ttMonths < matchStartMonths - 1)
return MatchStatus::kPrior;
1762 int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1763 + match->untilDateTime.month;
1764 if (matchUntilMonths + 2 <= ttMonths)
return MatchStatus::kFarFuture;
1766 return MatchStatus::kWithinMatch;
1778 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1779 logging::printf(
"fixTransitionTimes(): START; #transitions=%d\n",
1780 (
int) (end - begin));
1781 printTransitions(begin, end);
1787 for (
Transition** iter = begin; iter != end; ++iter) {
1790 &curr->transitionTime,
1791 prev->offsetMinutes,
1793 &curr->transitionTime,
1794 &curr->transitionTimeS,
1795 &curr->transitionTimeU);
1798 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1799 logging::printf(
"fixTransitionTimes(): FIXED\n");
1800 printTransitions(begin, end);
1801 logging::printf(
"fixTransitionTimes(): END\n");
1805 #ifdef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1807 for (
Transition** iter = begin; iter != end; ++iter) {
1809 logging::printf(
"\n");
1819 static void expandDateTuple(
1820 const extended::DateTuple* tt,
1821 int16_t offsetMinutes,
1822 int16_t deltaMinutes,
1823 extended::DateTuple* ttw,
1824 extended::DateTuple* tts,
1825 extended::DateTuple* ttu) {
1829 *ttu = {tt->yearTiny, tt->month, tt->day,
1830 (int16_t) (tt->minutes - offsetMinutes),
1832 *ttw = {tt->yearTiny, tt->month, tt->day,
1833 (int16_t) (tt->minutes + deltaMinutes),
1837 *tts = {tt->yearTiny, tt->month, tt->day,
1838 (int16_t) (tt->minutes + offsetMinutes),
1840 *ttw = {tt->yearTiny, tt->month, tt->day,
1841 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1847 *tts = {tt->yearTiny, tt->month, tt->day,
1848 (int16_t) (tt->minutes - deltaMinutes),
1850 *ttu = {tt->yearTiny, tt->month, tt->day,
1851 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1855 extended::normalizeDateTuple(ttw);
1856 extended::normalizeDateTuple(tts);
1857 extended::normalizeDateTuple(ttu);
1865 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1866 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1867 (
int) (end - begin));
1871 for (
Transition** iter = begin; iter != end; ++iter) {
1873 processTransitionMatchStatus(transition, &prior);
1879 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1881 "selectActiveTransitions(): found latest prior\n");
1883 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1884 prior->originalTransitionTime = prior->transitionTime;
1886 prior->transitionTime = prior->match->startDateTime;
1896 static void processTransitionMatchStatus(
1899 using extended::MatchStatus;
1901 MatchStatus status = compareTransitionToMatch(
1902 transition, transition->match);
1903 transition->matchStatus = status;
1905 if (status == MatchStatus::kExactMatch) {
1907 (*prior)->matchStatus = MatchStatus::kFarPast;
1909 (*prior) = transition;
1910 }
else if (status == MatchStatus::kPrior) {
1912 if ((*prior)->transitionTimeU <= transition->transitionTimeU) {
1913 (*prior)->matchStatus = MatchStatus::kFarPast;
1914 (*prior) = transition;
1916 transition->matchStatus = MatchStatus::kFarPast;
1919 (*prior) = transition;
1932 static extended::MatchStatus compareTransitionToMatch(
1937 int16_t prevMatchOffsetMinutes;
1938 int16_t prevMatchDeltaMinutes;
1939 if (match->prevMatch) {
1940 prevMatchOffsetMinutes = match->prevMatch->lastOffsetMinutes;
1941 prevMatchDeltaMinutes = match->prevMatch->lastDeltaMinutes;
1943 prevMatchOffsetMinutes = match->era.offsetMinutes();
1944 prevMatchDeltaMinutes = 0;
1948 extended::DateTuple stw;
1949 extended::DateTuple sts;
1950 extended::DateTuple stu;
1952 &match->startDateTime,
1953 prevMatchOffsetMinutes,
1954 prevMatchDeltaMinutes,
1960 const extended::DateTuple& ttw = transition->transitionTime;
1961 const extended::DateTuple& tts = transition->transitionTimeS;
1962 const extended::DateTuple& ttu = transition->transitionTimeU;
1967 if (ttw == stw || tts == sts || ttu == stu) {
1968 return extended::MatchStatus::kExactMatch;
1972 return extended::MatchStatus::kPrior;
1980 const extended::DateTuple& matchUntil = match->untilDateTime;
1981 const extended::DateTuple* transitionTime;
1983 transitionTime = &tts;
1985 transitionTime = &ttu;
1987 transitionTime = &ttw;
1989 if (*transitionTime < matchUntil) {
1990 return extended::MatchStatus::kWithinMatch;
1992 return extended::MatchStatus::kFarFuture;
2001 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2003 "generateStartUntilTimes(): #transitions=%d\n",
2004 (
int) (end - begin));
2008 bool isAfterFirst =
false;
2010 for (
Transition** iter = begin; iter != end; ++iter) {
2014 const extended::DateTuple& tt = t->transitionTime;
2016 prev->untilDateTime = tt;
2022 int16_t minutes = tt.minutes + (
2023 - prev->offsetMinutes - prev->deltaMinutes
2024 + t->offsetMinutes + t->deltaMinutes);
2025 t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
2027 extended::normalizeDateTuple(&t->startDateTime);
2039 const extended::DateTuple& st = t->startDateTime;
2040 const acetime_t offsetSeconds = (acetime_t) 60
2041 * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
2043 st.yearTiny, st.month, st.day);
2044 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
2047 isAfterFirst =
true;
2051 extended::DateTuple untilTimeW;
2052 extended::DateTuple untilTimeS;
2053 extended::DateTuple untilTimeU;
2055 &prev->match->untilDateTime,
2056 prev->offsetMinutes,
2061 prev->untilDateTime = untilTimeW;
2068 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2069 logging::printf(
"calcAbbreviations(): #transitions: %d\n",
2070 (
int) (end - begin));
2072 for (
Transition** iter = begin; iter != end; ++iter) {
2074 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2076 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
2077 t->format(), t->deltaMinutes, t->letter());
2081 internal::kAbbrevSize,
2096 static void createAbbreviation(
2100 uint16_t deltaMinutes,
2101 const char* letterString) {
2104 if (strchr(format,
'%') !=
nullptr) {
2106 if (letterString ==
nullptr) {
2107 strncpy(dest, format, destSize - 1);
2108 dest[destSize - 1] =
'\0';
2110 ace_common::copyReplaceString(
2111 dest, destSize, format,
'%', letterString);
2115 const char* slashPos = strchr(format,
'/');
2116 if (slashPos !=
nullptr) {
2117 if (deltaMinutes == 0) {
2118 uint8_t headLength = (slashPos - format);
2119 if (headLength >= destSize) headLength = destSize - 1;
2120 memcpy(dest, format, headLength);
2121 dest[headLength] =
'\0';
2123 uint8_t tailLength = strlen(slashPos+1);
2124 if (tailLength >= destSize) tailLength = destSize - 1;
2125 memcpy(dest, slashPos+1, tailLength);
2126 dest[tailLength] =
'\0';
2130 strncpy(dest, format, destSize);
2131 dest[destSize - 1] =
'\0';
2136 const BF* mBrokerFactory;
2137 ZIB mZoneInfoBroker;
2139 mutable int16_t mYear = 0;
2140 mutable bool mIsFilled =
false;
2142 mutable uint8_t mNumMatches = 0;
2153 extended::BrokerFactory,
2154 extended::ZoneInfoBroker,
2155 extended::ZoneEraBroker,
2156 extended::ZonePolicyBroker,
2157 extended::ZoneRuleBroker> {
2165 extended::BrokerFactory,
2166 extended::ZoneInfoBroker,
2167 extended::ZoneEraBroker,
2168 extended::ZonePolicyBroker,
2169 extended::ZoneRuleBroker>(