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 (mZoneInfoBroker.equals(zoneKey))
return;
333 mZoneInfoBroker = mBrokerFactory->createZoneInfoBroker(zoneKey);
340 return mZoneInfoBroker.equals(zoneKey);
345 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
347 logging::printf(
"*not initialized*\n");
350 logging::printf(
"mYearTiny: %d\n", mYearTiny);
351 logging::printf(
"mNumTransitions: %d\n", mNumTransitions);
352 for (
int i = 0; i < mNumTransitions; i++) {
353 logging::printf(
"mT[%d]=", i);
354 mTransitions[i].
log();
359 void setBrokerFactory(
const BF* brokerFactory) {
360 mBrokerFactory = brokerFactory;
373 const BF* brokerFactory,
377 mBrokerFactory(brokerFactory)
383 friend class ::BasicZoneProcessorTest_priorYearOfRule;
384 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
385 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
386 friend class ::BasicZoneProcessorTest_findZoneEra;
387 friend class ::BasicZoneProcessorTest_init_primitives;
388 friend class ::BasicZoneProcessorTest_init;
389 friend class ::BasicZoneProcessorTest_setZoneKey;
390 friend class ::BasicZoneProcessorTest_createAbbreviation;
391 friend class ::BasicZoneProcessorTest_calcRuleOffsetMinutes;
403 static const uint8_t kMaxCacheEntries = 5;
410 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
418 return mZoneInfoBroker.equals(
423 const Transition* getTransition(acetime_t epochSeconds)
const {
425 bool success = init(ld);
426 return (success) ? findMatch(epochSeconds) : nullptr;
457 bool init(
const LocalDate& ld)
const {
458 int8_t yearTiny = ld.yearTiny();
459 if (ld.month() == 1 && ld.day() == 1) {
462 if (isFilled(yearTiny)) {
463 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
464 logging::printf(
"init(): %d (using cached %d)\n",
465 ld.yearTiny(), yearTiny);
469 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
470 logging::printf(
"init(): %d (new year %d)\n",
471 ld.yearTiny(), yearTiny);
475 mYearTiny = yearTiny;
479 < mZoneInfoBroker.zoneContext()->startYear - 1
480 || mZoneInfoBroker.zoneContext()->untilYear
485 ZEB priorEra = addTransitionPriorToYear(yearTiny);
486 ZEB currentEra = addTransitionsForYear(yearTiny, priorEra);
487 addTransitionAfterYear(yearTiny, currentEra);
493 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
501 bool isFilled(int8_t yearTiny)
const {
502 return mIsFilled && (yearTiny == mYearTiny);
511 ZEB addTransitionPriorToYear(int8_t yearTiny)
const {
512 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
513 logging::printf(
"addTransitionPriorToYear(): %d\n", yearTiny);
516 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny - 1);
520 ZRB latest = findLatestPriorRule(
521 era.zonePolicy(), yearTiny);
522 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
523 logging::printf(
"addTransitionsPriorToYear(): adding latest prior ");
524 if (latest.isNull()) {
525 logging::printf(
"ZR(null)\n");
527 logging::printf(
"ZR[%d,%d]\n",
528 latest.fromYearTiny(), latest.toYearTiny());
531 addTransition(yearTiny - 1, 0 , era, latest);
541 static ZRB findLatestPriorRule(
const ZPB& zonePolicy, int8_t yearTiny) {
543 if (zonePolicy.isNull())
return latest;
545 uint8_t numRules = zonePolicy.numRules();
546 for (uint8_t i = 0; i < numRules; i++) {
547 const ZRB rule = zonePolicy.rule(i);
549 if (rule.fromYearTiny() < yearTiny) {
550 if ((latest.isNull()) ||
551 compareRulesBeforeYear(yearTiny, rule, latest) > 0) {
561 static int8_t compareRulesBeforeYear(int8_t yearTiny,
562 const ZRB& a,
const ZRB& b) {
563 return basic::compareYearMonth(
564 priorYearOfRule(yearTiny, a), a.inMonth(),
565 priorYearOfRule(yearTiny, b), b.inMonth());
576 static int8_t priorYearOfRule(int8_t yearTiny,
const ZRB& rule) {
577 if (rule.toYearTiny() < yearTiny) {
578 return rule.toYearTiny();
587 ZEB addTransitionsForYear(int8_t yearTiny,
const ZEB& priorEra)
const {
588 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
589 logging::printf(
"addTransitionsForYear(): %d\n", yearTiny);
592 const ZEB era = findZoneEra(mZoneInfoBroker, yearTiny);
596 const ZPB zonePolicy = era.zonePolicy();
597 if (zonePolicy.isNull()) {
598 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
599 logging::printf(
"addTransitionsForYear(): adding ZE.untilY=%d\n",
600 era.untilYearTiny());
602 addTransition(yearTiny, 0 , era, ZRB());
606 if (! era.equals(priorEra)) {
610 ZRB latestPrior = findLatestPriorRule(
611 era.zonePolicy(), yearTiny);
612 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
614 "addTransitionsForYear(): adding latest prior ");
615 if (latestPrior.isNull()) {
616 logging::printf(
"ZR(null)\n");
618 logging::printf(
"ZR[%d,%d]\n",
619 latestPrior.fromYearTiny(), latestPrior.toYearTiny());
622 addTransition(yearTiny, 1 , era, latestPrior);
628 uint8_t numRules = zonePolicy.numRules();
629 for (uint8_t i = 0; i < numRules; i++) {
630 const ZRB rule = zonePolicy.rule(i);
631 if ((rule.fromYearTiny() <= yearTiny) &&
632 (yearTiny <= rule.toYearTiny())) {
633 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
635 "addTransitionsForYear(): adding rule ");
637 logging::printf(
"ZR(null)\n");
639 logging::printf(
"ZR[%d,%d]\n",
640 rule.fromYearTiny(), rule.toYearTiny());
643 addTransition(yearTiny, 0 , era, rule);
651 void addTransitionAfterYear(int8_t yearTiny,
const ZEB& currentEra)
const {
652 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
653 logging::printf(
"addTransitionAfterYear(): %d\n", yearTiny);
656 const ZEB eraAfter = findZoneEra(mZoneInfoBroker, yearTiny + 1);
661 if (currentEra.equals(eraAfter)) {
668 ZRB latest = findLatestPriorRule(eraAfter.zonePolicy(), yearTiny + 1);
669 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
671 "addTransitionsAfterYear(): adding latest prior ");
672 if (latest.isNull()) {
673 logging::printf(
"ZR(null)\n");
675 logging::printf(
"ZR[%d,%d]\n",
676 latest.fromYearTiny(), latest.toYearTiny());
679 addTransition(yearTiny + 1, 1 , eraAfter, latest);
705 void addTransition(int8_t yearTiny, uint8_t month,
const ZEB& era,
706 const ZRB& rule)
const {
724 if (mNumTransitions >= kMaxCacheEntries)
return;
727 mTransitions[mNumTransitions] = createTransition(
728 yearTiny, month, era, rule);
732 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
736 if (basic::compareYearMonth(left.yearTiny, left.month,
737 right.yearTiny, right.month) > 0) {
750 static Transition createTransition(int8_t yearTiny, uint8_t month,
751 const ZEB& era,
const ZRB& rule) {
752 int16_t deltaMinutes;
757 deltaMinutes = era.deltaMinutes();
760 mon = rule.inMonth();
761 deltaMinutes = rule.deltaMinutes();
762 letter = rule.letter();
768 int16_t offsetMinutes = era.offsetMinutes() + deltaMinutes;
787 static ZEB findZoneEra(
const ZIB& info, int8_t yearTiny) {
788 for (uint8_t i = 0; i < info.numEras(); i++) {
789 const ZEB era = info.era(i);
790 if (yearTiny < era.untilYearTiny())
return era;
793 return info.era(info.numEras() - 1);
803 void calcTransitions()
const {
804 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
805 logging::printf(
"calcTransitions():\n");
809 Transition* prevTransition = &mTransitions[0];
812 for (uint8_t i = 1; i < mNumTransitions; i++) {
816 if (transition.rule.isNull()) {
828 const int16_t prevOffsetMinutes = prevTransition->offsetMinutes;
832 transition.startEpochSeconds = startDateTime.toEpochSeconds();
840 const internal::MonthDay monthDay = internal::calcStartDayOfMonth(
841 year, transition.month, transition.rule.onDayOfWeek(),
842 transition.rule.onDayOfMonth());
846 const int16_t prevOffsetMinutes = calcRuleOffsetMinutes(
847 prevTransition->offsetMinutes,
848 transition.era.offsetMinutes(),
849 transition.rule.atTimeSuffix());
852 const uint16_t minutes = transition.rule.atTimeMinutes();
853 const uint8_t atHour = minutes / 60;
854 const uint8_t atMinute = minutes % 60;
856 year, monthDay.month, monthDay.day,
857 atHour, atMinute, 0 ,
859 transition.startEpochSeconds = startDateTime.toEpochSeconds();
862 prevTransition = &transition;
872 static int16_t calcRuleOffsetMinutes(int16_t prevEffectiveOffsetMinutes,
873 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
875 return prevEffectiveOffsetMinutes;
877 return currentBaseOffsetMinutes;
884 void calcAbbreviations()
const {
885 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
886 logging::printf(
"calcAbbreviations():\n");
889 for (uint8_t i = 0; i < mNumTransitions; i++) {
890 calcAbbreviation(&mTransitions[i]);
895 static void calcAbbreviation(
Transition* transition) {
898 internal::kAbbrevSize,
899 transition->era.format(),
900 transition->deltaMinutes,
901 transition->abbrev[0]);
952 static void createAbbreviation(
char* dest, uint8_t destSize,
953 const char* format, int16_t deltaMinutes,
char letter) {
955 if (strchr(format,
'%') !=
nullptr) {
957 if (letter ==
'\0') {
958 strncpy(dest, format, destSize - 1);
959 dest[destSize - 1] =
'\0';
961 ace_common::copyReplaceChar(dest, destSize, format,
'%',
962 letter ==
'-' ?
'\0' : letter);
966 const char* slashPos = strchr(format,
'/');
967 if (slashPos !=
nullptr) {
968 if (deltaMinutes == 0) {
969 uint8_t headLength = (slashPos - format);
970 if (headLength >= destSize) headLength = destSize - 1;
971 memcpy(dest, format, headLength);
972 dest[headLength] =
'\0';
974 uint8_t tailLength = strlen(slashPos+1);
975 if (tailLength >= destSize) tailLength = destSize - 1;
976 memcpy(dest, slashPos+1, tailLength);
977 dest[tailLength] =
'\0';
981 strncpy(dest, format, destSize - 1);
982 dest[destSize - 1] =
'\0';
988 const Transition* findMatch(acetime_t epochSeconds)
const {
990 for (uint8_t i = 0; i < mNumTransitions; i++) {
992 if (closestMatch ==
nullptr || m->startEpochSeconds <= epochSeconds) {
999 const BF* mBrokerFactory;
1000 ZIB mZoneInfoBroker;
1003 mutable bool mIsFilled =
false;
1004 mutable uint8_t mNumTransitions = 0;
1005 mutable Transition mTransitions[kMaxCacheEntries];
1013 basic::BrokerFactory,
1014 basic::ZoneInfoBroker,
1015 basic::ZoneEraBroker,
1016 basic::ZonePolicyBroker,
1017 basic::ZoneRuleBroker> {
1025 basic::BrokerFactory,
1026 basic::ZoneInfoBroker,
1027 basic::ZoneEraBroker,
1028 basic::ZonePolicyBroker,
1029 basic::ZoneRuleBroker>(
1030 kTypeBasic, &mBrokerFactory, (uintptr_t) zoneInfo)