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 (! mBrokerFactory)
return;
1140 if (mZoneInfoBroker.equals(zoneKey))
return;
1142 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
1150 return mZoneInfoBroker.equals(zoneKey);
1160 mBrokerFactory = brokerFactory;
1179 if (isFilled(year))
return true;
1180 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1181 logging::printf(
"initForYear(): %d\n", year);
1186 mTransitionStorage.
init();
1188 if (year < mZoneInfoBroker.zoneContext()->startYear - 1
1189 || mZoneInfoBroker.zoneContext()->untilYear < year) {
1190 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1192 "initForYear(): Year %d out of valid range [%d, %d)\n",
1194 mZoneInfoBroker.zoneContext()->startYear,
1195 mZoneInfoBroker.zoneContext()->untilYear);
1208 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1209 logging::printf(
"==== Step 1: findMatches()\n");
1211 mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
1213 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1216 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1217 logging::printf(
"==== Step 2: createTransitions()\n");
1219 createTransitions(mTransitionStorage, mMatches, mNumMatches);
1220 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1223 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1224 logging::printf(
"==== Step 3: fixTransitionTimes()\n");
1226 Transition** begin = mTransitionStorage.getActivePoolBegin();
1227 Transition** end = mTransitionStorage.getActivePoolEnd();
1228 fixTransitionTimes(begin, end);
1229 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1232 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1233 logging::printf(
"==== Step 4: generateStartUntilTimes()\n");
1235 generateStartUntilTimes(begin, end);
1236 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1239 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1240 logging::printf(
"==== Step 5: calcAbbreviations()\n");
1242 calcAbbreviations(begin, end);
1243 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1261 const BF* brokerFactory ,
1265 mBrokerFactory(brokerFactory)
1271 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
1272 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
1273 friend class ::ExtendedZoneProcessorTest_createMatch;
1274 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
1275 friend class ::ExtendedZoneProcessorTest_findMatches_named;
1276 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
1277 friend class ::ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
1278 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
1279 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
1280 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
1281 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
1282 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
1283 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
1284 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
1285 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
1286 friend class ::ExtendedZoneProcessorTest_processTransitionMatchStatus;
1287 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
1288 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
1289 friend class ::ExtendedZoneProcessorTest_setZoneKey;
1290 friend class ::TransitionStorageTest_findTransitionForDateTime;
1291 friend class ::TransitionValidation;
1303 static const uint8_t kMaxMatches = 4;
1309 static const uint8_t kMaxInteriorYears = 4;
1312 return mZoneInfoBroker.equals(
1317 bool isFilled(int16_t year)
const {
1318 return mIsFilled && (year == mYear);
1328 static uint8_t findMatches(
1329 const ZIB& zoneInfo,
1330 const extended::YearMonthTuple& startYm,
1331 const extended::YearMonthTuple& untilYm,
1335 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1336 logging::printf(
"findMatches()\n");
1340 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
1341 const ZEB era = zoneInfo.era(iEra);
1342 if (eraOverlapsInterval(prevMatch, era, startYm, untilYm)) {
1343 if (iMatch < maxMatches) {
1344 matches[iMatch] = createMatch(prevMatch, era, startYm, untilYm);
1345 prevMatch = &matches[iMatch];
1364 static bool eraOverlapsInterval(
1367 const extended::YearMonthTuple& startYm,
1368 const extended::YearMonthTuple& untilYm) {
1369 return (prevMatch ==
nullptr || compareEraToYearMonth(
1370 prevMatch->era, untilYm.yearTiny, untilYm.month) < 0)
1371 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1375 static int8_t compareEraToYearMonth(
const ZEB& era,
1376 int8_t yearTiny, uint8_t month) {
1377 if (era.untilYearTiny() < yearTiny)
return -1;
1378 if (era.untilYearTiny() > yearTiny)
return 1;
1379 if (era.untilMonth() < month)
return -1;
1380 if (era.untilMonth() > month)
return 1;
1381 if (era.untilDay() > 1)
return 1;
1383 if (era.untilTimeMinutes() > 0)
return 1;
1396 const extended::YearMonthTuple& startYm,
1397 const extended::YearMonthTuple& untilYm) {
1401 extended::DateTuple startDate = (prevMatch ==
nullptr)
1402 ? extended::DateTuple{
1409 : extended::DateTuple{
1410 prevMatch->era.untilYearTiny(),
1411 prevMatch->era.untilMonth(),
1412 prevMatch->era.untilDay(),
1413 (int16_t) prevMatch->era.untilTimeMinutes(),
1414 prevMatch->era.untilTimeSuffix()
1416 extended::DateTuple lowerBound{
1423 if (startDate < lowerBound) {
1424 startDate = lowerBound;
1427 extended::DateTuple untilDate{
1428 era.untilYearTiny(),
1431 (int16_t) era.untilTimeMinutes(),
1432 era.untilTimeSuffix()
1434 extended::DateTuple upperBound{
1441 if (upperBound < untilDate) {
1442 untilDate = upperBound;
1445 return {startDate, untilDate, era, prevMatch, 0, 0};
1452 static void createTransitions(
1455 uint8_t numMatches) {
1456 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1457 logging::printf(
"createTransitions()\n");
1460 for (uint8_t i = 0; i < numMatches; i++) {
1461 createTransitionsForMatch(transitionStorage, &matches[i]);
1466 static void createTransitionsForMatch(
1469 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1470 logging::printf(
"== createTransitionsForMatch()\n");
1472 const ZPB policy = match->era.zonePolicy();
1473 if (policy.isNull()) {
1474 createTransitionsFromSimpleMatch(transitionStorage, match);
1476 createTransitionsFromNamedMatch(transitionStorage, match);
1480 static void createTransitionsFromSimpleMatch(
1483 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1484 logging::printf(
"== createTransitionsFromSimpleMatch()\n");
1487 Transition* freeTransition = transitionStorage.getFreeAgent();
1488 createTransitionForYear(freeTransition, 0 ,
1490 freeTransition->matchStatus = extended::MatchStatus::kExactMatch;
1491 match->lastOffsetMinutes = freeTransition->offsetMinutes;
1492 match->lastDeltaMinutes = freeTransition->deltaMinutes;
1493 transitionStorage.addFreeAgentToActivePool();
1494 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1495 freeTransition->log();
1496 logging::printf(
"\n");
1500 static void createTransitionsFromNamedMatch(
1503 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1504 logging::printf(
"== createTransitionsFromNamedMatch()\n");
1507 transitionStorage.resetCandidatePool();
1508 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1509 match->log(); logging::printf(
"\n");
1513 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1514 logging::printf(
"---- Pass 1: findCandidateTransitions()\n");
1516 findCandidateTransitions(transitionStorage, match);
1517 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1518 transitionStorage.log();
1523 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1524 logging::printf(
"---- Pass 2: fixTransitionTimes()\n");
1527 transitionStorage.getCandidatePoolBegin(),
1528 transitionStorage.getCandidatePoolEnd());
1532 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1533 logging::printf(
"---- Pass 3: selectActiveTransitions()\n");
1535 selectActiveTransitions(
1536 transitionStorage.getCandidatePoolBegin(),
1537 transitionStorage.getCandidatePoolEnd());
1538 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1539 transitionStorage.log();
1542 transitionStorage.addActiveCandidatesToActivePool();
1543 match->lastOffsetMinutes = lastTransition->offsetMinutes;
1544 match->lastDeltaMinutes = lastTransition->deltaMinutes;
1545 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1546 transitionStorage.log();
1550 static void findCandidateTransitions(
1553 using extended::MatchStatus;
1555 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1556 logging::printf(
"findCandidateTransitions(): \n");
1558 logging::printf(
"\n");
1560 const ZPB policy = match->era.zonePolicy();
1561 uint8_t numRules = policy.numRules();
1562 int8_t startY = match->startDateTime.yearTiny;
1563 int8_t endY = match->untilDateTime.yearTiny;
1568 Transition** prior = transitionStorage.reservePrior();
1569 (*prior)->isValidPrior =
false;
1570 for (uint8_t r = 0; r < numRules; r++) {
1571 const ZRB rule = policy.rule(r);
1574 int8_t interiorYears[kMaxInteriorYears];
1575 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1576 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1577 for (uint8_t y = 0; y < numYears; y++) {
1578 int8_t year = interiorYears[y];
1579 Transition* t = transitionStorage.getFreeAgent();
1580 createTransitionForYear(t, year, rule, match);
1581 MatchStatus status = compareTransitionToMatchFuzzy(t, match);
1582 if (status == MatchStatus::kPrior) {
1583 transitionStorage.setFreeAgentAsPriorIfValid();
1584 }
else if (status == MatchStatus::kWithinMatch) {
1585 transitionStorage.addFreeAgentToCandidatePool();
1593 int8_t priorYear = getMostRecentPriorYear(
1594 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1596 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1598 "findCandidateTransitions(): priorYear: %d\n",
1601 Transition* t = transitionStorage.getFreeAgent();
1602 createTransitionForYear(t, priorYear, rule, match);
1603 transitionStorage.setFreeAgentAsPriorIfValid();
1609 if ((*prior)->isValidPrior) {
1610 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1612 "findCandidateTransitions(): adding prior to Candidate pool\n");
1614 logging::printf(
"\n");
1616 transitionStorage.addPriorToCandidatePool();
1639 static uint8_t calcInteriorYears(
1640 int8_t* interiorYears,
1641 uint8_t maxInteriorYears,
1642 int8_t fromYear, int8_t toYear,
1643 int8_t startYear, int8_t endYear) {
1645 for (int8_t year = startYear; year <= endYear; year++) {
1646 if (fromYear <= year && year <= toYear) {
1647 interiorYears[i] = year;
1649 if (i >= maxInteriorYears)
break;
1661 static void createTransitionForYear(
1668 t->offsetMinutes = match->era.offsetMinutes();
1669 t->letterBuf[0] =
'\0';
1671 if (! rule.isNull()) {
1672 t->transitionTime = getTransitionTime(year, rule);
1673 t->deltaMinutes = rule.deltaMinutes();
1675 char letter = rule.letter();
1678 if (letter !=
'-') {
1679 t->letterBuf[0] = letter;
1680 t->letterBuf[1] =
'\0';
1690 t->transitionTime = match->startDateTime;
1691 t->deltaMinutes = match->era.deltaMinutes();
1707 static int8_t getMostRecentPriorYear(
1708 int8_t fromYear, int8_t toYear,
1709 int8_t startYear, int8_t endYear) {
1713 if (fromYear < startYear) {
1714 if (toYear < startYear) {
1717 return startYear - 1;
1728 static extended::DateTuple getTransitionTime(
1729 int8_t yearTiny,
const ZRB& rule) {
1731 internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1735 rule.onDayOfMonth());
1740 (int16_t) rule.atTimeMinutes(),
1755 static extended::MatchStatus compareTransitionToMatchFuzzy(
1757 using extended::MatchStatus;
1759 int16_t ttMonths = t->transitionTime.yearTiny * 12
1760 + t->transitionTime.month;
1762 int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1763 + match->startDateTime.month;
1764 if (ttMonths < matchStartMonths - 1)
return MatchStatus::kPrior;
1766 int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1767 + match->untilDateTime.month;
1768 if (matchUntilMonths + 2 <= ttMonths)
return MatchStatus::kFarFuture;
1770 return MatchStatus::kWithinMatch;
1782 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1783 logging::printf(
"fixTransitionTimes(): START; #transitions=%d\n",
1784 (
int) (end - begin));
1785 printTransitions(begin, end);
1791 for (
Transition** iter = begin; iter != end; ++iter) {
1794 &curr->transitionTime,
1795 prev->offsetMinutes,
1797 &curr->transitionTime,
1798 &curr->transitionTimeS,
1799 &curr->transitionTimeU);
1802 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1803 logging::printf(
"fixTransitionTimes(): FIXED\n");
1804 printTransitions(begin, end);
1805 logging::printf(
"fixTransitionTimes(): END\n");
1809 #ifdef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1811 for (
Transition** iter = begin; iter != end; ++iter) {
1813 logging::printf(
"\n");
1823 static void expandDateTuple(
1824 const extended::DateTuple* tt,
1825 int16_t offsetMinutes,
1826 int16_t deltaMinutes,
1827 extended::DateTuple* ttw,
1828 extended::DateTuple* tts,
1829 extended::DateTuple* ttu) {
1833 *ttu = {tt->yearTiny, tt->month, tt->day,
1834 (int16_t) (tt->minutes - offsetMinutes),
1836 *ttw = {tt->yearTiny, tt->month, tt->day,
1837 (int16_t) (tt->minutes + deltaMinutes),
1841 *tts = {tt->yearTiny, tt->month, tt->day,
1842 (int16_t) (tt->minutes + offsetMinutes),
1844 *ttw = {tt->yearTiny, tt->month, tt->day,
1845 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1851 *tts = {tt->yearTiny, tt->month, tt->day,
1852 (int16_t) (tt->minutes - deltaMinutes),
1854 *ttu = {tt->yearTiny, tt->month, tt->day,
1855 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1859 extended::normalizeDateTuple(ttw);
1860 extended::normalizeDateTuple(tts);
1861 extended::normalizeDateTuple(ttu);
1869 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1870 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1871 (
int) (end - begin));
1875 for (
Transition** iter = begin; iter != end; ++iter) {
1877 processTransitionMatchStatus(transition, &prior);
1883 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1885 "selectActiveTransitions(): found latest prior\n");
1887 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1888 prior->originalTransitionTime = prior->transitionTime;
1890 prior->transitionTime = prior->match->startDateTime;
1900 static void processTransitionMatchStatus(
1903 using extended::MatchStatus;
1905 MatchStatus status = compareTransitionToMatch(
1906 transition, transition->match);
1907 transition->matchStatus = status;
1909 if (status == MatchStatus::kExactMatch) {
1911 (*prior)->matchStatus = MatchStatus::kFarPast;
1913 (*prior) = transition;
1914 }
else if (status == MatchStatus::kPrior) {
1916 if ((*prior)->transitionTimeU <= transition->transitionTimeU) {
1917 (*prior)->matchStatus = MatchStatus::kFarPast;
1918 (*prior) = transition;
1920 transition->matchStatus = MatchStatus::kFarPast;
1923 (*prior) = transition;
1936 static extended::MatchStatus compareTransitionToMatch(
1941 int16_t prevMatchOffsetMinutes;
1942 int16_t prevMatchDeltaMinutes;
1943 if (match->prevMatch) {
1944 prevMatchOffsetMinutes = match->prevMatch->lastOffsetMinutes;
1945 prevMatchDeltaMinutes = match->prevMatch->lastDeltaMinutes;
1947 prevMatchOffsetMinutes = match->era.offsetMinutes();
1948 prevMatchDeltaMinutes = 0;
1952 extended::DateTuple stw;
1953 extended::DateTuple sts;
1954 extended::DateTuple stu;
1956 &match->startDateTime,
1957 prevMatchOffsetMinutes,
1958 prevMatchDeltaMinutes,
1964 const extended::DateTuple& ttw = transition->transitionTime;
1965 const extended::DateTuple& tts = transition->transitionTimeS;
1966 const extended::DateTuple& ttu = transition->transitionTimeU;
1971 if (ttw == stw || tts == sts || ttu == stu) {
1972 return extended::MatchStatus::kExactMatch;
1976 return extended::MatchStatus::kPrior;
1984 const extended::DateTuple& matchUntil = match->untilDateTime;
1985 const extended::DateTuple* transitionTime;
1987 transitionTime = &tts;
1989 transitionTime = &ttu;
1991 transitionTime = &ttw;
1993 if (*transitionTime < matchUntil) {
1994 return extended::MatchStatus::kWithinMatch;
1996 return extended::MatchStatus::kFarFuture;
2005 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2007 "generateStartUntilTimes(): #transitions=%d\n",
2008 (
int) (end - begin));
2012 bool isAfterFirst =
false;
2014 for (
Transition** iter = begin; iter != end; ++iter) {
2018 const extended::DateTuple& tt = t->transitionTime;
2020 prev->untilDateTime = tt;
2026 int16_t minutes = tt.minutes + (
2027 - prev->offsetMinutes - prev->deltaMinutes
2028 + t->offsetMinutes + t->deltaMinutes);
2029 t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
2031 extended::normalizeDateTuple(&t->startDateTime);
2043 const extended::DateTuple& st = t->startDateTime;
2044 const acetime_t offsetSeconds = (acetime_t) 60
2045 * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
2047 st.yearTiny, st.month, st.day);
2048 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
2051 isAfterFirst =
true;
2055 extended::DateTuple untilTimeW;
2056 extended::DateTuple untilTimeS;
2057 extended::DateTuple untilTimeU;
2059 &prev->match->untilDateTime,
2060 prev->offsetMinutes,
2065 prev->untilDateTime = untilTimeW;
2072 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2073 logging::printf(
"calcAbbreviations(): #transitions: %d\n",
2074 (
int) (end - begin));
2076 for (
Transition** iter = begin; iter != end; ++iter) {
2078 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
2080 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
2081 t->format(), t->deltaMinutes, t->letter());
2085 internal::kAbbrevSize,
2100 static void createAbbreviation(
2104 uint16_t deltaMinutes,
2105 const char* letterString) {
2108 if (strchr(format,
'%') !=
nullptr) {
2110 if (letterString ==
nullptr) {
2111 strncpy(dest, format, destSize - 1);
2112 dest[destSize - 1] =
'\0';
2114 ace_common::copyReplaceString(
2115 dest, destSize, format,
'%', letterString);
2119 const char* slashPos = strchr(format,
'/');
2120 if (slashPos !=
nullptr) {
2121 if (deltaMinutes == 0) {
2122 uint8_t headLength = (slashPos - format);
2123 if (headLength >= destSize) headLength = destSize - 1;
2124 memcpy(dest, format, headLength);
2125 dest[headLength] =
'\0';
2127 uint8_t tailLength = strlen(slashPos+1);
2128 if (tailLength >= destSize) tailLength = destSize - 1;
2129 memcpy(dest, slashPos+1, tailLength);
2130 dest[tailLength] =
'\0';
2134 strncpy(dest, format, destSize);
2135 dest[destSize - 1] =
'\0';
2140 const BF* mBrokerFactory;
2141 ZIB mZoneInfoBroker;
2143 mutable int16_t mYear = 0;
2144 mutable bool mIsFilled =
false;
2146 mutable uint8_t mNumMatches = 0;
2157 extended::BrokerFactory,
2158 extended::ZoneInfoBroker,
2159 extended::ZoneEraBroker,
2160 extended::ZonePolicyBroker,
2161 extended::ZoneRuleBroker> {
2169 extended::BrokerFactory,
2170 extended::ZoneInfoBroker,
2171 extended::ZoneEraBroker,
2172 extended::ZonePolicyBroker,
2173 extended::ZoneRuleBroker>(