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>
449 for (uint8_t i = 0; i < SIZE; i++) {
450 mTransitions[i] = &mPool[i];
453 mIndexCandidates = 0;
459 return mTransitions[mIndexPrior];
471 mIndexCandidates = mIndexPrior;
472 mIndexFree = mIndexPrior;
476 return &mTransitions[mIndexCandidates];
479 return &mTransitions[mIndexFree];
483 return &mTransitions[0];
486 return &mTransitions[mIndexFree];
495 if (mIndexFree < SIZE) {
497 if (mIndexFree >= mAllocSize) {
498 mAllocSize = mIndexFree + 1;
500 return mTransitions[mIndexFree];
506 return mTransitions[SIZE - 1];
518 if (mIndexFree >= SIZE)
return;
520 mIndexPrior = mIndexFree;
521 mIndexCandidates = mIndexFree;
537 return &mTransitions[mIndexPrior];
543 Transition* prior = mTransitions[mIndexPrior];
548 swap(mTransitions[mIndexPrior], mTransitions[mIndexFree]);
568 if (mIndexFree >= SIZE)
return;
577 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
581 mTransitions[i] = prev;
582 mTransitions[i - 1] = curr;
594 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
595 logging::printf(
"addActiveCandidatesToActivePool()\n");
599 uint8_t iActive = mIndexPrior;
600 uint8_t iCandidate = mIndexCandidates;
601 for (; iCandidate < mIndexFree; iCandidate++) {
602 if (isMatchStatusActive(mTransitions[iCandidate]->matchStatus)) {
603 if (iActive != iCandidate) {
606 swap(mTransitions[iActive], mTransitions[iCandidate]);
612 mIndexPrior = iActive;
613 mIndexCandidates = iActive;
614 mIndexFree = iActive;
616 return mTransitions[iActive - 1];
628 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
629 logging::printf(
"findTransition(): mIndexFree: %d\n", mIndexFree);
633 for (uint8_t i = 0; i < mIndexFree; i++) {
634 const Transition* candidate = mTransitions[i];
666 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
668 "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
682 for (uint8_t i = 0; i < mIndexFree; i++) {
683 const Transition* candidate = mTransitions[i];
692 logging::printf(
"TransitionStorage: ");
693 logging::printf(
"nActives=%d", mIndexPrior);
694 logging::printf(
", nPrior=%d", mIndexCandidates - mIndexPrior);
695 logging::printf(
", nCandidates=%d", mIndexFree - mIndexCandidates);
696 logging::printf(
", nFree=%d", SIZE - mIndexFree);
697 logging::printf(
"\n");
699 if (mIndexPrior != 0) {
700 logging::printf(
" Actives:\n");
701 for (uint8_t i = 0; i < mIndexPrior; i++) {
702 logging::printf(
" ");
703 mTransitions[i]->
log();
704 logging::printf(
"\n");
707 if (mIndexPrior != mIndexCandidates) {
708 logging::printf(
" Prior: \n");
709 logging::printf(
" ");
710 mTransitions[mIndexPrior]->
log();
711 logging::printf(
"\n");
713 if (mIndexCandidates != mIndexFree) {
714 logging::printf(
" Candidates:\n");
715 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
716 logging::printf(
" ");
717 mTransitions[i]->
log();
718 logging::printf(
"\n");
734 friend class ::TransitionStorageTest_getFreeAgent;
735 friend class ::TransitionStorageTest_getFreeAgent2;
736 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
737 friend class ::TransitionStorageTest_reservePrior;
738 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
739 friend class ::TransitionStorageTest_setFreeAgentAsPriorIfValid;
740 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
741 friend class ::TransitionStorageTest_findTransitionForDateTime;
742 friend class ::TransitionStorageTest_resetCandidatePool;
746 return mTransitions[i];
752 uint8_t mIndexCandidates;
756 uint8_t mAllocSize = 0;
792 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
817 uint32_t
getZoneId()
const override {
return mZoneInfoBroker.zoneId(); }
822 const Transition* transition = findTransition(epochSeconds);
832 const Transition* transition = findTransition(epochSeconds);
836 const char*
getAbbrev(acetime_t epochSeconds)
const override {
838 if (!success)
return "";
839 const Transition* transition = findTransition(epochSeconds);
840 return transition->
abbrev;
845 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
846 logging::printf(
"getOffsetDateTime(): ldt=");
847 ldt.
printTo(SERIAL_PORT_MONITOR);
848 SERIAL_PORT_MONITOR.println();
856 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
857 logging::printf(
"getOffsetDateTime(): match transition=");
859 logging::printf(
"\n");
861 offset = (transition)
873 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
874 logging::printf(
"getOffsetDateTime(): odt=");
875 odt.printTo(SERIAL_PORT_MONITOR);
876 SERIAL_PORT_MONITOR.println();
885 acetime_t epochSeconds = odt.toEpochSeconds();
888 offset = (transition)
893 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
894 logging::printf(
"getOffsetDateTime(): normalized(odt)=");
895 odt.printTo(SERIAL_PORT_MONITOR);
896 SERIAL_PORT_MONITOR.println();
902 mZoneInfoBroker.printNameTo(printer);
906 mZoneInfoBroker.printShortNameTo(printer);
911 logging::printf(
"ExtendedZoneProcessor\n");
912 logging::printf(
" mYear: %d\n", mYear);
913 logging::printf(
" mNumMatches: %d\n", mNumMatches);
914 for (
int i = 0; i < mNumMatches; i++) {
915 logging::printf(
" Match %d: ", i);
917 logging::printf(
"\n");
919 mTransitionStorage.
log();
933 if (mZoneInfoBroker.equals(zoneKey))
return;
935 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
943 return mZoneInfoBroker.equals(zoneKey);
953 mBrokerFactory = brokerFactory;
972 if (isFilled(year))
return true;
973 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
974 logging::printf(
"initForYear(): %d\n", year);
979 mTransitionStorage.
init();
981 if (year < mZoneInfoBroker.zoneContext()->startYear - 1
982 || mZoneInfoBroker.zoneContext()->untilYear < year) {
983 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
984 logging::printf(
"init(): Year %d out of valid range [%d, %d)\n",
986 mZoneInfoBroker.zoneContext()->startYear,
987 mZoneInfoBroker.zoneContext()->untilYear);
1000 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1001 logging::printf(
"==== Step 1: findMatches()\n");
1003 mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
1005 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1008 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1009 logging::printf(
"==== Step 2: createTransitions()\n");
1011 createTransitions(mTransitionStorage, mMatches, mNumMatches);
1012 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1015 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1016 logging::printf(
"==== Step 3: fixTransitionTimes()\n");
1018 Transition** begin = mTransitionStorage.getActivePoolBegin();
1019 Transition** end = mTransitionStorage.getActivePoolEnd();
1020 fixTransitionTimes(begin, end);
1021 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1024 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1025 logging::printf(
"==== Step 4: generateStartUntilTimes()\n");
1027 generateStartUntilTimes(begin, end);
1028 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1031 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1032 logging::printf(
"==== Step 5: calcAbbreviations()\n");
1034 calcAbbreviations(begin, end);
1035 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
1050 const BF* brokerFactory,
1054 mBrokerFactory(brokerFactory)
1060 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
1061 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
1062 friend class ::ExtendedZoneProcessorTest_createMatch;
1063 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
1064 friend class ::ExtendedZoneProcessorTest_findMatches_named;
1065 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
1066 friend class ::ExtendedZoneProcessorTest_createTransitionsFromNamedMatch;
1067 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
1068 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
1069 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
1070 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
1071 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
1072 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
1073 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
1074 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
1075 friend class ::ExtendedZoneProcessorTest_processTransitionMatchStatus;
1076 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
1077 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
1078 friend class ::ExtendedZoneProcessorTest_setZoneKey;
1079 friend class ::TransitionStorageTest_findTransitionForDateTime;
1080 friend class ::TransitionValidation;
1092 static const uint8_t kMaxMatches = 4;
1098 static const uint8_t kMaxInteriorYears = 4;
1101 return mZoneInfoBroker.equals(
1109 const Transition* findTransition(acetime_t epochSeconds)
const {
1114 bool isFilled(int16_t year)
const {
1115 return mIsFilled && (year == mYear);
1125 static uint8_t findMatches(
1126 const ZIB& zoneInfo,
1127 const extended::YearMonthTuple& startYm,
1128 const extended::YearMonthTuple& untilYm,
1132 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1133 logging::printf(
"findMatches()\n");
1137 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
1138 const ZEB era = zoneInfo.era(iEra);
1139 if (eraOverlapsInterval(prevMatch, era, startYm, untilYm)) {
1140 if (iMatch < maxMatches) {
1141 matches[iMatch] = createMatch(prevMatch, era, startYm, untilYm);
1142 prevMatch = &matches[iMatch];
1161 static bool eraOverlapsInterval(
1164 const extended::YearMonthTuple& startYm,
1165 const extended::YearMonthTuple& untilYm) {
1166 return (prevMatch ==
nullptr || compareEraToYearMonth(
1167 prevMatch->era, untilYm.yearTiny, untilYm.month) < 0)
1168 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1172 static int8_t compareEraToYearMonth(
const ZEB& era,
1173 int8_t yearTiny, uint8_t month) {
1174 if (era.untilYearTiny() < yearTiny)
return -1;
1175 if (era.untilYearTiny() > yearTiny)
return 1;
1176 if (era.untilMonth() < month)
return -1;
1177 if (era.untilMonth() > month)
return 1;
1178 if (era.untilDay() > 1)
return 1;
1180 if (era.untilTimeMinutes() > 0)
return 1;
1193 const extended::YearMonthTuple& startYm,
1194 const extended::YearMonthTuple& untilYm) {
1198 extended::DateTuple startDate = (prevMatch ==
nullptr)
1199 ? extended::DateTuple{
1206 : extended::DateTuple{
1207 prevMatch->era.untilYearTiny(),
1208 prevMatch->era.untilMonth(),
1209 prevMatch->era.untilDay(),
1210 (int16_t) prevMatch->era.untilTimeMinutes(),
1211 prevMatch->era.untilTimeSuffix()
1213 extended::DateTuple lowerBound{
1220 if (startDate < lowerBound) {
1221 startDate = lowerBound;
1224 extended::DateTuple untilDate{
1225 era.untilYearTiny(),
1228 (int16_t) era.untilTimeMinutes(),
1229 era.untilTimeSuffix()
1231 extended::DateTuple upperBound{
1238 if (upperBound < untilDate) {
1239 untilDate = upperBound;
1242 return {startDate, untilDate, era, prevMatch, 0, 0};
1249 static void createTransitions(
1252 uint8_t numMatches) {
1253 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1254 logging::printf(
"createTransitions()\n");
1257 for (uint8_t i = 0; i < numMatches; i++) {
1258 createTransitionsForMatch(transitionStorage, &matches[i]);
1263 static void createTransitionsForMatch(
1266 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1267 logging::printf(
"== createTransitionsForMatch()\n");
1269 const ZPB policy = match->era.zonePolicy();
1270 if (policy.isNull()) {
1271 createTransitionsFromSimpleMatch(transitionStorage, match);
1273 createTransitionsFromNamedMatch(transitionStorage, match);
1277 static void createTransitionsFromSimpleMatch(
1280 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1281 logging::printf(
"== createTransitionsFromSimpleMatch()\n");
1284 Transition* freeTransition = transitionStorage.getFreeAgent();
1285 createTransitionForYear(freeTransition, 0 ,
1287 freeTransition->matchStatus = extended::MatchStatus::kExactMatch;
1288 match->lastOffsetMinutes = freeTransition->offsetMinutes;
1289 match->lastDeltaMinutes = freeTransition->deltaMinutes;
1290 transitionStorage.addFreeAgentToActivePool();
1291 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1292 freeTransition->log();
1293 logging::printf(
"\n");
1297 static void createTransitionsFromNamedMatch(
1300 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1301 logging::printf(
"== createTransitionsFromNamedMatch()\n");
1304 transitionStorage.resetCandidatePool();
1305 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1306 match->log(); logging::printf(
"\n");
1310 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1311 logging::printf(
"---- Pass 1: findCandidateTransitions()\n");
1313 findCandidateTransitions(transitionStorage, match);
1314 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1315 transitionStorage.log();
1320 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1321 logging::printf(
"---- Pass 2: fixTransitionTimes()\n");
1324 transitionStorage.getCandidatePoolBegin(),
1325 transitionStorage.getCandidatePoolEnd());
1329 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1330 logging::printf(
"---- Pass 3: selectActiveTransitions()\n");
1332 selectActiveTransitions(
1333 transitionStorage.getCandidatePoolBegin(),
1334 transitionStorage.getCandidatePoolEnd());
1335 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1336 transitionStorage.log();
1339 transitionStorage.addActiveCandidatesToActivePool();
1340 match->lastOffsetMinutes = lastTransition->offsetMinutes;
1341 match->lastDeltaMinutes = lastTransition->deltaMinutes;
1342 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1343 transitionStorage.log();
1347 static void findCandidateTransitions(
1350 using extended::MatchStatus;
1352 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1353 logging::printf(
"findCandidateTransitions(): \n");
1355 logging::printf(
"\n");
1357 const ZPB policy = match->era.zonePolicy();
1358 uint8_t numRules = policy.numRules();
1359 int8_t startY = match->startDateTime.yearTiny;
1360 int8_t endY = match->untilDateTime.yearTiny;
1365 Transition** prior = transitionStorage.reservePrior();
1366 (*prior)->isValidPrior =
false;
1367 for (uint8_t r = 0; r < numRules; r++) {
1368 const ZRB rule = policy.rule(r);
1371 int8_t interiorYears[kMaxInteriorYears];
1372 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1373 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1374 for (uint8_t y = 0; y < numYears; y++) {
1375 int8_t year = interiorYears[y];
1376 Transition* t = transitionStorage.getFreeAgent();
1377 createTransitionForYear(t, year, rule, match);
1378 MatchStatus status = compareTransitionToMatchFuzzy(t, match);
1379 if (status == MatchStatus::kPrior) {
1380 transitionStorage.setFreeAgentAsPriorIfValid();
1381 }
else if (status == MatchStatus::kWithinMatch) {
1382 transitionStorage.addFreeAgentToCandidatePool();
1390 int8_t priorYear = getMostRecentPriorYear(
1391 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1393 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1395 "findCandidateTransitions(): priorYear: %d\n",
1398 Transition* t = transitionStorage.getFreeAgent();
1399 createTransitionForYear(t, priorYear, rule, match);
1400 transitionStorage.setFreeAgentAsPriorIfValid();
1406 if ((*prior)->isValidPrior) {
1407 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1409 "findCandidateTransitions(): adding prior to Candidate pool\n");
1411 logging::printf(
"\n");
1413 transitionStorage.addPriorToCandidatePool();
1436 static uint8_t calcInteriorYears(
1437 int8_t* interiorYears,
1438 uint8_t maxInteriorYears,
1439 int8_t fromYear, int8_t toYear,
1440 int8_t startYear, int8_t endYear) {
1442 for (int8_t year = startYear; year <= endYear; year++) {
1443 if (fromYear <= year && year <= toYear) {
1444 interiorYears[i] = year;
1446 if (i >= maxInteriorYears)
break;
1458 static void createTransitionForYear(
1465 t->offsetMinutes = match->era.offsetMinutes();
1466 t->letterBuf[0] =
'\0';
1468 if (! rule.isNull()) {
1469 t->transitionTime = getTransitionTime(year, rule);
1470 t->deltaMinutes = rule.deltaMinutes();
1472 char letter = rule.letter();
1475 if (letter !=
'-') {
1476 t->letterBuf[0] = letter;
1477 t->letterBuf[1] =
'\0';
1487 t->transitionTime = match->startDateTime;
1488 t->deltaMinutes = match->era.deltaMinutes();
1504 static int8_t getMostRecentPriorYear(
1505 int8_t fromYear, int8_t toYear,
1506 int8_t startYear, int8_t endYear) {
1510 if (fromYear < startYear) {
1511 if (toYear < startYear) {
1514 return startYear - 1;
1525 static extended::DateTuple getTransitionTime(
1526 int8_t yearTiny,
const ZRB& rule) {
1528 internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1532 rule.onDayOfMonth());
1537 (int16_t) rule.atTimeMinutes(),
1552 static extended::MatchStatus compareTransitionToMatchFuzzy(
1554 using extended::MatchStatus;
1556 int16_t ttMonths = t->transitionTime.yearTiny * 12
1557 + t->transitionTime.month;
1559 int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1560 + match->startDateTime.month;
1561 if (ttMonths < matchStartMonths - 1)
return MatchStatus::kPrior;
1563 int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1564 + match->untilDateTime.month;
1565 if (matchUntilMonths + 2 <= ttMonths)
return MatchStatus::kFarFuture;
1567 return MatchStatus::kWithinMatch;
1579 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1580 logging::printf(
"fixTransitionTimes(): START; #transitions=%d\n",
1581 (
int) (end - begin));
1582 printTransitions(begin, end);
1588 for (
Transition** iter = begin; iter != end; ++iter) {
1591 &curr->transitionTime,
1592 prev->offsetMinutes,
1594 &curr->transitionTime,
1595 &curr->transitionTimeS,
1596 &curr->transitionTimeU);
1599 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1600 logging::printf(
"fixTransitionTimes(): FIXED\n");
1601 printTransitions(begin, end);
1602 logging::printf(
"fixTransitionTimes(): END\n");
1606 #ifdef ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1608 for (
Transition** iter = begin; iter != end; ++iter) {
1610 logging::printf(
"\n");
1620 static void expandDateTuple(
1621 const extended::DateTuple* tt,
1622 int16_t offsetMinutes,
1623 int16_t deltaMinutes,
1624 extended::DateTuple* ttw,
1625 extended::DateTuple* tts,
1626 extended::DateTuple* ttu) {
1630 *ttu = {tt->yearTiny, tt->month, tt->day,
1631 (int16_t) (tt->minutes - offsetMinutes),
1633 *ttw = {tt->yearTiny, tt->month, tt->day,
1634 (int16_t) (tt->minutes + deltaMinutes),
1638 *tts = {tt->yearTiny, tt->month, tt->day,
1639 (int16_t) (tt->minutes + offsetMinutes),
1641 *ttw = {tt->yearTiny, tt->month, tt->day,
1642 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1648 *tts = {tt->yearTiny, tt->month, tt->day,
1649 (int16_t) (tt->minutes - deltaMinutes),
1651 *ttu = {tt->yearTiny, tt->month, tt->day,
1652 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1656 normalizeDateTuple(ttw);
1657 normalizeDateTuple(tts);
1658 normalizeDateTuple(ttu);
1665 static void normalizeDateTuple(extended::DateTuple* dt) {
1666 const int16_t kOneDayAsMinutes = 60 * 24;
1667 if (dt->minutes <= -kOneDayAsMinutes) {
1669 dt->yearTiny, dt->month, dt->day);
1670 local_date_mutation::decrementOneDay(ld);
1671 dt->yearTiny = ld.yearTiny();
1672 dt->month = ld.month();
1674 dt->minutes += kOneDayAsMinutes;
1675 }
else if (kOneDayAsMinutes <= dt->minutes) {
1677 dt->yearTiny, dt->month, dt->day);
1678 local_date_mutation::incrementOneDay(ld);
1679 dt->yearTiny = ld.yearTiny();
1680 dt->month = ld.month();
1682 dt->minutes -= kOneDayAsMinutes;
1693 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1694 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1695 (
int) (end - begin));
1699 for (
Transition** iter = begin; iter != end; ++iter) {
1701 processTransitionMatchStatus(transition, &prior);
1707 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1709 "selectActiveTransitions(): found latest prior\n");
1711 #if ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG
1712 prior->originalTransitionTime = prior->transitionTime;
1714 prior->transitionTime = prior->match->startDateTime;
1724 static void processTransitionMatchStatus(
1727 using extended::MatchStatus;
1729 MatchStatus status = compareTransitionToMatch(
1730 transition, transition->match);
1731 transition->matchStatus = status;
1733 if (status == MatchStatus::kExactMatch) {
1735 (*prior)->matchStatus = MatchStatus::kFarPast;
1737 (*prior) = transition;
1738 }
else if (status == MatchStatus::kPrior) {
1740 if ((*prior)->transitionTimeU <= transition->transitionTimeU) {
1741 (*prior)->matchStatus = MatchStatus::kFarPast;
1742 (*prior) = transition;
1744 transition->matchStatus = MatchStatus::kFarPast;
1747 (*prior) = transition;
1760 static extended::MatchStatus compareTransitionToMatch(
1765 int16_t prevMatchOffsetMinutes;
1766 int16_t prevMatchDeltaMinutes;
1767 if (match->prevMatch) {
1768 prevMatchOffsetMinutes = match->prevMatch->lastOffsetMinutes;
1769 prevMatchDeltaMinutes = match->prevMatch->lastDeltaMinutes;
1771 prevMatchOffsetMinutes = match->era.offsetMinutes();
1772 prevMatchDeltaMinutes = 0;
1776 extended::DateTuple stw;
1777 extended::DateTuple sts;
1778 extended::DateTuple stu;
1780 &match->startDateTime,
1781 prevMatchOffsetMinutes,
1782 prevMatchDeltaMinutes,
1788 const extended::DateTuple& ttw = transition->transitionTime;
1789 const extended::DateTuple& tts = transition->transitionTimeS;
1790 const extended::DateTuple& ttu = transition->transitionTimeU;
1795 if (ttw == stw || tts == sts || ttu == stu) {
1796 return extended::MatchStatus::kExactMatch;
1800 return extended::MatchStatus::kPrior;
1808 const extended::DateTuple& matchUntil = match->untilDateTime;
1809 const extended::DateTuple* transitionTime;
1811 transitionTime = &tts;
1813 transitionTime = &ttu;
1815 transitionTime = &ttw;
1817 if (*transitionTime < matchUntil) {
1818 return extended::MatchStatus::kWithinMatch;
1820 return extended::MatchStatus::kFarFuture;
1829 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1831 "generateStartUntilTimes(): #transitions=%d\n",
1832 (
int) (end - begin));
1836 bool isAfterFirst =
false;
1838 for (
Transition** iter = begin; iter != end; ++iter) {
1842 const extended::DateTuple& tt = t->transitionTime;
1844 prev->untilDateTime = tt;
1850 int16_t minutes = tt.minutes + (
1851 - prev->offsetMinutes - prev->deltaMinutes
1852 + t->offsetMinutes + t->deltaMinutes);
1853 t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
1855 normalizeDateTuple(&t->startDateTime);
1867 const extended::DateTuple& st = t->startDateTime;
1868 const acetime_t offsetSeconds = (acetime_t) 60
1869 * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
1871 st.yearTiny, st.month, st.day);
1872 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1875 isAfterFirst =
true;
1879 extended::DateTuple untilTimeW;
1880 extended::DateTuple untilTimeS;
1881 extended::DateTuple untilTimeU;
1883 &prev->match->untilDateTime,
1884 prev->offsetMinutes,
1889 prev->untilDateTime = untilTimeW;
1896 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1897 logging::printf(
"calcAbbreviations(): #transitions: %d\n",
1898 (
int) (end - begin));
1900 for (
Transition** iter = begin; iter != end; ++iter) {
1902 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1904 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1905 t->format(), t->deltaMinutes, t->letter());
1909 internal::kAbbrevSize,
1924 static void createAbbreviation(
1928 uint16_t deltaMinutes,
1929 const char* letterString) {
1932 if (strchr(format,
'%') !=
nullptr) {
1934 if (letterString ==
nullptr) {
1935 strncpy(dest, format, destSize - 1);
1936 dest[destSize - 1] =
'\0';
1938 ace_common::copyReplaceString(
1939 dest, destSize, format,
'%', letterString);
1943 const char* slashPos = strchr(format,
'/');
1944 if (slashPos !=
nullptr) {
1945 if (deltaMinutes == 0) {
1946 uint8_t headLength = (slashPos - format);
1947 if (headLength >= destSize) headLength = destSize - 1;
1948 memcpy(dest, format, headLength);
1949 dest[headLength] =
'\0';
1951 uint8_t tailLength = strlen(slashPos+1);
1952 if (tailLength >= destSize) tailLength = destSize - 1;
1953 memcpy(dest, slashPos+1, tailLength);
1954 dest[tailLength] =
'\0';
1958 strncpy(dest, format, destSize);
1959 dest[destSize - 1] =
'\0';
1964 const BF* mBrokerFactory;
1965 ZIB mZoneInfoBroker;
1967 mutable int16_t mYear = 0;
1968 mutable bool mIsFilled =
false;
1970 mutable uint8_t mNumMatches = 0;
1981 extended::BrokerFactory,
1982 extended::ZoneInfoBroker,
1983 extended::ZoneEraBroker,
1984 extended::ZonePolicyBroker,
1985 extended::ZoneRuleBroker> {
1993 extended::BrokerFactory,
1994 extended::ZoneInfoBroker,
1995 extended::ZoneEraBroker,
1996 extended::ZonePolicyBroker,
1997 extended::ZoneRuleBroker>(