6 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_H
7 #define ACE_TIME_BASIC_ZONE_PROCESSOR_H
11 #include <AceCommon.h>
12 #include "internal/ZonePolicy.h"
13 #include "internal/ZoneInfo.h"
15 #include "common/logging.h"
16 #include "TimeOffset.h"
17 #include "LocalDate.h"
18 #include "OffsetDateTime.h"
19 #include "ZoneProcessor.h"
21 #define ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG 0
23 class BasicZoneProcessorTest_priorYearOfRule;
24 class BasicZoneProcessorTest_compareRulesBeforeYear;
25 class BasicZoneProcessorTest_findLatestPriorRule;
26 class BasicZoneProcessorTest_findZoneEra;
27 class BasicZoneProcessorTest_init_primitives;
28 class BasicZoneProcessorTest_init;
29 class BasicZoneProcessorTest_setZoneKey;
30 class BasicZoneProcessorTest_createAbbreviation;
31 class BasicZoneProcessorTest_calcRuleOffsetMinutes;
58 template <
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
112 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
114 if (
sizeof(acetime_t) ==
sizeof(
int)) {
120 logging::printf(
"; abbrev: %s",
abbrev);
121 if (!
rule.isNull()) {
122 logging::printf(
"; r.fromYear: %d",
rule.fromYearTiny());
123 logging::printf(
"; r.toYear: %d",
rule.toYearTiny());
124 logging::printf(
"; r.inMonth: %d",
rule.inMonth());
125 logging::printf(
"; r.onDayOfMonth: %d",
rule.onDayOfMonth());
127 logging::printf(
"\n");
133 inline int8_t compareYearMonth(int8_t aYear, uint8_t aMonth,
134 int8_t bYear, uint8_t bMonth) {
135 if (aYear < bYear)
return -1;
136 if (aYear > bYear)
return 1;
137 if (aMonth < bMonth)
return -1;
138 if (aMonth > bMonth)
return 1;
201 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
207 uint32_t
getZoneId()
const override {
return mZoneInfoBroker.zoneId(); }
210 const Transition* transition = getTransition(epochSeconds);
211 int16_t minutes = (transition)
217 const Transition* transition = getTransition(epochSeconds);
218 int16_t minutes = (transition)
223 const char*
getAbbrev(acetime_t epochSeconds)
const override {
224 const Transition* transition = getTransition(epochSeconds);
225 return (transition) ? transition->
abbrev :
"";
279 if (offset1 == offset2) {
283 acetime_t epochSeconds;
285 if (epochSeconds1 > epochSeconds2) {
286 epochSeconds = epochSeconds1;
289 epochSeconds = epochSeconds2;
302 mZoneInfoBroker.printNameTo(printer);
306 mZoneInfoBroker.printShortNameTo(printer);
310 if (mZoneInfoBroker.equals(zoneKey))
return;
312 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
319 return mZoneInfoBroker.equals(zoneKey);
324 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
326 logging::printf(
"*not initialized*\n");
329 logging::printf(
"mYearTiny: %d\n", mYearTiny);
330 logging::printf(
"mNumTransitions: %d\n", mNumTransitions);
331 for (
int i = 0; i < mNumTransitions; i++) {
332 logging::printf(
"mT[%d]=", i);
333 mTransitions[i].
log();
338 void setBrokerFactory(
const BF* brokerFactory) {
339 mBrokerFactory = brokerFactory;
352 const BF* brokerFactory,
356 mBrokerFactory(brokerFactory)
362 friend class ::BasicZoneProcessorTest_priorYearOfRule;
363 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
364 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
365 friend class ::BasicZoneProcessorTest_findZoneEra;
366 friend class ::BasicZoneProcessorTest_init_primitives;
367 friend class ::BasicZoneProcessorTest_init;
368 friend class ::BasicZoneProcessorTest_setZoneKey;
369 friend class ::BasicZoneProcessorTest_createAbbreviation;
370 friend class ::BasicZoneProcessorTest_calcRuleOffsetMinutes;
382 static const uint8_t kMaxCacheEntries = 5;
389 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
397 return mZoneInfoBroker.equals(
402 const Transition* getTransition(acetime_t epochSeconds)
const {
404 bool success = init(ld);
405 return (success) ? findMatch(epochSeconds) : nullptr;
436 bool init(
const LocalDate& ld)
const {
437 int8_t yearTiny = ld.yearTiny();
438 if (ld.month() == 1 && ld.day() == 1) {
441 if (isFilled(yearTiny)) {
442 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
443 logging::printf(
"init(): %d (using cached %d)\n",
444 ld.yearTiny(), yearTiny);
448 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
449 logging::printf(
"init(): %d (new year %d)\n",
450 ld.yearTiny(), yearTiny);
454 mYearTiny = yearTiny;
458 < mZoneInfoBroker.zoneContext()->startYear - 1
459 || mZoneInfoBroker.zoneContext()->untilYear
464 ZEB priorEra = addTransitionPriorToYear(yearTiny);
465 ZEB currentEra = addTransitionsForYear(yearTiny, priorEra);
466 addTransitionAfterYear(yearTiny, currentEra);
472 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
480 bool isFilled(int8_t yearTiny)
const {
481 return mIsFilled && (yearTiny == mYearTiny);
490 ZEB addTransitionPriorToYear(int8_t yearTiny)
const {
491 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
492 logging::printf(
"addTransitionPriorToYear(): %d\n", yearTiny);
495 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny - 1);
499 ZRB latest = findLatestPriorRule(
500 era.zonePolicy(), yearTiny);
501 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
502 logging::printf(
"addTransitionsPriorToYear(): adding latest prior ");
503 if (latest.isNull()) {
504 logging::printf(
"ZR(null)\n");
506 logging::printf(
"ZR[%d,%d]\n",
507 latest.fromYearTiny(), latest.toYearTiny());
510 addTransition(yearTiny - 1, 0 , era, latest);
520 static ZRB findLatestPriorRule(
const ZPB& zonePolicy, int8_t yearTiny) {
522 if (zonePolicy.isNull())
return latest;
524 uint8_t numRules = zonePolicy.numRules();
525 for (uint8_t i = 0; i < numRules; i++) {
526 const ZRB rule = zonePolicy.rule(i);
528 if (rule.fromYearTiny() < yearTiny) {
529 if ((latest.isNull()) ||
530 compareRulesBeforeYear(yearTiny, rule, latest) > 0) {
540 static int8_t compareRulesBeforeYear(int8_t yearTiny,
541 const ZRB& a,
const ZRB& b) {
542 return basic::compareYearMonth(
543 priorYearOfRule(yearTiny, a), a.inMonth(),
544 priorYearOfRule(yearTiny, b), b.inMonth());
555 static int8_t priorYearOfRule(int8_t yearTiny,
const ZRB& rule) {
556 if (rule.toYearTiny() < yearTiny) {
557 return rule.toYearTiny();
566 ZEB addTransitionsForYear(int8_t yearTiny,
const ZEB& priorEra)
const {
567 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
568 logging::printf(
"addTransitionsForYear(): %d\n", yearTiny);
571 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny);
575 const ZPB zonePolicy = era.zonePolicy();
576 if (zonePolicy.isNull()) {
577 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
578 logging::printf(
"addTransitionsForYear(): adding ZE.untilY=%d\n",
579 era.untilYearTiny());
581 addTransition(yearTiny, 0 , era, ZRB());
585 if (! era.equals(priorEra)) {
589 ZRB latestPrior = findLatestPriorRule(
590 era.zonePolicy(), yearTiny);
591 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
593 "addTransitionsForYear(): adding latest prior ");
594 if (latestPrior.isNull()) {
595 logging::printf(
"ZR(null)\n");
597 logging::printf(
"ZR[%d,%d]\n",
598 latestPrior.fromYearTiny(), latestPrior.toYearTiny());
601 addTransition(yearTiny, 1 , era, latestPrior);
607 uint8_t numRules = zonePolicy.numRules();
608 for (uint8_t i = 0; i < numRules; i++) {
609 const ZRB rule = zonePolicy.rule(i);
610 if ((rule.fromYearTiny() <= yearTiny) &&
611 (yearTiny <= rule.toYearTiny())) {
612 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
614 "addTransitionsForYear(): adding rule ");
616 logging::printf(
"ZR(null)\n");
618 logging::printf(
"ZR[%d,%d]\n",
619 rule.fromYearTiny(), rule.toYearTiny());
622 addTransition(yearTiny, 0 , era, rule);
630 void addTransitionAfterYear(int8_t yearTiny,
const ZEB& currentEra)
const {
631 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
632 logging::printf(
"addTransitionAfterYear(): %d\n", yearTiny);
635 const ZEB eraAfter = findZoneEra(mZoneInfoBroker, yearTiny + 1);
640 if (currentEra.equals(eraAfter)) {
647 ZRB latest = findLatestPriorRule(eraAfter.zonePolicy(), yearTiny + 1);
648 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
650 "addTransitionsAfterYear(): adding latest prior ");
651 if (latest.isNull()) {
652 logging::printf(
"ZR(null)\n");
654 logging::printf(
"ZR[%d,%d]\n",
655 latest.fromYearTiny(), latest.toYearTiny());
658 addTransition(yearTiny + 1, 1 , eraAfter, latest);
684 void addTransition(int8_t yearTiny, uint8_t month,
const ZEB& era,
685 const ZRB& rule)
const {
703 if (mNumTransitions >= kMaxCacheEntries)
return;
706 mTransitions[mNumTransitions] = createTransition(
707 yearTiny, month, era, rule);
711 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
715 if (basic::compareYearMonth(left.yearTiny, left.month,
716 right.yearTiny, right.month) > 0) {
729 static Transition createTransition(int8_t yearTiny, uint8_t month,
730 const ZEB& era,
const ZRB& rule) {
731 int16_t deltaMinutes;
736 deltaMinutes = era.deltaMinutes();
739 mon = rule.inMonth();
740 deltaMinutes = rule.deltaMinutes();
741 letter = rule.letter();
747 int16_t offsetMinutes = era.offsetMinutes() + deltaMinutes;
766 static ZEB findZoneEra(
const ZIB& info, int8_t yearTiny) {
767 for (uint8_t i = 0; i < info.numEras(); i++) {
768 const ZEB era = info.era(i);
769 if (yearTiny < era.untilYearTiny())
return era;
772 return info.era(info.numEras() - 1);
782 void calcTransitions()
const {
783 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
784 logging::printf(
"calcTransitions():\n");
788 Transition* prevTransition = &mTransitions[0];
791 for (uint8_t i = 1; i < mNumTransitions; i++) {
795 if (transition.rule.isNull()) {
807 const int16_t prevOffsetMinutes = prevTransition->offsetMinutes;
811 transition.startEpochSeconds = startDateTime.toEpochSeconds();
819 const internal::MonthDay monthDay = internal::calcStartDayOfMonth(
820 year, transition.month, transition.rule.onDayOfWeek(),
821 transition.rule.onDayOfMonth());
825 const int16_t prevOffsetMinutes = calcRuleOffsetMinutes(
826 prevTransition->offsetMinutes,
827 transition.era.offsetMinutes(),
828 transition.rule.atTimeSuffix());
831 const uint16_t minutes = transition.rule.atTimeMinutes();
832 const uint8_t atHour = minutes / 60;
833 const uint8_t atMinute = minutes % 60;
835 year, monthDay.month, monthDay.day,
836 atHour, atMinute, 0 ,
838 transition.startEpochSeconds = startDateTime.toEpochSeconds();
841 prevTransition = &transition;
851 static int16_t calcRuleOffsetMinutes(int16_t prevEffectiveOffsetMinutes,
852 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
854 return prevEffectiveOffsetMinutes;
856 return currentBaseOffsetMinutes;
863 void calcAbbreviations()
const {
864 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
865 logging::printf(
"calcAbbreviations():\n");
868 for (uint8_t i = 0; i < mNumTransitions; i++) {
869 calcAbbreviation(&mTransitions[i]);
874 static void calcAbbreviation(
Transition* transition) {
877 internal::kAbbrevSize,
878 transition->era.format(),
879 transition->deltaMinutes,
880 transition->abbrev[0]);
931 static void createAbbreviation(
char* dest, uint8_t destSize,
932 const char* format, int16_t deltaMinutes,
char letter) {
934 if (strchr(format,
'%') !=
nullptr) {
936 if (letter ==
'\0') {
937 strncpy(dest, format, destSize - 1);
938 dest[destSize - 1] =
'\0';
940 ace_common::copyReplaceChar(dest, destSize, format,
'%',
941 letter ==
'-' ?
'\0' : letter);
945 const char* slashPos = strchr(format,
'/');
946 if (slashPos !=
nullptr) {
947 if (deltaMinutes == 0) {
948 uint8_t headLength = (slashPos - format);
949 if (headLength >= destSize) headLength = destSize - 1;
950 memcpy(dest, format, headLength);
951 dest[headLength] =
'\0';
953 uint8_t tailLength = strlen(slashPos+1);
954 if (tailLength >= destSize) tailLength = destSize - 1;
955 memcpy(dest, slashPos+1, tailLength);
956 dest[tailLength] =
'\0';
960 strncpy(dest, format, destSize - 1);
961 dest[destSize - 1] =
'\0';
967 const Transition* findMatch(acetime_t epochSeconds)
const {
969 for (uint8_t i = 0; i < mNumTransitions; i++) {
971 if (closestMatch ==
nullptr || m->startEpochSeconds <= epochSeconds) {
978 const BF* mBrokerFactory;
982 mutable bool mIsFilled =
false;
983 mutable uint8_t mNumTransitions = 0;
984 mutable Transition mTransitions[kMaxCacheEntries];
992 basic::BrokerFactory,
993 basic::ZoneInfoBroker,
994 basic::ZoneEraBroker,
995 basic::ZonePolicyBroker,
996 basic::ZoneRuleBroker> {
1004 basic::BrokerFactory,
1005 basic::ZoneInfoBroker,
1006 basic::ZoneEraBroker,
1007 basic::ZonePolicyBroker,
1008 basic::ZoneRuleBroker>(
1009 kTypeBasic, &mBrokerFactory, (uintptr_t) zoneInfo)