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 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG
22 #define ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG 0
25 class BasicZoneProcessorTest_priorYearOfRule;
26 class BasicZoneProcessorTest_compareRulesBeforeYear;
27 class BasicZoneProcessorTest_findLatestPriorRule;
28 class BasicZoneProcessorTest_findZoneEra;
29 class BasicZoneProcessorTest_init_primitives;
30 class BasicZoneProcessorTest_init;
31 class BasicZoneProcessorTest_setZoneKey;
32 class BasicZoneProcessorTest_createAbbreviation;
33 class BasicZoneProcessorTest_calcRuleOffsetMinutes;
60 template <
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
114 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
116 if (
sizeof(acetime_t) ==
sizeof(
int)) {
122 logging::printf(
"; abbrev: %s",
abbrev);
123 if (!
rule.isNull()) {
124 logging::printf(
"; r.fromYear: %d",
rule.fromYearTiny());
125 logging::printf(
"; r.toYear: %d",
rule.toYearTiny());
126 logging::printf(
"; r.inMonth: %d",
rule.inMonth());
127 logging::printf(
"; r.onDayOfMonth: %d",
rule.onDayOfMonth());
129 logging::printf(
"\n");
135 inline int8_t compareYearMonth(int8_t aYear, uint8_t aMonth,
136 int8_t bYear, uint8_t bMonth) {
137 if (aYear < bYear)
return -1;
138 if (aYear > bYear)
return 1;
139 if (aMonth < bMonth)
return -1;
140 if (aMonth > bMonth)
return 1;
203 template <
typename BF,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
209 uint32_t
getZoneId()
const override {
return mZoneInfoBroker.zoneId(); }
212 const Transition* transition = getTransition(epochSeconds);
213 int16_t minutes = (transition)
219 const Transition* transition = getTransition(epochSeconds);
220 int16_t minutes = (transition)
225 const char*
getAbbrev(acetime_t epochSeconds)
const override {
226 const Transition* transition = getTransition(epochSeconds);
227 return (transition) ? transition->
abbrev :
"";
281 if (offset1 == offset2) {
285 acetime_t epochSeconds;
287 if (epochSeconds1 > epochSeconds2) {
288 epochSeconds = epochSeconds1;
291 epochSeconds = epochSeconds2;
304 mZoneInfoBroker.printNameTo(printer);
308 mZoneInfoBroker.printShortNameTo(printer);
312 if (mZoneInfoBroker.equals(zoneKey))
return;
314 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
321 return mZoneInfoBroker.equals(zoneKey);
326 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
328 logging::printf(
"*not initialized*\n");
331 logging::printf(
"mYearTiny: %d\n", mYearTiny);
332 logging::printf(
"mNumTransitions: %d\n", mNumTransitions);
333 for (
int i = 0; i < mNumTransitions; i++) {
334 logging::printf(
"mT[%d]=", i);
335 mTransitions[i].
log();
340 void setBrokerFactory(
const BF* brokerFactory) {
341 mBrokerFactory = brokerFactory;
354 const BF* brokerFactory,
358 mBrokerFactory(brokerFactory)
364 friend class ::BasicZoneProcessorTest_priorYearOfRule;
365 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
366 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
367 friend class ::BasicZoneProcessorTest_findZoneEra;
368 friend class ::BasicZoneProcessorTest_init_primitives;
369 friend class ::BasicZoneProcessorTest_init;
370 friend class ::BasicZoneProcessorTest_setZoneKey;
371 friend class ::BasicZoneProcessorTest_createAbbreviation;
372 friend class ::BasicZoneProcessorTest_calcRuleOffsetMinutes;
384 static const uint8_t kMaxCacheEntries = 5;
391 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
399 return mZoneInfoBroker.equals(
404 const Transition* getTransition(acetime_t epochSeconds)
const {
406 bool success = init(ld);
407 return (success) ? findMatch(epochSeconds) : nullptr;
438 bool init(
const LocalDate& ld)
const {
439 int8_t yearTiny = ld.yearTiny();
440 if (ld.month() == 1 && ld.day() == 1) {
443 if (isFilled(yearTiny)) {
444 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
445 logging::printf(
"init(): %d (using cached %d)\n",
446 ld.yearTiny(), yearTiny);
450 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
451 logging::printf(
"init(): %d (new year %d)\n",
452 ld.yearTiny(), yearTiny);
456 mYearTiny = yearTiny;
460 < mZoneInfoBroker.zoneContext()->startYear - 1
461 || mZoneInfoBroker.zoneContext()->untilYear
466 ZEB priorEra = addTransitionPriorToYear(yearTiny);
467 ZEB currentEra = addTransitionsForYear(yearTiny, priorEra);
468 addTransitionAfterYear(yearTiny, currentEra);
474 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
482 bool isFilled(int8_t yearTiny)
const {
483 return mIsFilled && (yearTiny == mYearTiny);
492 ZEB addTransitionPriorToYear(int8_t yearTiny)
const {
493 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
494 logging::printf(
"addTransitionPriorToYear(): %d\n", yearTiny);
497 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny - 1);
501 ZRB latest = findLatestPriorRule(
502 era.zonePolicy(), yearTiny);
503 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
504 logging::printf(
"addTransitionsPriorToYear(): adding latest prior ");
505 if (latest.isNull()) {
506 logging::printf(
"ZR(null)\n");
508 logging::printf(
"ZR[%d,%d]\n",
509 latest.fromYearTiny(), latest.toYearTiny());
512 addTransition(yearTiny - 1, 0 , era, latest);
522 static ZRB findLatestPriorRule(
const ZPB& zonePolicy, int8_t yearTiny) {
524 if (zonePolicy.isNull())
return latest;
526 uint8_t numRules = zonePolicy.numRules();
527 for (uint8_t i = 0; i < numRules; i++) {
528 const ZRB rule = zonePolicy.rule(i);
530 if (rule.fromYearTiny() < yearTiny) {
531 if ((latest.isNull()) ||
532 compareRulesBeforeYear(yearTiny, rule, latest) > 0) {
542 static int8_t compareRulesBeforeYear(int8_t yearTiny,
543 const ZRB& a,
const ZRB& b) {
544 return basic::compareYearMonth(
545 priorYearOfRule(yearTiny, a), a.inMonth(),
546 priorYearOfRule(yearTiny, b), b.inMonth());
557 static int8_t priorYearOfRule(int8_t yearTiny,
const ZRB& rule) {
558 if (rule.toYearTiny() < yearTiny) {
559 return rule.toYearTiny();
568 ZEB addTransitionsForYear(int8_t yearTiny,
const ZEB& priorEra)
const {
569 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
570 logging::printf(
"addTransitionsForYear(): %d\n", yearTiny);
573 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny);
577 const ZPB zonePolicy = era.zonePolicy();
578 if (zonePolicy.isNull()) {
579 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
580 logging::printf(
"addTransitionsForYear(): adding ZE.untilY=%d\n",
581 era.untilYearTiny());
583 addTransition(yearTiny, 0 , era, ZRB());
587 if (! era.equals(priorEra)) {
591 ZRB latestPrior = findLatestPriorRule(
592 era.zonePolicy(), yearTiny);
593 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
595 "addTransitionsForYear(): adding latest prior ");
596 if (latestPrior.isNull()) {
597 logging::printf(
"ZR(null)\n");
599 logging::printf(
"ZR[%d,%d]\n",
600 latestPrior.fromYearTiny(), latestPrior.toYearTiny());
603 addTransition(yearTiny, 1 , era, latestPrior);
609 uint8_t numRules = zonePolicy.numRules();
610 for (uint8_t i = 0; i < numRules; i++) {
611 const ZRB rule = zonePolicy.rule(i);
612 if ((rule.fromYearTiny() <= yearTiny) &&
613 (yearTiny <= rule.toYearTiny())) {
614 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
616 "addTransitionsForYear(): adding rule ");
618 logging::printf(
"ZR(null)\n");
620 logging::printf(
"ZR[%d,%d]\n",
621 rule.fromYearTiny(), rule.toYearTiny());
624 addTransition(yearTiny, 0 , era, rule);
632 void addTransitionAfterYear(int8_t yearTiny,
const ZEB& currentEra)
const {
633 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
634 logging::printf(
"addTransitionAfterYear(): %d\n", yearTiny);
637 const ZEB eraAfter = findZoneEra(mZoneInfoBroker, yearTiny + 1);
642 if (currentEra.equals(eraAfter)) {
649 ZRB latest = findLatestPriorRule(eraAfter.zonePolicy(), yearTiny + 1);
650 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
652 "addTransitionsAfterYear(): adding latest prior ");
653 if (latest.isNull()) {
654 logging::printf(
"ZR(null)\n");
656 logging::printf(
"ZR[%d,%d]\n",
657 latest.fromYearTiny(), latest.toYearTiny());
660 addTransition(yearTiny + 1, 1 , eraAfter, latest);
686 void addTransition(int8_t yearTiny, uint8_t month,
const ZEB& era,
687 const ZRB& rule)
const {
705 if (mNumTransitions >= kMaxCacheEntries)
return;
708 mTransitions[mNumTransitions] = createTransition(
709 yearTiny, month, era, rule);
713 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
717 if (basic::compareYearMonth(left.yearTiny, left.month,
718 right.yearTiny, right.month) > 0) {
731 static Transition createTransition(int8_t yearTiny, uint8_t month,
732 const ZEB& era,
const ZRB& rule) {
733 int16_t deltaMinutes;
738 deltaMinutes = era.deltaMinutes();
741 mon = rule.inMonth();
742 deltaMinutes = rule.deltaMinutes();
743 letter = rule.letter();
749 int16_t offsetMinutes = era.offsetMinutes() + deltaMinutes;
768 static ZEB findZoneEra(
const ZIB& info, int8_t yearTiny) {
769 for (uint8_t i = 0; i < info.numEras(); i++) {
770 const ZEB era = info.era(i);
771 if (yearTiny < era.untilYearTiny())
return era;
774 return info.era(info.numEras() - 1);
784 void calcTransitions()
const {
785 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
786 logging::printf(
"calcTransitions():\n");
790 Transition* prevTransition = &mTransitions[0];
793 for (uint8_t i = 1; i < mNumTransitions; i++) {
797 if (transition.rule.isNull()) {
809 const int16_t prevOffsetMinutes = prevTransition->offsetMinutes;
813 transition.startEpochSeconds = startDateTime.toEpochSeconds();
821 const internal::MonthDay monthDay = internal::calcStartDayOfMonth(
822 year, transition.month, transition.rule.onDayOfWeek(),
823 transition.rule.onDayOfMonth());
827 const int16_t prevOffsetMinutes = calcRuleOffsetMinutes(
828 prevTransition->offsetMinutes,
829 transition.era.offsetMinutes(),
830 transition.rule.atTimeSuffix());
833 const uint16_t minutes = transition.rule.atTimeMinutes();
834 const uint8_t atHour = minutes / 60;
835 const uint8_t atMinute = minutes % 60;
837 year, monthDay.month, monthDay.day,
838 atHour, atMinute, 0 ,
840 transition.startEpochSeconds = startDateTime.toEpochSeconds();
843 prevTransition = &transition;
853 static int16_t calcRuleOffsetMinutes(int16_t prevEffectiveOffsetMinutes,
854 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
856 return prevEffectiveOffsetMinutes;
858 return currentBaseOffsetMinutes;
865 void calcAbbreviations()
const {
866 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
867 logging::printf(
"calcAbbreviations():\n");
870 for (uint8_t i = 0; i < mNumTransitions; i++) {
871 calcAbbreviation(&mTransitions[i]);
876 static void calcAbbreviation(
Transition* transition) {
879 internal::kAbbrevSize,
880 transition->era.format(),
881 transition->deltaMinutes,
882 transition->abbrev[0]);
933 static void createAbbreviation(
char* dest, uint8_t destSize,
934 const char* format, int16_t deltaMinutes,
char letter) {
936 if (strchr(format,
'%') !=
nullptr) {
938 if (letter ==
'\0') {
939 strncpy(dest, format, destSize - 1);
940 dest[destSize - 1] =
'\0';
942 ace_common::copyReplaceChar(dest, destSize, format,
'%',
943 letter ==
'-' ?
'\0' : letter);
947 const char* slashPos = strchr(format,
'/');
948 if (slashPos !=
nullptr) {
949 if (deltaMinutes == 0) {
950 uint8_t headLength = (slashPos - format);
951 if (headLength >= destSize) headLength = destSize - 1;
952 memcpy(dest, format, headLength);
953 dest[headLength] =
'\0';
955 uint8_t tailLength = strlen(slashPos+1);
956 if (tailLength >= destSize) tailLength = destSize - 1;
957 memcpy(dest, slashPos+1, tailLength);
958 dest[tailLength] =
'\0';
962 strncpy(dest, format, destSize - 1);
963 dest[destSize - 1] =
'\0';
969 const Transition* findMatch(acetime_t epochSeconds)
const {
971 for (uint8_t i = 0; i < mNumTransitions; i++) {
973 if (closestMatch ==
nullptr || m->startEpochSeconds <= epochSeconds) {
980 const BF* mBrokerFactory;
984 mutable bool mIsFilled =
false;
985 mutable uint8_t mNumTransitions = 0;
986 mutable Transition mTransitions[kMaxCacheEntries];
994 basic::BrokerFactory,
995 basic::ZoneInfoBroker,
996 basic::ZoneEraBroker,
997 basic::ZonePolicyBroker,
998 basic::ZoneRuleBroker> {
1006 basic::BrokerFactory,
1007 basic::ZoneInfoBroker,
1008 basic::ZoneEraBroker,
1009 basic::ZonePolicyBroker,
1010 basic::ZoneRuleBroker>(
1011 kTypeBasic, &mBrokerFactory, (uintptr_t) zoneInfo)