6 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
7 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
11 #include <AceCommon.h>
13 #include "internal/ZonePolicy.h"
14 #include "internal/ZoneInfo.h"
16 #include "common/logging.h"
17 #include "TimeOffset.h"
18 #include "LocalDate.h"
19 #include "OffsetDateTime.h"
20 #include "ZoneProcessor.h"
21 #include "local_date_mutation.h"
23 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG 0
25 class ExtendedZoneProcessorTest_compareEraToYearMonth;
26 class ExtendedZoneProcessorTest_compareEraToYearMonth2;
27 class ExtendedZoneProcessorTest_createMatch;
28 class ExtendedZoneProcessorTest_findMatches_simple;
29 class ExtendedZoneProcessorTest_findMatches_named;
30 class ExtendedZoneProcessorTest_findCandidateTransitions;
31 class ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
32 class ExtendedZoneProcessorTest_getTransitionTime;
33 class ExtendedZoneProcessorTest_createTransitionForYear;
34 class ExtendedZoneProcessorTest_normalizeDateTuple;
35 class ExtendedZoneProcessorTest_expandDateTuple;
36 class ExtendedZoneProcessorTest_calcInteriorYears;
37 class ExtendedZoneProcessorTest_getMostRecentPriorYear;
38 class ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
39 class ExtendedZoneProcessorTest_compareTransitionToMatch;
40 class ExtendedZoneProcessorTest_processActiveTransition;
41 class ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
42 class ExtendedZoneProcessorTest_createAbbreviation;
43 class ExtendedZoneProcessorTest_setZoneKey;
44 class TransitionStorageTest_getFreeAgent;
45 class TransitionStorageTest_getFreeAgent2;
46 class TransitionStorageTest_addFreeAgentToActivePool;
47 class TransitionStorageTest_reservePrior;
48 class TransitionStorageTest_addFreeAgentToCandidatePool;
49 class TransitionStorageTest_setFreeAgentAsPrior;
50 class TransitionStorageTest_addActiveCandidatesToActivePool;
51 class TransitionStorageTest_resetCandidatePool;
52 class TransitionStorageTest_findTransitionForDateTime;
66 DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
67 yearTiny(y), month(mon), day(d), suffix(mod), minutes(min) {}
77 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
78 char c =
"wsu"[(suffix>>4)];
79 logging::printf(
"DateTuple(%04d-%02u-%02uT%d'%c')",
86 inline bool operator<(
const DateTuple& a,
const DateTuple& b) {
87 if (a.yearTiny < b.yearTiny)
return true;
88 if (a.yearTiny > b.yearTiny)
return false;
89 if (a.month < b.month)
return true;
90 if (a.month > b.month)
return false;
91 if (a.day < b.day)
return true;
92 if (a.day > b.day)
return false;
93 if (a.minutes < b.minutes)
return true;
94 if (a.minutes > b.minutes)
return false;
98 inline bool operator>=(
const DateTuple& a,
const DateTuple& b) {
102 inline bool operator<=(
const DateTuple& a,
const DateTuple& b) {
106 inline bool operator>(
const DateTuple& a,
const DateTuple& b) {
111 inline bool operator==(
const DateTuple& a,
const DateTuple& b) {
112 return a.yearTiny == b.yearTiny
113 && a.month == b.month
115 && a.minutes == b.minutes
116 && a.suffix == b.suffix;
131 template<
typename ZEB>
143 logging::printf(
"ZoneMatch(");
146 logging::printf(
"; Era: %snull", (
era.isNull()) ?
"" :
"!");
147 logging::printf(
")");
180 template <
typename ZEB,
typename ZPB,
typename ZRB>
273 const char* format()
const {
274 return match->era.format();
298 const ZPB policy =
match->era.zonePolicy();
299 uint8_t numLetters = policy.numLetters();
300 if (
letter >= numLetters) {
308 return policy.letter(
letter);
313 logging::printf(
"Transition(");
314 if (
sizeof(acetime_t) <=
sizeof(
int)) {
319 logging::printf(
"; match: %snull", (
match) ?
"!" :
"");
320 logging::printf(
"; era: %snull",
325 if (!
rule.isNull()) {
326 logging::printf(
"; R.fY: %d",
rule.fromYearTiny());
327 logging::printf(
"; R.tY: %d",
rule.toYearTiny());
328 logging::printf(
"; R.M: %d",
rule.inMonth());
329 logging::printf(
"; R.dow: %d",
rule.onDayOfWeek());
330 logging::printf(
"; R.dom: %d",
rule.onDayOfMonth());
366 template<u
int8_t SIZE,
typename ZEB,
typename ZPB,
typename ZRB>
380 for (uint8_t i = 0; i < SIZE; i++) {
381 mTransitions[i] = &mPool[i];
384 mIndexCandidates = 0;
390 return mTransitions[mIndexPrior];
411 mIndexCandidates = mIndexPrior;
412 mIndexFree = mIndexPrior;
416 return &mTransitions[mIndexCandidates];
419 return &mTransitions[mIndexFree];
423 return &mTransitions[0];
426 return &mTransitions[mIndexFree];
437 if (mIndexFree > mHighWater) {
438 mHighWater = mIndexFree;
441 if (mIndexFree < SIZE) {
442 return mTransitions[mIndexFree];
444 return mTransitions[SIZE - 1];
456 if (mIndexFree >= SIZE)
return;
458 mIndexPrior = mIndexFree;
459 mIndexCandidates = mIndexFree;
470 return &mTransitions[mIndexPrior];
475 swap(&mTransitions[mIndexPrior], &mTransitions[mIndexFree]);
494 if (mIndexFree >= SIZE)
return;
495 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
499 mTransitions[i] = prev;
500 mTransitions[i - 1] = curr;
510 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
511 logging::printf(
"addActiveCandidatesToActivePool()\n");
513 uint8_t iActive = mIndexPrior;
514 uint8_t iCandidate = mIndexCandidates;
515 for (; iCandidate < mIndexFree; iCandidate++) {
516 if (mTransitions[iCandidate]->active) {
517 if (iActive != iCandidate) {
518 swap(&mTransitions[iActive], &mTransitions[iCandidate]);
523 mIndexPrior = iActive;
524 mIndexCandidates = iActive;
525 mIndexFree = iActive;
537 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
538 logging::printf(
"findTransition(): mIndexFree: %d\n", mIndexFree);
542 for (uint8_t i = 0; i < mIndexFree; i++) {
543 const Transition* candidate = mTransitions[i];
575 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
577 "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
587 for (uint8_t i = 0; i < mIndexFree; i++) {
588 const Transition* candidate = mTransitions[i];
597 logging::printf(
"TransitionStorage:\n");
598 logging::printf(
" mIndexPrior: %d\n", mIndexPrior);
599 logging::printf(
" mIndexCandidates: %d\n", mIndexCandidates);
600 logging::printf(
" mIndexFree: %d\n", mIndexFree);
601 if (mIndexPrior != 0) {
602 logging::printf(
" Actives:\n");
603 for (uint8_t i = 0; i < mIndexPrior; i++) {
604 mTransitions[i]->
log();
605 logging::printf(
"\n");
608 if (mIndexPrior != mIndexCandidates) {
609 logging::printf(
" Prior: ");
610 mTransitions[mIndexPrior]->
log();
611 logging::printf(
"\n");
613 if (mIndexCandidates != mIndexFree) {
614 logging::printf(
" Candidates:\n");
615 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
616 mTransitions[i]->
log();
617 logging::printf(
"\n");
633 friend class ::TransitionStorageTest_getFreeAgent;
634 friend class ::TransitionStorageTest_getFreeAgent2;
635 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
636 friend class ::TransitionStorageTest_reservePrior;
637 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
638 friend class ::TransitionStorageTest_setFreeAgentAsPrior;
639 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
640 friend class ::TransitionStorageTest_findTransitionForDateTime;
641 friend class ::TransitionStorageTest_resetCandidatePool;
645 return mTransitions[i];
651 uint8_t mIndexCandidates;
655 uint8_t mHighWater = 0;
691 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
714 uint32_t
getZoneId()
const override {
return mZoneInfoBroker.zoneId(); }
717 bool success = init(epochSeconds);
719 const Transition* transition = findTransition(epochSeconds);
727 bool success = init(epochSeconds);
729 const Transition* transition = findTransition(epochSeconds);
733 const char*
getAbbrev(acetime_t epochSeconds)
const override {
734 bool success = init(epochSeconds);
735 if (!success)
return "";
736 const Transition* transition = findTransition(epochSeconds);
737 return transition->
abbrev;
742 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
743 logging::printf(
"getOffsetDateTime(): ldt=");
744 ldt.
printTo(SERIAL_PORT_MONITOR);
745 SERIAL_PORT_MONITOR.println();
753 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
754 logging::printf(
"getOffsetDateTime(): match transition=");
756 logging::printf(
"\n");
758 offset = (transition)
770 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
771 logging::printf(
"getOffsetDateTime(): odt=");
772 odt.printTo(SERIAL_PORT_MONITOR);
773 SERIAL_PORT_MONITOR.println();
782 acetime_t epochSeconds = odt.toEpochSeconds();
785 offset = (transition)
790 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
791 logging::printf(
"getOffsetDateTime(): normalized(odt)=");
792 odt.printTo(SERIAL_PORT_MONITOR);
793 SERIAL_PORT_MONITOR.println();
799 mZoneInfoBroker.printNameTo(printer);
803 mZoneInfoBroker.printShortNameTo(printer);
808 logging::printf(
"ExtendedZoneProcessor:\n");
809 logging::printf(
" mYear: %d\n", mYear);
810 logging::printf(
" mNumMatches: %d\n", mNumMatches);
811 for (
int i = 0; i < mNumMatches; i++) {
812 logging::printf(
" Match %d: ", i);
814 logging::printf(
"\n");
816 mTransitionStorage.
log();
830 if (mZoneInfoBroker.equals(zoneKey))
return;
832 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
840 return mZoneInfoBroker.equals(zoneKey);
843 void setBrokerFactory(
const BF* brokerFactory) {
844 mBrokerFactory = brokerFactory;
857 const BF* brokerFactory,
861 mBrokerFactory(brokerFactory)
867 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
868 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
869 friend class ::ExtendedZoneProcessorTest_createMatch;
870 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
871 friend class ::ExtendedZoneProcessorTest_findMatches_named;
872 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
873 friend class ::ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
874 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
875 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
876 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
877 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
878 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
879 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
880 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
881 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
882 friend class ::ExtendedZoneProcessorTest_processActiveTransition;
883 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
884 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
885 friend class ::ExtendedZoneProcessorTest_setZoneKey;
886 friend class ::TransitionStorageTest_findTransitionForDateTime;
898 static const uint8_t kMaxMatches = 4;
904 static const uint8_t kMaxInteriorYears = 4;
907 return mZoneInfoBroker.equals(
915 const Transition* findTransition(acetime_t epochSeconds)
const {
920 bool init(acetime_t epochSeconds)
const {
929 bool init(
const LocalDate& ld)
const {
930 int16_t year = ld.year();
931 if (isFilled(year))
return true;
932 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
933 logging::printf(
"init(): %d\n", year);
938 mTransitionStorage.
init();
940 if (year < mZoneInfoBroker.zoneContext()->startYear - 1
941 || mZoneInfoBroker.zoneContext()->untilYear < year) {
942 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
943 logging::printf(
"init(): Year %d out of valid range [%d, %d)\n",
945 mZoneInfoBroker.zoneContext()->startYear,
946 mZoneInfoBroker.zoneContext()->untilYear);
951 extended::YearMonthTuple startYm = {
953 extended::YearMonthTuple untilYm = {
956 mNumMatches = findMatches(mZoneInfoBroker, startYm, untilYm, mMatches,
958 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
959 findTransitions(mTransitionStorage, mMatches, mNumMatches);
960 Transition** begin = mTransitionStorage.getActivePoolBegin();
961 Transition** end = mTransitionStorage.getActivePoolEnd();
962 fixTransitionTimes(begin, end);
963 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
964 generateStartUntilTimes(begin, end);
965 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
966 calcAbbreviations(begin, end);
967 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
974 bool isFilled(int16_t year)
const {
975 return mIsFilled && (year == mYear);
985 static uint8_t findMatches(
const ZIB& zoneInfo,
986 const extended::YearMonthTuple& startYm,
987 const extended::YearMonthTuple& untilYm,
988 ZoneMatch* matches, uint8_t maxMatches) {
989 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
990 logging::printf(
"findMatches()\n");
994 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
995 const ZEB era = zoneInfo.era(iEra);
996 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
997 if (iMatch < maxMatches) {
998 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
1018 static bool eraOverlapsInterval(
1021 const extended::YearMonthTuple& startYm,
1022 const extended::YearMonthTuple& untilYm) {
1023 return (prev.isNull() || compareEraToYearMonth(
1024 prev, untilYm.yearTiny, untilYm.month) < 0)
1025 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
1029 static int8_t compareEraToYearMonth(
const ZEB& era,
1030 int8_t yearTiny, uint8_t month) {
1031 if (era.untilYearTiny() < yearTiny)
return -1;
1032 if (era.untilYearTiny() > yearTiny)
return 1;
1033 if (era.untilMonth() < month)
return -1;
1034 if (era.untilMonth() > month)
return 1;
1035 if (era.untilDay() > 1)
return 1;
1037 if (era.untilTimeMinutes() > 0)
return 1;
1050 const extended::YearMonthTuple& startYm,
1051 const extended::YearMonthTuple& untilYm) {
1053 extended::DateTuple startDate = prev.isNull()
1054 ? extended::DateTuple{
1058 : extended::DateTuple{
1059 prev.untilYearTiny(), prev.untilMonth(), prev.untilDay(),
1060 (int16_t) prev.untilTimeMinutes(), prev.untilTimeSuffix()
1062 extended::DateTuple lowerBound = {
1063 startYm.yearTiny, startYm.month, 1, 0,
1066 if (startDate < lowerBound) {
1067 startDate = lowerBound;
1070 extended::DateTuple untilDate = {
1071 era.untilYearTiny(), era.untilMonth(), era.untilDay(),
1072 (int16_t) era.untilTimeMinutes(), era.untilTimeSuffix()
1074 extended::DateTuple upperBound = {
1075 untilYm.yearTiny, untilYm.month, 1, 0,
1078 if (upperBound < untilDate) {
1079 untilDate = upperBound;
1082 return {startDate, untilDate, era};
1089 static void findTransitions(
1092 uint8_t numMatches) {
1093 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1094 logging::printf(
"findTransitions()\n");
1096 for (uint8_t i = 0; i < numMatches; i++) {
1097 findTransitionsForMatch(transitionStorage, &matches[i]);
1102 static void findTransitionsForMatch(
1105 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1106 logging::printf(
"findTransitionsForMatch()\n");
1108 const ZPB policy = match->era.zonePolicy();
1109 if (policy.isNull()) {
1110 findTransitionsFromSimpleMatch(transitionStorage, match);
1112 findTransitionsFromNamedMatch(transitionStorage, match);
1116 static void findTransitionsFromSimpleMatch(
1119 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1120 logging::printf(
"findTransitionsFromSimpleMatch()\n");
1122 Transition* freeTransition = transitionStorage.getFreeAgent();
1123 createTransitionForYear(freeTransition, 0 ,
1125 transitionStorage.addFreeAgentToActivePool();
1128 static void findTransitionsFromNamedMatch(
1131 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1132 logging::printf(
"findTransitionsFromNamedMatch()\n");
1134 transitionStorage.resetCandidatePool();
1135 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1136 match->log(); logging::printf(
"\n");
1138 findCandidateTransitions(transitionStorage, match);
1139 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1140 transitionStorage.log(); logging::printf(
"\n");
1143 transitionStorage.getCandidatePoolBegin(),
1144 transitionStorage.getCandidatePoolEnd());
1145 selectActiveTransitions(transitionStorage, match);
1146 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1147 transitionStorage.log(); logging::printf(
"\n");
1150 transitionStorage.addActiveCandidatesToActivePool();
1151 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1152 transitionStorage.log(); logging::printf(
"\n");
1156 static void findCandidateTransitions(
1159 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1160 logging::printf(
"findCandidateTransitions(): ");
1162 logging::printf(
"\n");
1164 const ZPB policy = match->era.zonePolicy();
1165 uint8_t numRules = policy.numRules();
1166 int8_t startY = match->startDateTime.yearTiny;
1167 int8_t endY = match->untilDateTime.yearTiny;
1169 Transition** prior = transitionStorage.reservePrior();
1170 (*prior)->active =
false;
1171 for (uint8_t r = 0; r < numRules; r++) {
1172 const ZRB rule = policy.rule(r);
1175 int8_t interiorYears[kMaxInteriorYears];
1176 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1177 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1178 for (uint8_t y = 0; y < numYears; y++) {
1179 int8_t year = interiorYears[y];
1180 Transition* t = transitionStorage.getFreeAgent();
1181 createTransitionForYear(t, year, rule, match);
1182 int8_t status = compareTransitionToMatchFuzzy(t, match);
1184 setAsPriorTransition(transitionStorage, t);
1185 }
else if (status == 1) {
1186 transitionStorage.addFreeAgentToCandidatePool();
1191 int8_t priorYear = getMostRecentPriorYear(
1192 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1194 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1196 "findCandidateTransitions(): priorYear: %d\n", priorYear);
1198 Transition* t = transitionStorage.getFreeAgent();
1199 createTransitionForYear(t, priorYear, rule, match);
1200 setAsPriorTransition(transitionStorage, t);
1206 if ((*prior)->active) {
1207 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1209 "findCandidateTransitions(): adding prior to Candidate pool\n");
1211 transitionStorage.addPriorToCandidatePool();
1219 static uint8_t calcInteriorYears(int8_t* interiorYears,
1220 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1221 int8_t startYear, int8_t endYear) {
1223 for (int8_t year = startYear; year <= endYear; year++) {
1224 if (fromYear <= year && year <= toYear) {
1225 interiorYears[i] = year;
1227 if (i >= maxInteriorYears)
break;
1239 static void createTransitionForYear(
Transition* t, int8_t year,
1240 const ZRB& rule,
const ZoneMatch* match) {
1243 t->offsetMinutes = match->era.offsetMinutes();
1244 t->letterBuf[0] =
'\0';
1246 if (! rule.isNull()) {
1247 t->transitionTime = getTransitionTime(year, rule);
1248 t->deltaMinutes = rule.deltaMinutes();
1250 char letter = rule.letter();
1253 if (letter !=
'-') {
1254 t->letterBuf[0] = letter;
1255 t->letterBuf[1] =
'\0';
1263 t->transitionTime = match->startDateTime;
1264 t->deltaMinutes = match->era.deltaMinutes();
1273 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1274 int8_t startYear, int8_t ) {
1275 if (fromYear < startYear) {
1276 if (toYear < startYear) {
1279 return startYear - 1;
1286 static extended::DateTuple getTransitionTime(
1287 int8_t yearTiny,
const ZRB& rule) {
1288 internal::MonthDay monthDay = internal::calcStartDayOfMonth(
1290 rule.onDayOfMonth());
1291 return {yearTiny, monthDay.month, monthDay.day,
1292 (int16_t) rule.atTimeMinutes(), rule.atTimeSuffix()};
1305 static int8_t compareTransitionToMatchFuzzy(
1307 int16_t ttMonths = t->transitionTime.yearTiny * 12
1308 + t->transitionTime.month;
1310 int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1311 + match->startDateTime.month;
1312 if (ttMonths < matchStartMonths - 1)
return -1;
1314 int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1315 + match->untilDateTime.month;
1316 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1322 static void setAsPriorTransition(
1325 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1326 logging::printf(
"setAsPriorTransition()\n");
1328 Transition* prior = transitionStorage.getPrior();
1329 if (prior->active) {
1330 if (prior->transitionTime < t->transitionTime) {
1332 transitionStorage.setFreeAgentAsPrior();
1336 transitionStorage.setFreeAgentAsPrior();
1349 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1350 logging::printf(
"fixTransitionTimes(): #transitions: %d;\n",
1351 (
int) (end - begin));
1357 for (
Transition** iter = begin; iter != end; ++iter) {
1359 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1360 logging::printf(
"fixTransitionTimes(): LOOP\n");
1362 logging::printf(
"\n");
1364 expandDateTuple(&curr->transitionTime,
1365 &curr->transitionTimeS, &curr->transitionTimeU,
1366 prev->offsetMinutes, prev->deltaMinutes);
1369 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1370 logging::printf(
"fixTransitionTimes(): END\n");
1379 static void expandDateTuple(extended::DateTuple* tt,
1380 extended::DateTuple* tts, extended::DateTuple* ttu,
1381 int16_t offsetMinutes, int16_t deltaMinutes) {
1382 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1383 logging::printf(
"expandDateTuple()\n");
1387 *ttu = {tt->yearTiny, tt->month, tt->day,
1388 (int16_t) (tt->minutes - offsetMinutes),
1390 *tt = {tt->yearTiny, tt->month, tt->day,
1391 (int16_t) (tt->minutes + deltaMinutes),
1395 *tts = {tt->yearTiny, tt->month, tt->day,
1396 (int16_t) (tt->minutes + offsetMinutes),
1398 *tt = {tt->yearTiny, tt->month, tt->day,
1399 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1404 *tts = {tt->yearTiny, tt->month, tt->day,
1405 (int16_t) (tt->minutes - deltaMinutes),
1407 *ttu = {tt->yearTiny, tt->month, tt->day,
1408 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1412 normalizeDateTuple(tt);
1413 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1414 logging::printf(
"expandDateTuple(): normalizeDateTuple(tt): ");
1416 logging::printf(
"\n");
1419 normalizeDateTuple(tts);
1420 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1421 logging::printf(
"expandDateTuple(): normalizeDateTuple(tts): ");
1423 logging::printf(
"\n");
1426 normalizeDateTuple(ttu);
1427 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1428 logging::printf(
"expandDateTuple(): normalizeDateTuple(ttu): ");
1430 logging::printf(
"\n");
1438 static void normalizeDateTuple(extended::DateTuple* dt) {
1439 const int16_t kOneDayAsMinutes = 60 * 24;
1440 if (dt->minutes <= -kOneDayAsMinutes) {
1442 dt->yearTiny, dt->month, dt->day);
1443 local_date_mutation::decrementOneDay(ld);
1444 dt->yearTiny = ld.yearTiny();
1445 dt->month = ld.month();
1447 dt->minutes += kOneDayAsMinutes;
1448 }
else if (kOneDayAsMinutes <= dt->minutes) {
1450 dt->yearTiny, dt->month, dt->day);
1451 local_date_mutation::incrementOneDay(ld);
1452 dt->yearTiny = ld.yearTiny();
1453 dt->month = ld.month();
1455 dt->minutes -= kOneDayAsMinutes;
1465 static void selectActiveTransitions(
1468 Transition** begin = transitionStorage.getCandidatePoolBegin();
1469 Transition** end = transitionStorage.getCandidatePoolEnd();
1470 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1471 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1472 (
int) (end - begin));
1475 for (
Transition** iter = begin; iter != end; ++iter) {
1477 processActiveTransition(match, transition, &prior);
1483 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1485 "selectActiveTransitions(): found latest prior\n");
1487 prior->originalTransitionTime = prior->transitionTime;
1488 prior->transitionTime = match->startDateTime;
1499 static void processActiveTransition(
1503 int8_t status = compareTransitionToMatch(transition, match);
1505 transition->active =
false;
1506 }
else if (status == 1) {
1507 transition->active =
true;
1508 }
else if (status == 0) {
1510 (*prior)->active =
false;
1512 transition->active =
true;
1513 (*prior) = transition;
1516 if ((*prior)->transitionTime < transition->transitionTime) {
1517 (*prior)->active =
false;
1518 transition->active =
true;
1519 (*prior) = transition;
1522 transition->active =
true;
1523 (*prior) = transition;
1542 static int8_t compareTransitionToMatch(
1545 const extended::DateTuple* transitionTime;
1547 const extended::DateTuple& matchStart = match->startDateTime;
1549 transitionTime = &transition->transitionTimeS;
1551 transitionTime = &transition->transitionTimeU;
1553 transitionTime = &transition->transitionTime;
1555 if (*transitionTime < matchStart)
return -1;
1556 if (*transitionTime == matchStart)
return 0;
1558 const extended::DateTuple& matchUntil = match->untilDateTime;
1560 transitionTime = &transition->transitionTimeS;
1561 }
else if (matchUntil.suffix ==
1563 transitionTime = &transition->transitionTimeU;
1565 transitionTime = &transition->transitionTime;
1567 if (*transitionTime < matchUntil)
return 1;
1577 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1579 "generateStartUntilTimes(): #transitions: %d;\n",
1580 (
int) (end - begin));
1584 bool isAfterFirst =
false;
1586 for (
Transition** iter = begin; iter != end; ++iter) {
1590 const extended::DateTuple& tt = t->transitionTime;
1592 prev->untilDateTime = tt;
1598 int16_t minutes = tt.minutes + (
1599 - prev->offsetMinutes - prev->deltaMinutes
1600 + t->offsetMinutes + t->deltaMinutes);
1601 t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
1603 normalizeDateTuple(&t->startDateTime);
1615 const extended::DateTuple& st = t->startDateTime;
1616 const acetime_t offsetSeconds = (acetime_t) 60
1617 * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
1619 st.yearTiny, st.month, st.day);
1620 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1623 isAfterFirst =
true;
1627 extended::DateTuple untilTime = prev->match->untilDateTime;
1628 extended::DateTuple untilTimeS;
1629 extended::DateTuple untilTimeU;
1630 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1631 prev->offsetMinutes, prev->deltaMinutes);
1632 prev->untilDateTime = untilTime;
1639 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1640 logging::printf(
"calcAbbreviations(): #transitions: %d;\n",
1641 (
int) (end - begin));
1643 for (
Transition** iter = begin; iter != end; ++iter) {
1645 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1647 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1648 t->format(), t->deltaMinutes, t->letter());
1650 createAbbreviation(t->abbrev, internal::kAbbrevSize,
1651 t->format(), t->deltaMinutes, t->letter());
1663 static void createAbbreviation(
char* dest, uint8_t destSize,
1664 const char* format, uint16_t deltaMinutes,
const char* letterString) {
1666 if (strchr(format,
'%') !=
nullptr) {
1668 if (letterString ==
nullptr) {
1669 strncpy(dest, format, destSize - 1);
1670 dest[destSize - 1] =
'\0';
1672 ace_common::copyReplaceString(
1673 dest, destSize, format,
'%', letterString);
1677 const char* slashPos = strchr(format,
'/');
1678 if (slashPos !=
nullptr) {
1679 if (deltaMinutes == 0) {
1680 uint8_t headLength = (slashPos - format);
1681 if (headLength >= destSize) headLength = destSize - 1;
1682 memcpy(dest, format, headLength);
1683 dest[headLength] =
'\0';
1685 uint8_t tailLength = strlen(slashPos+1);
1686 if (tailLength >= destSize) tailLength = destSize - 1;
1687 memcpy(dest, slashPos+1, tailLength);
1688 dest[tailLength] =
'\0';
1692 strncpy(dest, format, destSize);
1693 dest[destSize - 1] =
'\0';
1698 const BF* mBrokerFactory;
1699 ZIB mZoneInfoBroker;
1701 mutable int16_t mYear = 0;
1702 mutable bool mIsFilled =
false;
1704 mutable uint8_t mNumMatches = 0;
1705 mutable ZoneMatch mMatches[kMaxMatches];
1715 extended::BrokerFactory,
1716 extended::ZoneInfoBroker,
1717 extended::ZoneEraBroker,
1718 extended::ZonePolicyBroker,
1719 extended::ZoneRuleBroker> {
1727 extended::BrokerFactory,
1728 extended::ZoneInfoBroker,
1729 extended::ZoneEraBroker,
1730 extended::ZonePolicyBroker,
1731 extended::ZoneRuleBroker>(