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 bool isLink()
const override {
return mZoneInfoBroker.isLink(); }
211 uint32_t
getZoneId(
bool followLink =
false)
const override {
212 ZIB zib = (
isLink() && followLink)
213 ? mZoneInfoBroker.targetZoneInfo()
219 const Transition* transition = getTransition(epochSeconds);
220 int16_t minutes = (transition)
226 const Transition* transition = getTransition(epochSeconds);
227 int16_t minutes = (transition)
232 const char*
getAbbrev(acetime_t epochSeconds)
const override {
233 const Transition* transition = getTransition(epochSeconds);
234 return (transition) ? transition->
abbrev :
"";
288 if (offset1 == offset2) {
292 acetime_t epochSeconds;
294 if (epochSeconds1 > epochSeconds2) {
295 epochSeconds = epochSeconds1;
298 epochSeconds = epochSeconds2;
315 void printNameTo(Print& printer,
bool followLink =
false)
const override {
316 ZIB zib = (
isLink() && followLink)
317 ? mZoneInfoBroker.targetZoneInfo()
319 zib.printNameTo(printer);
324 ZIB zib = (
isLink() && followLink)
325 ? mZoneInfoBroker.targetZoneInfo()
327 zib.printShortNameTo(printer);
331 if (! mBrokerFactory)
return;
332 if (mZoneInfoBroker.equals(zoneKey))
return;
334 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
342 return mZoneInfoBroker.equals(zoneKey);
347 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
349 logging::printf(
"*not initialized*\n");
352 logging::printf(
"mYearTiny: %d\n", mYearTiny);
353 logging::printf(
"mNumTransitions: %d\n", mNumTransitions);
354 for (
int i = 0; i < mNumTransitions; i++) {
355 logging::printf(
"mT[%d]=", i);
356 mTransitions[i].
log();
368 mBrokerFactory = brokerFactory;
383 const BF* brokerFactory ,
387 mBrokerFactory(brokerFactory)
393 friend class ::BasicZoneProcessorTest_priorYearOfRule;
394 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
395 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
396 friend class ::BasicZoneProcessorTest_findZoneEra;
397 friend class ::BasicZoneProcessorTest_init_primitives;
398 friend class ::BasicZoneProcessorTest_init;
399 friend class ::BasicZoneProcessorTest_setZoneKey;
400 friend class ::BasicZoneProcessorTest_createAbbreviation;
401 friend class ::BasicZoneProcessorTest_calcRuleOffsetMinutes;
413 static const uint8_t kMaxCacheEntries = 5;
420 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
428 return mZoneInfoBroker.equals(
433 const Transition* getTransition(acetime_t epochSeconds)
const {
435 bool success = init(ld);
436 return (success) ? findMatch(epochSeconds) : nullptr;
467 bool init(
const LocalDate& ld)
const {
468 int8_t yearTiny = ld.yearTiny();
469 if (ld.month() == 1 && ld.day() == 1) {
472 if (isFilled(yearTiny)) {
473 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
474 logging::printf(
"init(): %d (using cached %d)\n",
475 ld.yearTiny(), yearTiny);
479 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
480 logging::printf(
"init(): %d (new year %d)\n",
481 ld.yearTiny(), yearTiny);
485 mYearTiny = yearTiny;
489 < mZoneInfoBroker.zoneContext()->startYear - 1
490 || mZoneInfoBroker.zoneContext()->untilYear
495 ZEB priorEra = addTransitionPriorToYear(yearTiny);
496 ZEB currentEra = addTransitionsForYear(yearTiny, priorEra);
497 addTransitionAfterYear(yearTiny, currentEra);
503 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
511 bool isFilled(int8_t yearTiny)
const {
512 return mIsFilled && (yearTiny == mYearTiny);
521 ZEB addTransitionPriorToYear(int8_t yearTiny)
const {
522 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
523 logging::printf(
"addTransitionPriorToYear(): %d\n", yearTiny);
526 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny - 1);
530 ZRB latest = findLatestPriorRule(
531 era.zonePolicy(), yearTiny);
532 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
533 logging::printf(
"addTransitionsPriorToYear(): adding latest prior ");
534 if (latest.isNull()) {
535 logging::printf(
"ZR(null)\n");
537 logging::printf(
"ZR[%d,%d]\n",
538 latest.fromYearTiny(), latest.toYearTiny());
541 addTransition(yearTiny - 1, 0 , era, latest);
551 static ZRB findLatestPriorRule(
const ZPB& zonePolicy, int8_t yearTiny) {
553 if (zonePolicy.isNull())
return latest;
555 uint8_t numRules = zonePolicy.numRules();
556 for (uint8_t i = 0; i < numRules; i++) {
557 const ZRB rule = zonePolicy.rule(i);
559 if (rule.fromYearTiny() < yearTiny) {
560 if ((latest.isNull()) ||
561 compareRulesBeforeYear(yearTiny, rule, latest) > 0) {
571 static int8_t compareRulesBeforeYear(int8_t yearTiny,
572 const ZRB& a,
const ZRB& b) {
573 return basic::compareYearMonth(
574 priorYearOfRule(yearTiny, a), a.inMonth(),
575 priorYearOfRule(yearTiny, b), b.inMonth());
586 static int8_t priorYearOfRule(int8_t yearTiny,
const ZRB& rule) {
587 if (rule.toYearTiny() < yearTiny) {
588 return rule.toYearTiny();
597 ZEB addTransitionsForYear(int8_t yearTiny,
const ZEB& priorEra)
const {
598 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
599 logging::printf(
"addTransitionsForYear(): %d\n", yearTiny);
602 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny);
606 const ZPB zonePolicy = era.zonePolicy();
607 if (zonePolicy.isNull()) {
608 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
609 logging::printf(
"addTransitionsForYear(): adding ZE.untilY=%d\n",
610 era.untilYearTiny());
612 addTransition(yearTiny, 0 , era, ZRB());
616 if (! era.equals(priorEra)) {
620 ZRB latestPrior = findLatestPriorRule(
621 era.zonePolicy(), yearTiny);
622 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
624 "addTransitionsForYear(): adding latest prior ");
625 if (latestPrior.isNull()) {
626 logging::printf(
"ZR(null)\n");
628 logging::printf(
"ZR[%d,%d]\n",
629 latestPrior.fromYearTiny(), latestPrior.toYearTiny());
632 addTransition(yearTiny, 1 , era, latestPrior);
638 uint8_t numRules = zonePolicy.numRules();
639 for (uint8_t i = 0; i < numRules; i++) {
640 const ZRB rule = zonePolicy.rule(i);
641 if ((rule.fromYearTiny() <= yearTiny) &&
642 (yearTiny <= rule.toYearTiny())) {
643 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
645 "addTransitionsForYear(): adding rule ");
647 logging::printf(
"ZR(null)\n");
649 logging::printf(
"ZR[%d,%d]\n",
650 rule.fromYearTiny(), rule.toYearTiny());
653 addTransition(yearTiny, 0 , era, rule);
661 void addTransitionAfterYear(int8_t yearTiny,
const ZEB& currentEra)
const {
662 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
663 logging::printf(
"addTransitionAfterYear(): %d\n", yearTiny);
666 const ZEB eraAfter = findZoneEra(mZoneInfoBroker, yearTiny + 1);
671 if (currentEra.equals(eraAfter)) {
678 ZRB latest = findLatestPriorRule(eraAfter.zonePolicy(), yearTiny + 1);
679 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
681 "addTransitionsAfterYear(): adding latest prior ");
682 if (latest.isNull()) {
683 logging::printf(
"ZR(null)\n");
685 logging::printf(
"ZR[%d,%d]\n",
686 latest.fromYearTiny(), latest.toYearTiny());
689 addTransition(yearTiny + 1, 1 , eraAfter, latest);
715 void addTransition(int8_t yearTiny, uint8_t month,
const ZEB& era,
716 const ZRB& rule)
const {
734 if (mNumTransitions >= kMaxCacheEntries)
return;
737 mTransitions[mNumTransitions] = createTransition(
738 yearTiny, month, era, rule);
742 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
746 if (basic::compareYearMonth(left.yearTiny, left.month,
747 right.yearTiny, right.month) > 0) {
760 static Transition createTransition(int8_t yearTiny, uint8_t month,
761 const ZEB& era,
const ZRB& rule) {
762 int16_t deltaMinutes;
767 deltaMinutes = era.deltaMinutes();
770 mon = rule.inMonth();
771 deltaMinutes = rule.deltaMinutes();
772 letter = rule.letter();
778 int16_t offsetMinutes = era.offsetMinutes() + deltaMinutes;
797 static ZEB findZoneEra(
const ZIB& info, int8_t yearTiny) {
798 for (uint8_t i = 0; i < info.numEras(); i++) {
799 const ZEB era = info.era(i);
800 if (yearTiny < era.untilYearTiny())
return era;
803 return info.era(info.numEras() - 1);
813 void calcTransitions()
const {
814 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
815 logging::printf(
"calcTransitions():\n");
819 Transition* prevTransition = &mTransitions[0];
822 for (uint8_t i = 1; i < mNumTransitions; i++) {
826 if (transition.rule.isNull()) {
838 const int16_t prevOffsetMinutes = prevTransition->offsetMinutes;
842 transition.startEpochSeconds = startDateTime.toEpochSeconds();
850 const internal::MonthDay monthDay = internal::calcStartDayOfMonth(
851 year, transition.month, transition.rule.onDayOfWeek(),
852 transition.rule.onDayOfMonth());
856 const int16_t prevOffsetMinutes = calcRuleOffsetMinutes(
857 prevTransition->offsetMinutes,
858 transition.era.offsetMinutes(),
859 transition.rule.atTimeSuffix());
862 const uint16_t minutes = transition.rule.atTimeMinutes();
863 const uint8_t atHour = minutes / 60;
864 const uint8_t atMinute = minutes % 60;
866 year, monthDay.month, monthDay.day,
867 atHour, atMinute, 0 ,
869 transition.startEpochSeconds = startDateTime.toEpochSeconds();
872 prevTransition = &transition;
882 static int16_t calcRuleOffsetMinutes(int16_t prevEffectiveOffsetMinutes,
883 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
885 return prevEffectiveOffsetMinutes;
887 return currentBaseOffsetMinutes;
894 void calcAbbreviations()
const {
895 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
896 logging::printf(
"calcAbbreviations():\n");
899 for (uint8_t i = 0; i < mNumTransitions; i++) {
900 calcAbbreviation(&mTransitions[i]);
905 static void calcAbbreviation(
Transition* transition) {
908 internal::kAbbrevSize,
909 transition->era.format(),
910 transition->deltaMinutes,
911 transition->abbrev[0]);
962 static void createAbbreviation(
char* dest, uint8_t destSize,
963 const char* format, int16_t deltaMinutes,
char letter) {
965 if (strchr(format,
'%') !=
nullptr) {
967 if (letter ==
'\0') {
968 strncpy(dest, format, destSize - 1);
969 dest[destSize - 1] =
'\0';
971 ace_common::copyReplaceChar(dest, destSize, format,
'%',
972 letter ==
'-' ?
'\0' : letter);
976 const char* slashPos = strchr(format,
'/');
977 if (slashPos !=
nullptr) {
978 if (deltaMinutes == 0) {
979 uint8_t headLength = (slashPos - format);
980 if (headLength >= destSize) headLength = destSize - 1;
981 memcpy(dest, format, headLength);
982 dest[headLength] =
'\0';
984 uint8_t tailLength = strlen(slashPos+1);
985 if (tailLength >= destSize) tailLength = destSize - 1;
986 memcpy(dest, slashPos+1, tailLength);
987 dest[tailLength] =
'\0';
991 strncpy(dest, format, destSize - 1);
992 dest[destSize - 1] =
'\0';
998 const Transition* findMatch(acetime_t epochSeconds)
const {
1000 for (uint8_t i = 0; i < mNumTransitions; i++) {
1002 if (closestMatch ==
nullptr || m->startEpochSeconds <= epochSeconds) {
1006 return closestMatch;
1009 const BF* mBrokerFactory;
1010 ZIB mZoneInfoBroker;
1013 mutable bool mIsFilled =
false;
1014 mutable uint8_t mNumTransitions = 0;
1015 mutable Transition mTransitions[kMaxCacheEntries];
1023 basic::BrokerFactory,
1024 basic::ZoneInfoBroker,
1025 basic::ZoneEraBroker,
1026 basic::ZonePolicyBroker,
1027 basic::ZoneRuleBroker> {
1035 basic::BrokerFactory,
1036 basic::ZoneInfoBroker,
1037 basic::ZoneEraBroker,
1038 basic::ZonePolicyBroker,
1039 basic::ZoneRuleBroker>(
1040 kTypeBasic, &mBrokerFactory, (uintptr_t) zoneInfo)