6 #ifndef ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
7 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_H
12 #include "internal/ZonePolicy.h"
13 #include "internal/ZoneInfo.h"
14 #include "common/logging.h"
15 #include "TimeOffset.h"
16 #include "LocalDate.h"
17 #include "OffsetDateTime.h"
18 #include "ZoneProcessor.h"
19 #include "BasicZoneProcessor.h"
20 #include "local_date_mutation.h"
22 #define ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG 0
24 class ExtendedZoneProcessorTest_compareEraToYearMonth;
25 class ExtendedZoneProcessorTest_compareEraToYearMonth2;
26 class ExtendedZoneProcessorTest_createMatch;
27 class ExtendedZoneProcessorTest_findMatches_simple;
28 class ExtendedZoneProcessorTest_findMatches_named;
29 class ExtendedZoneProcessorTest_findCandidateTransitions;
30 class ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
31 class ExtendedZoneProcessorTest_getTransitionTime;
32 class ExtendedZoneProcessorTest_createTransitionForYear;
33 class ExtendedZoneProcessorTest_normalizeDateTuple;
34 class ExtendedZoneProcessorTest_expandDateTuple;
35 class ExtendedZoneProcessorTest_calcInteriorYears;
36 class ExtendedZoneProcessorTest_getMostRecentPriorYear;
37 class ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
38 class ExtendedZoneProcessorTest_compareTransitionToMatch;
39 class ExtendedZoneProcessorTest_processActiveTransition;
40 class ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
41 class ExtendedZoneProcessorTest_createAbbreviation;
42 class ExtendedZoneProcessorTest_setZoneInfo;
43 class TransitionStorageTest_getFreeAgent;
44 class TransitionStorageTest_getFreeAgent2;
45 class TransitionStorageTest_addFreeAgentToActivePool;
46 class TransitionStorageTest_reservePrior;
47 class TransitionStorageTest_addFreeAgentToCandidatePool;
48 class TransitionStorageTest_setFreeAgentAsPrior;
49 class TransitionStorageTest_addActiveCandidatesToActivePool;
50 class TransitionStorageTest_resetCandidatePool;
51 class TransitionStorageTest_findTransitionForDateTime;
55 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
56 class ZoneProcessorCacheImpl;
67 DateTuple(int8_t y, uint8_t mon, uint8_t d, int16_t min, uint8_t mod):
68 yearTiny(y), month(mon), day(d), suffix(mod), minutes(min) {}
78 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
79 char c =
"wsu"[(suffix>>4)];
80 logging::printf(
"DateTuple(%04d-%02u-%02uT%d'%c')",
87 inline bool operator<(
const DateTuple& a,
const DateTuple& b) {
88 if (a.yearTiny < b.yearTiny)
return true;
89 if (a.yearTiny > b.yearTiny)
return false;
90 if (a.month < b.month)
return true;
91 if (a.month > b.month)
return false;
92 if (a.day < b.day)
return true;
93 if (a.day > b.day)
return false;
94 if (a.minutes < b.minutes)
return true;
95 if (a.minutes > b.minutes)
return false;
99 inline bool operator>=(
const DateTuple& a,
const DateTuple& b) {
103 inline bool operator<=(
const DateTuple& a,
const DateTuple& b) {
107 inline bool operator>(
const DateTuple& a,
const DateTuple& b) {
112 inline bool operator==(
const DateTuple& a,
const DateTuple& b) {
113 return a.yearTiny == b.yearTiny
114 && a.month == b.month
116 && a.minutes == b.minutes
117 && a.suffix == b.suffix;
141 logging::printf(
"ZoneMatch(");
144 logging::printf(
"; Era: %snull", (
era.isNull()) ?
"" :
"!");
145 logging::printf(
")");
269 const char* format()
const {
295 uint8_t numLetters = policy.numLetters();
296 if (
letter >= numLetters) {
304 return policy.letter(
letter);
309 logging::printf(
"Transition(");
310 if (
sizeof(acetime_t) <=
sizeof(
int)) {
315 logging::printf(
"; match: %snull", (
match) ?
"!" :
"");
316 logging::printf(
"; era: %snull",
321 if (!
rule.isNull()) {
322 logging::printf(
"; R.fY: %d",
rule.fromYearTiny());
323 logging::printf(
"; R.tY: %d",
rule.toYearTiny());
324 logging::printf(
"; R.M: %d",
rule.inMonth());
325 logging::printf(
"; R.dow: %d",
rule.onDayOfWeek());
326 logging::printf(
"; R.dom: %d",
rule.onDayOfMonth());
357 template<u
int8_t SIZE>
365 for (uint8_t i = 0; i < SIZE; i++) {
366 mTransitions[i] = &mPool[i];
369 mIndexCandidates = 0;
392 mIndexCandidates = mIndexPrior;
393 mIndexFree = mIndexPrior;
397 return &mTransitions[mIndexCandidates];
399 Transition** getCandidatePoolEnd() {
400 return &mTransitions[mIndexFree];
403 Transition** getActivePoolBegin() {
return &mTransitions[0]; }
404 Transition** getActivePoolEnd() {
return &mTransitions[mIndexFree]; }
414 if (mIndexFree > mHighWater) {
415 mHighWater = mIndexFree;
418 if (mIndexFree < SIZE) {
419 return mTransitions[mIndexFree];
421 return mTransitions[SIZE - 1];
433 if (mIndexFree >= SIZE)
return;
435 mIndexPrior = mIndexFree;
436 mIndexCandidates = mIndexFree;
447 return &mTransitions[mIndexPrior];
452 swap(&mTransitions[mIndexPrior], &mTransitions[mIndexFree]);
471 if (mIndexFree >= SIZE)
return;
472 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
476 mTransitions[i] = prev;
477 mTransitions[i - 1] = curr;
487 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
488 logging::printf(
"addActiveCandidatesToActivePool()\n");
490 uint8_t iActive = mIndexPrior;
491 uint8_t iCandidate = mIndexCandidates;
492 for (; iCandidate < mIndexFree; iCandidate++) {
493 if (mTransitions[iCandidate]->active) {
494 if (iActive != iCandidate) {
495 swap(&mTransitions[iActive], &mTransitions[iCandidate]);
500 mIndexPrior = iActive;
501 mIndexCandidates = iActive;
502 mIndexFree = iActive;
514 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
515 logging::printf(
"findTransition(): mIndexFree: %d\n", mIndexFree);
519 for (uint8_t i = 0; i < mIndexFree; i++) {
520 const Transition* candidate = mTransitions[i];
552 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
554 "findTransitionForDateTime(): mIndexFree: %d\n", mIndexFree);
560 ZoneContext::kSuffixW };
564 for (uint8_t i = 0; i < mIndexFree; i++) {
565 const Transition* candidate = mTransitions[i];
574 logging::printf(
"TransitionStorage:\n");
575 logging::printf(
" mIndexPrior: %d\n", mIndexPrior);
576 logging::printf(
" mIndexCandidates: %d\n", mIndexCandidates);
577 logging::printf(
" mIndexFree: %d\n", mIndexFree);
578 if (mIndexPrior != 0) {
579 logging::printf(
" Actives:\n");
580 for (uint8_t i = 0; i < mIndexPrior; i++) {
581 mTransitions[i]->
log();
582 logging::printf(
"\n");
585 if (mIndexPrior != mIndexCandidates) {
586 logging::printf(
" Prior: ");
587 mTransitions[mIndexPrior]->
log();
588 logging::printf(
"\n");
590 if (mIndexCandidates != mIndexFree) {
591 logging::printf(
" Candidates:\n");
592 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
593 mTransitions[i]->
log();
594 logging::printf(
"\n");
610 friend class ::TransitionStorageTest_getFreeAgent;
611 friend class ::TransitionStorageTest_getFreeAgent2;
612 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
613 friend class ::TransitionStorageTest_reservePrior;
614 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
615 friend class ::TransitionStorageTest_setFreeAgentAsPrior;
616 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
617 friend class ::TransitionStorageTest_findTransitionForDateTime;
618 friend class ::TransitionStorageTest_resetCandidatePool;
621 Transition* getTransition(uint8_t i) {
return mTransitions[i]; }
623 Transition mPool[SIZE];
624 Transition* mTransitions[SIZE];
626 uint8_t mIndexCandidates;
630 uint8_t mHighWater = 0;
665 const extended::ZoneInfo* zoneInfo =
nullptr):
667 mZoneInfo(zoneInfo) {}
671 return mZoneInfo.zoneInfo();
674 uint32_t
getZoneId()
const override {
return mZoneInfo.zoneId(); }
677 bool success = init(epochSeconds);
687 bool success = init(epochSeconds);
693 const char*
getAbbrev(acetime_t epochSeconds)
const override {
694 bool success = init(epochSeconds);
695 if (!success)
return "";
697 return transition->
abbrev;
702 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
703 logging::printf(
"getOffsetDateTime(): ldt=");
704 ldt.
printTo(SERIAL_PORT_MONITOR);
705 SERIAL_PORT_MONITOR.println();
713 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
714 logging::printf(
"getOffsetDateTime(): match transition=");
716 logging::printf(
"\n");
718 offset = (transition)
730 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
731 logging::printf(
"getOffsetDateTime(): odt=");
732 odt.printTo(SERIAL_PORT_MONITOR);
733 SERIAL_PORT_MONITOR.println();
742 acetime_t epochSeconds = odt.toEpochSeconds();
745 offset = (transition)
750 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
751 logging::printf(
"getOffsetDateTime(): normalized(odt)=");
752 odt.printTo(SERIAL_PORT_MONITOR);
753 SERIAL_PORT_MONITOR.println();
758 void printTo(Print& printer)
const override;
764 logging::printf(
"ExtendedZoneProcessor:\n");
765 logging::printf(
" mYear: %d\n", mYear);
766 logging::printf(
" mNumMatches: %d\n", mNumMatches);
767 for (
int i = 0; i < mNumMatches; i++) {
768 logging::printf(
" Match %d: ", i);
770 logging::printf(
"\n");
772 mTransitionStorage.
log();
786 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth;
787 friend class ::ExtendedZoneProcessorTest_compareEraToYearMonth2;
788 friend class ::ExtendedZoneProcessorTest_createMatch;
789 friend class ::ExtendedZoneProcessorTest_findMatches_simple;
790 friend class ::ExtendedZoneProcessorTest_findMatches_named;
791 friend class ::ExtendedZoneProcessorTest_findCandidateTransitions;
792 friend class ::ExtendedZoneProcessorTest_findTransitionsFromNamedMatch;
793 friend class ::ExtendedZoneProcessorTest_getTransitionTime;
794 friend class ::ExtendedZoneProcessorTest_createTransitionForYear;
795 friend class ::ExtendedZoneProcessorTest_normalizeDateTuple;
796 friend class ::ExtendedZoneProcessorTest_expandDateTuple;
797 friend class ::ExtendedZoneProcessorTest_calcInteriorYears;
798 friend class ::ExtendedZoneProcessorTest_getMostRecentPriorYear;
799 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatchFuzzy;
800 friend class ::ExtendedZoneProcessorTest_compareTransitionToMatch;
801 friend class ::ExtendedZoneProcessorTest_processActiveTransition;
802 friend class ::ExtendedZoneProcessorTest_fixTransitionTimes_generateStartUntilTimes;
803 friend class ::ExtendedZoneProcessorTest_createAbbreviation;
804 friend class ::ExtendedZoneProcessorTest_setZoneInfo;
805 friend class ::TransitionStorageTest_findTransitionForDateTime;
807 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
818 static const uint8_t kMaxMatches = 4;
827 static const uint8_t kMaxTransitions = 8;
833 static const uint8_t kMaxInteriorYears = 4;
836 static const extended::ZoneEra kAnchorEra;
854 void setZoneInfo(
const void* zoneInfo)
override {
855 if (mZoneInfo.zoneInfo() == zoneInfo)
return;
857 mZoneInfo = extended::ZoneInfoBroker(
858 (
const extended::ZoneInfo*) zoneInfo);
868 const extended::Transition* findTransition(acetime_t epochSeconds)
const {
873 bool init(acetime_t epochSeconds)
const {
882 bool init(
const LocalDate& ld)
const {
883 int16_t year = ld.year();
884 if (isFilled(year))
return true;
885 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
886 logging::printf(
"init(): %d\n", year);
891 mTransitionStorage.
init();
893 if (year < mZoneInfo.startYear() - 1 || mZoneInfo.untilYear() < year) {
894 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
895 logging::printf(
"init(): Year %d out of valid range [%d, %d)\n",
896 year, mZoneInfo.startYear(), mZoneInfo.untilYear());
901 extended::YearMonthTuple startYm = {
903 extended::YearMonthTuple untilYm = {
906 mNumMatches = findMatches(mZoneInfo, startYm, untilYm, mMatches,
908 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
909 findTransitions(mTransitionStorage, mMatches, mNumMatches);
910 extended::Transition** begin = mTransitionStorage.getActivePoolBegin();
911 extended::Transition** end = mTransitionStorage.getActivePoolEnd();
912 fixTransitionTimes(begin, end);
913 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
914 generateStartUntilTimes(begin, end);
915 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
916 calcAbbreviations(begin, end);
917 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
log(); }
924 bool isFilled(int16_t year)
const {
925 return mIsFilled && (year == mYear);
935 static uint8_t findMatches(
const extended::ZoneInfoBroker zoneInfo,
936 const extended::YearMonthTuple& startYm,
937 const extended::YearMonthTuple& untilYm,
938 extended::ZoneMatch* matches, uint8_t maxMatches) {
939 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
940 logging::printf(
"findMatches()\n");
943 extended::ZoneEraBroker prev = extended::ZoneEraBroker(&kAnchorEra);
944 for (uint8_t iEra = 0; iEra < zoneInfo.numEras(); iEra++) {
945 const extended::ZoneEraBroker era = zoneInfo.era(iEra);
946 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
947 if (iMatch < maxMatches) {
948 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
967 static bool eraOverlapsInterval(
968 const extended::ZoneEraBroker prev,
969 const extended::ZoneEraBroker era,
970 const extended::YearMonthTuple& startYm,
971 const extended::YearMonthTuple& untilYm) {
972 return compareEraToYearMonth(prev, untilYm.yearTiny, untilYm.month) < 0
973 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
977 static int8_t compareEraToYearMonth(
const extended::ZoneEraBroker era,
978 int8_t yearTiny, uint8_t month) {
979 if (era.untilYearTiny() < yearTiny)
return -1;
980 if (era.untilYearTiny() > yearTiny)
return 1;
981 if (era.untilMonth() < month)
return -1;
982 if (era.untilMonth() > month)
return 1;
983 if (era.untilDay() > 1)
return 1;
985 if (era.untilTimeMinutes() > 0)
return 1;
995 static extended::ZoneMatch createMatch(
996 const extended::ZoneEraBroker prev,
997 const extended::ZoneEraBroker era,
998 const extended::YearMonthTuple& startYm,
999 const extended::YearMonthTuple& untilYm) {
1000 extended::DateTuple startDate = {
1001 prev.untilYearTiny(), prev.untilMonth(), prev.untilDay(),
1002 (int16_t) prev.untilTimeMinutes(), prev.untilTimeSuffix()
1004 extended::DateTuple lowerBound = {
1005 startYm.yearTiny, startYm.month, 1, 0,
1006 extended::ZoneContext::kSuffixW
1008 if (startDate < lowerBound) {
1009 startDate = lowerBound;
1012 extended::DateTuple untilDate = {
1013 era.untilYearTiny(), era.untilMonth(), era.untilDay(),
1014 (int16_t) era.untilTimeMinutes(), era.untilTimeSuffix()
1016 extended::DateTuple upperBound = {
1017 untilYm.yearTiny, untilYm.month, 1, 0,
1018 extended::ZoneContext::kSuffixW
1020 if (upperBound < untilDate) {
1021 untilDate = upperBound;
1024 return {startDate, untilDate, era};
1031 static void findTransitions(
1032 extended::TransitionStorage<kMaxTransitions>& transitionStorage,
1033 extended::ZoneMatch* matches,
1034 uint8_t numMatches) {
1035 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1036 logging::printf(
"findTransitions()\n");
1038 for (uint8_t i = 0; i < numMatches; i++) {
1039 findTransitionsForMatch(transitionStorage, &matches[i]);
1044 static void findTransitionsForMatch(
1045 extended::TransitionStorage<kMaxTransitions>& transitionStorage,
1046 const extended::ZoneMatch* match) {
1047 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1048 logging::printf(
"findTransitionsForMatch()\n");
1050 const extended::ZonePolicyBroker policy = match->era.zonePolicy();
1051 if (policy.isNull()) {
1052 findTransitionsFromSimpleMatch(transitionStorage, match);
1054 findTransitionsFromNamedMatch(transitionStorage, match);
1058 static void findTransitionsFromSimpleMatch(
1059 extended::TransitionStorage<kMaxTransitions>& transitionStorage,
1060 const extended::ZoneMatch* match) {
1061 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1062 logging::printf(
"findTransitionsFromSimpleMatch()\n");
1064 extended::Transition* freeTransition = transitionStorage.getFreeAgent();
1065 createTransitionForYear(freeTransition, 0 ,
1066 extended::ZoneRuleBroker(
nullptr) , match);
1067 transitionStorage.addFreeAgentToActivePool();
1070 static void findTransitionsFromNamedMatch(
1071 extended::TransitionStorage<kMaxTransitions>& transitionStorage,
1072 const extended::ZoneMatch* match) {
1073 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1074 logging::printf(
"findTransitionsFromNamedMatch()\n");
1076 transitionStorage.resetCandidatePool();
1077 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1078 match->log(); logging::printf(
"\n");
1080 findCandidateTransitions(transitionStorage, match);
1081 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1082 transitionStorage.log(); logging::printf(
"\n");
1085 transitionStorage.getCandidatePoolBegin(),
1086 transitionStorage.getCandidatePoolEnd());
1087 selectActiveTransitions(transitionStorage, match);
1088 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1089 transitionStorage.log(); logging::printf(
"\n");
1092 transitionStorage.addActiveCandidatesToActivePool();
1093 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1094 transitionStorage.log(); logging::printf(
"\n");
1098 static void findCandidateTransitions(
1099 extended::TransitionStorage<kMaxTransitions>& transitionStorage,
1100 const extended::ZoneMatch* match) {
1101 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1102 logging::printf(
"findCandidateTransitions(): ");
1104 logging::printf(
"\n");
1106 const extended::ZonePolicyBroker policy = match->era.zonePolicy();
1107 uint8_t numRules = policy.numRules();
1108 int8_t startY = match->startDateTime.yearTiny;
1109 int8_t endY = match->untilDateTime.yearTiny;
1111 extended::Transition** prior = transitionStorage.reservePrior();
1112 (*prior)->active =
false;
1113 for (uint8_t r = 0; r < numRules; r++) {
1114 const extended::ZoneRuleBroker rule = policy.rule(r);
1117 int8_t interiorYears[kMaxInteriorYears];
1118 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1119 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1120 for (uint8_t y = 0; y < numYears; y++) {
1121 int8_t year = interiorYears[y];
1122 extended::Transition* t = transitionStorage.getFreeAgent();
1123 createTransitionForYear(t, year, rule, match);
1124 int8_t status = compareTransitionToMatchFuzzy(t, match);
1126 setAsPriorTransition(transitionStorage, t);
1127 }
else if (status == 1) {
1128 transitionStorage.addFreeAgentToCandidatePool();
1133 int8_t priorYear = getMostRecentPriorYear(
1134 rule.fromYearTiny(), rule.toYearTiny(), startY, endY);
1136 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1138 "findCandidateTransitions(): priorYear: %d\n", priorYear);
1140 extended::Transition* t = transitionStorage.getFreeAgent();
1141 createTransitionForYear(t, priorYear, rule, match);
1142 setAsPriorTransition(transitionStorage, t);
1148 if ((*prior)->active) {
1149 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1151 "findCandidateTransitions(): adding prior to Candidate pool\n");
1153 transitionStorage.addPriorToCandidatePool();
1161 static uint8_t calcInteriorYears(int8_t* interiorYears,
1162 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1163 int8_t startYear, int8_t endYear) {
1165 for (int8_t year = startYear; year <= endYear; year++) {
1166 if (fromYear <= year && year <= toYear) {
1167 interiorYears[i] = year;
1169 if (i >= maxInteriorYears)
break;
1181 static void createTransitionForYear(extended::Transition* t, int8_t year,
1182 const extended::ZoneRuleBroker rule,
1183 const extended::ZoneMatch* match) {
1186 t->offsetMinutes = match->era.offsetMinutes();
1187 t->letterBuf[0] =
'\0';
1189 if (! rule.isNull()) {
1190 t->transitionTime = getTransitionTime(year, rule);
1191 t->deltaMinutes = rule.deltaMinutes();
1193 char letter = rule.letter();
1196 if (letter !=
'-') {
1197 t->letterBuf[0] = letter;
1198 t->letterBuf[1] =
'\0';
1206 t->transitionTime = match->startDateTime;
1207 t->deltaMinutes = match->era.deltaMinutes();
1216 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1217 int8_t startYear, int8_t ) {
1218 if (fromYear < startYear) {
1219 if (toYear < startYear) {
1222 return startYear - 1;
1229 static extended::DateTuple getTransitionTime(
1230 int8_t yearTiny,
const extended::ZoneRuleBroker rule) {
1233 rule.onDayOfMonth());
1234 return {yearTiny, monthDay.month, monthDay.day,
1235 (int16_t) rule.atTimeMinutes(), rule.atTimeSuffix()};
1248 static int8_t compareTransitionToMatchFuzzy(
1249 const extended::Transition* t,
const extended::ZoneMatch* match) {
1250 int16_t ttMonths = t->transitionTime.yearTiny * 12
1251 + t->transitionTime.month;
1253 int16_t matchStartMonths = match->startDateTime.yearTiny * 12
1254 + match->startDateTime.month;
1255 if (ttMonths < matchStartMonths - 1)
return -1;
1257 int16_t matchUntilMonths = match->untilDateTime.yearTiny * 12
1258 + match->untilDateTime.month;
1259 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1265 static void setAsPriorTransition(
1266 extended::TransitionStorage<kMaxTransitions>& transitionStorage,
1267 extended::Transition* t) {
1268 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1269 logging::printf(
"setAsPriorTransition()\n");
1271 extended::Transition* prior = transitionStorage.getPrior();
1272 if (prior->active) {
1273 if (prior->transitionTime < t->transitionTime) {
1275 transitionStorage.setFreeAgentAsPrior();
1279 transitionStorage.setFreeAgentAsPrior();
1291 static void fixTransitionTimes(
1292 extended::Transition** begin, extended::Transition** end) {
1293 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1294 logging::printf(
"fixTransitionTimes(): #transitions: %d;\n",
1295 (
int) (end - begin));
1299 extended::Transition* prev = *begin;
1301 for (extended::Transition** iter = begin; iter != end; ++iter) {
1302 extended::Transition* curr = *iter;
1303 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1304 logging::printf(
"fixTransitionTimes(): LOOP\n");
1306 logging::printf(
"\n");
1308 expandDateTuple(&curr->transitionTime,
1309 &curr->transitionTimeS, &curr->transitionTimeU,
1310 prev->offsetMinutes, prev->deltaMinutes);
1313 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1314 logging::printf(
"fixTransitionTimes(): END\n");
1323 static void expandDateTuple(extended::DateTuple* tt,
1324 extended::DateTuple* tts, extended::DateTuple* ttu,
1325 int16_t offsetMinutes, int16_t deltaMinutes) {
1326 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1327 logging::printf(
"expandDateTuple()\n");
1329 if (tt->suffix == extended::ZoneContext::kSuffixS) {
1331 *ttu = {tt->yearTiny, tt->month, tt->day,
1332 (int16_t) (tt->minutes - offsetMinutes),
1333 extended::ZoneContext::kSuffixU};
1334 *tt = {tt->yearTiny, tt->month, tt->day,
1335 (int16_t) (tt->minutes + deltaMinutes),
1336 extended::ZoneContext::kSuffixW};
1337 }
else if (tt->suffix == extended::ZoneContext::kSuffixU) {
1339 *tts = {tt->yearTiny, tt->month, tt->day,
1340 (int16_t) (tt->minutes + offsetMinutes),
1341 extended::ZoneContext::kSuffixS};
1342 *tt = {tt->yearTiny, tt->month, tt->day,
1343 (int16_t) (tt->minutes + (offsetMinutes + deltaMinutes)),
1344 extended::ZoneContext::kSuffixW};
1347 tt->suffix = extended::ZoneContext::kSuffixW;
1348 *tts = {tt->yearTiny, tt->month, tt->day,
1349 (int16_t) (tt->minutes - deltaMinutes),
1350 extended::ZoneContext::kSuffixS};
1351 *ttu = {tt->yearTiny, tt->month, tt->day,
1352 (int16_t) (tt->minutes - (deltaMinutes + offsetMinutes)),
1353 extended::ZoneContext::kSuffixU};
1356 normalizeDateTuple(tt);
1357 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1358 logging::printf(
"expandDateTuple(): normalizeDateTuple(tt): ");
1360 logging::printf(
"\n");
1363 normalizeDateTuple(tts);
1364 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1365 logging::printf(
"expandDateTuple(): normalizeDateTuple(tts): ");
1367 logging::printf(
"\n");
1370 normalizeDateTuple(ttu);
1371 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1372 logging::printf(
"expandDateTuple(): normalizeDateTuple(ttu): ");
1374 logging::printf(
"\n");
1382 static void normalizeDateTuple(extended::DateTuple* dt) {
1383 const int16_t kOneDayAsMinutes = 60 * 24;
1384 if (dt->minutes <= -kOneDayAsMinutes) {
1386 dt->yearTiny, dt->month, dt->day);
1387 local_date_mutation::decrementOneDay(ld);
1388 dt->yearTiny = ld.yearTiny();
1389 dt->month = ld.month();
1391 dt->minutes += kOneDayAsMinutes;
1392 }
else if (kOneDayAsMinutes <= dt->minutes) {
1394 dt->yearTiny, dt->month, dt->day);
1395 local_date_mutation::incrementOneDay(ld);
1396 dt->yearTiny = ld.yearTiny();
1397 dt->month = ld.month();
1399 dt->minutes -= kOneDayAsMinutes;
1409 static void selectActiveTransitions(
1410 extended::TransitionStorage<kMaxTransitions>& transitionStorage,
1411 const extended::ZoneMatch* match) {
1412 extended::Transition** begin = transitionStorage.getCandidatePoolBegin();
1413 extended::Transition** end = transitionStorage.getCandidatePoolEnd();
1414 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1415 logging::printf(
"selectActiveTransitions(): #candidates: %d\n",
1416 (
int) (end - begin));
1418 extended::Transition* prior =
nullptr;
1419 for (extended::Transition** iter = begin; iter != end; ++iter) {
1420 extended::Transition* transition = *iter;
1421 processActiveTransition(match, transition, &prior);
1427 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1429 "selectActiveTransitions(): found latest prior\n");
1431 prior->originalTransitionTime = prior->transitionTime;
1432 prior->transitionTime = match->startDateTime;
1443 static void processActiveTransition(
1444 const extended::ZoneMatch* match,
1445 extended::Transition* transition,
1446 extended::Transition** prior) {
1447 int8_t status = compareTransitionToMatch(transition, match);
1449 transition->active =
false;
1450 }
else if (status == 1) {
1451 transition->active =
true;
1452 }
else if (status == 0) {
1454 (*prior)->active =
false;
1456 transition->active =
true;
1457 (*prior) = transition;
1460 if ((*prior)->transitionTime < transition->transitionTime) {
1461 (*prior)->active =
false;
1462 transition->active =
true;
1463 (*prior) = transition;
1466 transition->active =
true;
1467 (*prior) = transition;
1486 static int8_t compareTransitionToMatch(
1487 const extended::Transition* transition,
1488 const extended::ZoneMatch* match) {
1489 const extended::DateTuple* transitionTime;
1491 const extended::DateTuple& matchStart = match->startDateTime;
1492 if (matchStart.suffix == extended::ZoneContext::kSuffixS) {
1493 transitionTime = &transition->transitionTimeS;
1494 }
else if (matchStart.suffix ==
1495 extended::ZoneContext::kSuffixU) {
1496 transitionTime = &transition->transitionTimeU;
1498 transitionTime = &transition->transitionTime;
1500 if (*transitionTime < matchStart)
return -1;
1501 if (*transitionTime == matchStart)
return 0;
1503 const extended::DateTuple& matchUntil = match->untilDateTime;
1504 if (matchUntil.suffix == extended::ZoneContext::kSuffixS) {
1505 transitionTime = &transition->transitionTimeS;
1506 }
else if (matchUntil.suffix ==
1507 extended::ZoneContext::kSuffixU) {
1508 transitionTime = &transition->transitionTimeU;
1510 transitionTime = &transition->transitionTime;
1512 if (*transitionTime < matchUntil)
return 1;
1521 static void generateStartUntilTimes(
1522 extended::Transition** begin, extended::Transition** end) {
1523 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1525 "generateStartUntilTimes(): #transitions: %d;\n",
1526 (
int) (end - begin));
1529 extended::Transition* prev = *begin;
1530 bool isAfterFirst =
false;
1532 for (extended::Transition** iter = begin; iter != end; ++iter) {
1533 extended::Transition*
const t = *iter;
1536 const extended::DateTuple& tt = t->transitionTime;
1538 prev->untilDateTime = tt;
1544 int16_t minutes = tt.minutes + (
1545 - prev->offsetMinutes - prev->deltaMinutes
1546 + t->offsetMinutes + t->deltaMinutes);
1547 t->startDateTime = {tt.yearTiny, tt.month, tt.day, minutes,
1549 normalizeDateTuple(&t->startDateTime);
1561 const extended::DateTuple& st = t->startDateTime;
1562 const acetime_t offsetSeconds = (acetime_t) 60
1563 * (st.minutes - (t->offsetMinutes + t->deltaMinutes));
1565 st.yearTiny, st.month, st.day);
1566 t->startEpochSeconds = ld.toEpochSeconds() + offsetSeconds;
1569 isAfterFirst =
true;
1573 extended::DateTuple untilTime = prev->match->untilDateTime;
1574 extended::DateTuple untilTimeS;
1575 extended::DateTuple untilTimeU;
1576 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1577 prev->offsetMinutes, prev->deltaMinutes);
1578 prev->untilDateTime = untilTime;
1584 static void calcAbbreviations(
1585 extended::Transition** begin, extended::Transition** end) {
1586 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1587 logging::printf(
"calcAbbreviations(): #transitions: %d;\n",
1588 (
int) (end - begin));
1590 for (extended::Transition** iter = begin; iter != end; ++iter) {
1591 extended::Transition*
const t = *iter;
1592 if (ACE_TIME_EXTENDED_ZONE_PROCESSOR_DEBUG) {
1594 "calcAbbreviations(): format:%s, deltaMinutes:%d, letter:%s\n",
1595 t->format(), t->deltaMinutes, t->letter());
1598 t->format(), t->deltaMinutes, t->letter());
1610 static void createAbbreviation(
char* dest, uint8_t destSize,
1611 const char* format, uint16_t deltaMinutes,
const char* letterString) {
1613 if (strchr(format,
'%') !=
nullptr) {
1615 if (letterString ==
nullptr) {
1616 strncpy(dest, format, destSize - 1);
1617 dest[destSize - 1] =
'\0';
1619 copyAndReplace(dest, destSize, format,
'%', letterString);
1623 const char* slashPos = strchr(format,
'/');
1624 if (slashPos !=
nullptr) {
1625 if (deltaMinutes == 0) {
1626 uint8_t headLength = (slashPos - format);
1627 if (headLength >= destSize) headLength = destSize - 1;
1628 memcpy(dest, format, headLength);
1629 dest[headLength] =
'\0';
1631 uint8_t tailLength = strlen(slashPos+1);
1632 if (tailLength >= destSize) tailLength = destSize - 1;
1633 memcpy(dest, slashPos+1, tailLength);
1634 dest[tailLength] =
'\0';
1638 strncpy(dest, format, destSize);
1639 dest[destSize - 1] =
'\0';
1649 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
1650 char oldChar,
const char* newString) {
1651 while (*src !=
'\0' && dstSize > 0) {
1652 if (*src == oldChar) {
1653 while (*newString !=
'\0' && dstSize > 0) {
1654 *dst++ = *newString++;
1670 extended::ZoneInfoBroker mZoneInfo;
1672 mutable int16_t mYear = 0;
1673 mutable bool mIsFilled =
false;
1675 mutable uint8_t mNumMatches = 0;
1676 mutable extended::ZoneMatch mMatches[kMaxMatches];
1677 mutable extended::TransitionStorage<kMaxTransitions> mTransitionStorage;