6 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_H
7 #define ACE_TIME_BASIC_ZONE_PROCESSOR_H
11 #include "internal/ZonePolicy.h"
12 #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"
20 #define ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG 0
22 class BasicZoneProcessorTest_priorYearOfRule;
23 class BasicZoneProcessorTest_compareRulesBeforeYear;
24 class BasicZoneProcessorTest_findLatestPriorRule;
25 class BasicZoneProcessorTest_findZoneEra;
26 class BasicZoneProcessorTest_init_primitives;
27 class BasicZoneProcessorTest_init;
28 class BasicZoneProcessorTest_setZoneKey;
29 class BasicZoneProcessorTest_createAbbreviation;
30 class BasicZoneProcessorTest_calcRuleOffsetMinutes;
57 template <
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
111 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
113 if (
sizeof(acetime_t) ==
sizeof(
int)) {
119 logging::printf(
"; abbrev: %s",
abbrev);
120 if (!
rule.isNull()) {
121 logging::printf(
"; r.fromYear: %d",
rule.fromYearTiny());
122 logging::printf(
"; r.toYear: %d",
rule.toYearTiny());
123 logging::printf(
"; r.inMonth: %d",
rule.inMonth());
124 logging::printf(
"; r.onDayOfMonth: %d",
rule.onDayOfMonth());
126 logging::printf(
"\n");
132 inline int8_t compareYearMonth(int8_t aYear, uint8_t aMonth,
133 int8_t bYear, uint8_t bMonth) {
134 if (aYear < bYear)
return -1;
135 if (aYear > bYear)
return 1;
136 if (aMonth < bMonth)
return -1;
137 if (aMonth > bMonth)
return 1;
146 inline void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
147 char oldChar,
char newChar) {
148 while (*src !=
'\0' && dstSize > 0) {
149 if (*src == oldChar) {
150 if (newChar !=
'\0') {
227 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
233 uint32_t
getZoneId()
const override {
return mZoneInfoBroker.zoneId(); }
236 const Transition* transition = getTransition(epochSeconds);
237 int16_t minutes = (transition)
243 const Transition* transition = getTransition(epochSeconds);
244 int16_t minutes = (transition)
249 const char*
getAbbrev(acetime_t epochSeconds)
const override {
250 const Transition* transition = getTransition(epochSeconds);
251 return (transition) ? transition->
abbrev :
"";
305 if (offset1 == offset2) {
309 acetime_t epochSeconds;
311 if (epochSeconds1 > epochSeconds2) {
312 epochSeconds = epochSeconds1;
315 epochSeconds = epochSeconds2;
328 mZoneInfoBroker.printNameTo(printer);
332 mZoneInfoBroker.printShortNameTo(printer);
336 if (mZoneInfoBroker.equals(zoneKey))
return;
338 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
345 return mZoneInfoBroker.equals(zoneKey);
350 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
352 logging::printf(
"*not initialized*\n");
355 logging::printf(
"mYearTiny: %d\n", mYearTiny);
356 logging::printf(
"mNumTransitions: %d\n", mNumTransitions);
357 for (
int i = 0; i < mNumTransitions; i++) {
358 logging::printf(
"mT[%d]=", i);
359 mTransitions[i].
log();
364 void setBrokerFactory(
const BF* brokerFactory) {
365 mBrokerFactory = brokerFactory;
378 const BF* brokerFactory,
382 mBrokerFactory(brokerFactory)
388 friend class ::BasicZoneProcessorTest_priorYearOfRule;
389 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
390 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
391 friend class ::BasicZoneProcessorTest_findZoneEra;
392 friend class ::BasicZoneProcessorTest_init_primitives;
393 friend class ::BasicZoneProcessorTest_init;
394 friend class ::BasicZoneProcessorTest_setZoneKey;
395 friend class ::BasicZoneProcessorTest_createAbbreviation;
396 friend class ::BasicZoneProcessorTest_calcRuleOffsetMinutes;
408 static const uint8_t kMaxCacheEntries = 5;
415 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
423 return mZoneInfoBroker.equals(
428 const Transition* getTransition(acetime_t epochSeconds)
const {
430 bool success = init(ld);
431 return (success) ? findMatch(epochSeconds) : nullptr;
462 bool init(
const LocalDate& ld)
const {
463 int8_t yearTiny = ld.yearTiny();
464 if (ld.month() == 1 && ld.day() == 1) {
467 if (isFilled(yearTiny)) {
468 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
469 logging::printf(
"init(): %d (using cached %d)\n",
470 ld.yearTiny(), yearTiny);
474 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
475 logging::printf(
"init(): %d (new year %d)\n",
476 ld.yearTiny(), yearTiny);
480 mYearTiny = yearTiny;
484 < mZoneInfoBroker.zoneContext()->startYear - 1
485 || mZoneInfoBroker.zoneContext()->untilYear
490 ZEB priorEra = addTransitionPriorToYear(yearTiny);
491 ZEB currentEra = addTransitionsForYear(yearTiny, priorEra);
492 addTransitionAfterYear(yearTiny, currentEra);
498 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
506 bool isFilled(int8_t yearTiny)
const {
507 return mIsFilled && (yearTiny == mYearTiny);
516 ZEB addTransitionPriorToYear(int8_t yearTiny)
const {
517 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
518 logging::printf(
"addTransitionPriorToYear(): %d\n", yearTiny);
521 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny - 1);
525 ZRB latest = findLatestPriorRule(
526 era.zonePolicy(), yearTiny);
527 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
528 logging::printf(
"addTransitionsPriorToYear(): adding latest prior ");
529 if (latest.isNull()) {
530 logging::printf(
"ZR(null)\n");
532 logging::printf(
"ZR[%d,%d]\n",
533 latest.fromYearTiny(), latest.toYearTiny());
536 addTransition(yearTiny - 1, 0 , era, latest);
546 static ZRB findLatestPriorRule(
const ZPB& zonePolicy, int8_t yearTiny) {
548 if (zonePolicy.isNull())
return latest;
550 uint8_t numRules = zonePolicy.numRules();
551 for (uint8_t i = 0; i < numRules; i++) {
552 const ZRB rule = zonePolicy.rule(i);
554 if (rule.fromYearTiny() < yearTiny) {
555 if ((latest.isNull()) ||
556 compareRulesBeforeYear(yearTiny, rule, latest) > 0) {
566 static int8_t compareRulesBeforeYear(int8_t yearTiny,
567 const ZRB& a,
const ZRB& b) {
568 return basic::compareYearMonth(
569 priorYearOfRule(yearTiny, a), a.inMonth(),
570 priorYearOfRule(yearTiny, b), b.inMonth());
581 static int8_t priorYearOfRule(int8_t yearTiny,
const ZRB& rule) {
582 if (rule.toYearTiny() < yearTiny) {
583 return rule.toYearTiny();
592 ZEB addTransitionsForYear(int8_t yearTiny,
const ZEB& priorEra)
const {
593 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
594 logging::printf(
"addTransitionsForYear(): %d\n", yearTiny);
597 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny);
601 const ZPB zonePolicy = era.zonePolicy();
602 if (zonePolicy.isNull()) {
603 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
604 logging::printf(
"addTransitionsForYear(): adding ZE.untilY=%d\n",
605 era.untilYearTiny());
607 addTransition(yearTiny, 0 , era, ZRB());
611 if (! era.equals(priorEra)) {
615 ZRB latestPrior = findLatestPriorRule(
616 era.zonePolicy(), yearTiny);
617 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
619 "addTransitionsForYear(): adding latest prior ");
620 if (latestPrior.isNull()) {
621 logging::printf(
"ZR(null)\n");
623 logging::printf(
"ZR[%d,%d]\n",
624 latestPrior.fromYearTiny(), latestPrior.toYearTiny());
627 addTransition(yearTiny, 1 , era, latestPrior);
633 uint8_t numRules = zonePolicy.numRules();
634 for (uint8_t i = 0; i < numRules; i++) {
635 const ZRB rule = zonePolicy.rule(i);
636 if ((rule.fromYearTiny() <= yearTiny) &&
637 (yearTiny <= rule.toYearTiny())) {
638 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
640 "addTransitionsForYear(): adding rule ");
642 logging::printf(
"ZR(null)\n");
644 logging::printf(
"ZR[%d,%d]\n",
645 rule.fromYearTiny(), rule.toYearTiny());
648 addTransition(yearTiny, 0 , era, rule);
656 void addTransitionAfterYear(int8_t yearTiny,
const ZEB& currentEra)
const {
657 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
658 logging::printf(
"addTransitionAfterYear(): %d\n", yearTiny);
661 const ZEB eraAfter = findZoneEra(mZoneInfoBroker, yearTiny + 1);
666 if (currentEra.equals(eraAfter)) {
673 ZRB latest = findLatestPriorRule(eraAfter.zonePolicy(), yearTiny + 1);
674 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
676 "addTransitionsAfterYear(): adding latest prior ");
677 if (latest.isNull()) {
678 logging::printf(
"ZR(null)\n");
680 logging::printf(
"ZR[%d,%d]\n",
681 latest.fromYearTiny(), latest.toYearTiny());
684 addTransition(yearTiny + 1, 1 , eraAfter, latest);
710 void addTransition(int8_t yearTiny, uint8_t month,
const ZEB& era,
711 const ZRB& rule)
const {
729 if (mNumTransitions >= kMaxCacheEntries)
return;
732 mTransitions[mNumTransitions] = createTransition(
733 yearTiny, month, era, rule);
737 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
741 if (basic::compareYearMonth(left.yearTiny, left.month,
742 right.yearTiny, right.month) > 0) {
755 static Transition createTransition(int8_t yearTiny, uint8_t month,
756 const ZEB& era,
const ZRB& rule) {
757 int16_t deltaMinutes;
762 deltaMinutes = era.deltaMinutes();
765 mon = rule.inMonth();
766 deltaMinutes = rule.deltaMinutes();
767 letter = rule.letter();
773 int16_t offsetMinutes = era.offsetMinutes() + deltaMinutes;
792 static ZEB findZoneEra(
const ZIB& info, int8_t yearTiny) {
793 for (uint8_t i = 0; i < info.numEras(); i++) {
794 const ZEB era = info.era(i);
795 if (yearTiny < era.untilYearTiny())
return era;
798 return info.era(info.numEras() - 1);
808 void calcTransitions()
const {
809 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
810 logging::printf(
"calcTransitions():\n");
814 Transition* prevTransition = &mTransitions[0];
817 for (uint8_t i = 1; i < mNumTransitions; i++) {
821 if (transition.rule.isNull()) {
833 const int16_t prevOffsetMinutes = prevTransition->offsetMinutes;
837 transition.startEpochSeconds = startDateTime.toEpochSeconds();
845 const internal::MonthDay monthDay = internal::calcStartDayOfMonth(
846 year, transition.month, transition.rule.onDayOfWeek(),
847 transition.rule.onDayOfMonth());
851 const int16_t prevOffsetMinutes = calcRuleOffsetMinutes(
852 prevTransition->offsetMinutes,
853 transition.era.offsetMinutes(),
854 transition.rule.atTimeSuffix());
857 const uint16_t minutes = transition.rule.atTimeMinutes();
858 const uint8_t atHour = minutes / 60;
859 const uint8_t atMinute = minutes % 60;
861 year, monthDay.month, monthDay.day,
862 atHour, atMinute, 0 ,
864 transition.startEpochSeconds = startDateTime.toEpochSeconds();
867 prevTransition = &transition;
877 static int16_t calcRuleOffsetMinutes(int16_t prevEffectiveOffsetMinutes,
878 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
880 return prevEffectiveOffsetMinutes;
882 return currentBaseOffsetMinutes;
889 void calcAbbreviations()
const {
890 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
891 logging::printf(
"calcAbbreviations():\n");
894 for (uint8_t i = 0; i < mNumTransitions; i++) {
895 calcAbbreviation(&mTransitions[i]);
900 static void calcAbbreviation(
Transition* transition) {
903 internal::kAbbrevSize,
904 transition->era.format(),
905 transition->deltaMinutes,
906 transition->abbrev[0]);
957 static void createAbbreviation(
char* dest, uint8_t destSize,
958 const char* format, int16_t deltaMinutes,
char letter) {
960 if (strchr(format,
'%') !=
nullptr) {
962 if (letter ==
'\0') {
963 strncpy(dest, format, destSize - 1);
964 dest[destSize - 1] =
'\0';
966 basic::copyAndReplace(dest, destSize, format,
'%',
967 letter ==
'-' ?
'\0' : letter);
971 const char* slashPos = strchr(format,
'/');
972 if (slashPos !=
nullptr) {
973 if (deltaMinutes == 0) {
974 uint8_t headLength = (slashPos - format);
975 if (headLength >= destSize) headLength = destSize - 1;
976 memcpy(dest, format, headLength);
977 dest[headLength] =
'\0';
979 uint8_t tailLength = strlen(slashPos+1);
980 if (tailLength >= destSize) tailLength = destSize - 1;
981 memcpy(dest, slashPos+1, tailLength);
982 dest[tailLength] =
'\0';
986 strncpy(dest, format, destSize - 1);
987 dest[destSize - 1] =
'\0';
993 const Transition* findMatch(acetime_t epochSeconds)
const {
995 for (uint8_t i = 0; i < mNumTransitions; i++) {
997 if (closestMatch ==
nullptr || m->startEpochSeconds <= epochSeconds) {
1001 return closestMatch;
1004 const BF* mBrokerFactory;
1005 ZIB mZoneInfoBroker;
1008 mutable bool mIsFilled =
false;
1009 mutable uint8_t mNumTransitions = 0;
1010 mutable Transition mTransitions[kMaxCacheEntries];
1018 basic::BrokerFactory,
1019 basic::ZoneInfoBroker,
1020 basic::ZoneEraBroker,
1021 basic::ZonePolicyBroker,
1022 basic::ZoneRuleBroker> {
1030 basic::BrokerFactory,
1031 basic::ZoneInfoBroker,
1032 basic::ZoneEraBroker,
1033 basic::ZonePolicyBroker,
1034 basic::ZoneRuleBroker>(
1035 kTypeBasic, &mBrokerFactory, (uintptr_t) zoneInfo)