6 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_H
7 #define ACE_TIME_BASIC_ZONE_PROCESSOR_H
10 #include <AceCommon.h>
11 #include "../zoneinfo/infos.h"
13 #include "common/logging.h"
14 #include "TimeOffset.h"
15 #include "LocalDate.h"
16 #include "OffsetDateTime.h"
17 #include "ZoneProcessor.h"
19 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG
20 #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_initForLocalDate;
29 class BasicZoneProcessorTest_setZoneKey;
30 class BasicZoneProcessorTest_calcRuleOffsetMinutes;
61 typename D::ZoneEraBroker
era;
72 typename D::ZoneRuleBroker
rule;
107 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
115 logging::printf(
"; abbrev: %s",
abbrev);
116 if (!
rule.isNull()) {
117 logging::printf(
"; r.fromYear: %d",
rule.fromYear());
118 logging::printf(
"; r.toYear: %d",
rule.toYear());
119 logging::printf(
"; r.inMonth: %d",
rule.inMonth());
120 logging::printf(
"; r.onDayOfMonth: %d",
rule.onDayOfMonth());
122 logging::printf(
"\n");
128 inline int8_t compareYearMonth(int16_t aYear, uint8_t aMonth,
129 int16_t bYear, uint8_t bMonth) {
130 if (aYear < bYear)
return -1;
131 if (aYear > bYear)
return 1;
132 if (aMonth < bMonth)
return -1;
133 if (aMonth > bMonth)
return 1;
190 template <
typename D>
197 return ! mZoneInfoBroker.targetInfo().isNull();
201 return mZoneInfoBroker.zoneId();
235 bool success = initForLocalDate(ldt.
localDate());
236 if (!success)
return result;
241 if (result0.type == FindResult::kTypeNotFound)
return result;
243 result0.reqStdOffsetSeconds + result0.reqDstOffsetSeconds);
247 acetime_t epochSeconds1 = odt.toEpochSeconds();
249 if (result1.type == FindResult::kTypeNotFound)
return result;
251 result1.reqStdOffsetSeconds + result1.reqDstOffsetSeconds);
255 acetime_t epochSeconds2 = odt.toEpochSeconds();
257 if (result2.type == FindResult::kTypeNotFound)
return result;
259 result2.reqStdOffsetSeconds + result2.reqDstOffsetSeconds);
263 if (offset1 == offset2) {
273 if (epochSeconds1 > epochSeconds2) {
278 result.
type = FindResult::kTypeGap;
286 const Transition* transition = getTransition(epochSeconds);
287 if (!transition)
return result;
293 result.
type = FindResult::kTypeExact;
300 mZoneInfoBroker.printNameTo(printer);
304 mZoneInfoBroker.printShortNameTo(printer);
309 mZoneInfoBroker.targetInfo().printNameTo(printer);
314 if (! mZoneInfoStore)
return;
315 if (mZoneInfoBroker.equals(zoneKey))
return;
317 mZoneInfoBroker = mZoneInfoStore->createZoneInfoBroker(zoneKey);
323 return mZoneInfoBroker.equals(zoneKey);
328 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
329 logging::printf(
"BasicZoneProcessor:\n");
330 logging::printf(
" mEpochYear: %d\n",
mEpochYear);
331 logging::printf(
" mYear: %d\n",
mYear);
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();
347 mZoneInfoStore = zoneInfoStore;
365 const typename D::ZoneInfoStore* zoneInfoStore ,
369 mZoneInfoStore(zoneInfoStore)
375 friend class ::BasicZoneProcessorTest_priorYearOfRule;
376 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
377 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
378 friend class ::BasicZoneProcessorTest_findZoneEra;
379 friend class ::BasicZoneProcessorTest_init_primitives;
380 friend class ::BasicZoneProcessorTest_initForLocalDate;
381 friend class ::BasicZoneProcessorTest_setZoneKey;
382 friend class ::BasicZoneProcessorTest_calcRuleOffsetMinutes;
394 static const uint8_t kMaxCacheEntries = 5;
401 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
409 return mZoneInfoBroker.equals(
414 const Transition* getTransition(acetime_t epochSeconds)
const {
415 bool success = initForEpochSeconds(epochSeconds);
416 return (success) ? findMatch(epochSeconds) : nullptr;
447 bool initForLocalDate(
const LocalDate& ld)
const {
448 int16_t year = ld.year();
449 if (ld.month() == 1 && ld.day() == 1) {
459 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
460 logging::printf(
"initForLocalDate(): %d (new year %d)\n",
468 typename D::ZoneEraBroker priorEra = addTransitionPriorToYear(year);
469 typename D::ZoneEraBroker currentEra =
470 addTransitionsForYear(year, priorEra);
471 addTransitionAfterYear(year, currentEra);
475 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
487 bool initForEpochSeconds(acetime_t epochSeconds)
const {
489 return initForLocalDate(ld);
498 typename D::ZoneEraBroker addTransitionPriorToYear(int16_t year)
const {
499 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
500 logging::printf(
"addTransitionPriorToYear(): %d\n", year);
503 const typename D::ZoneEraBroker era =
504 findZoneEra(mZoneInfoBroker, year - 1);
508 typename D::ZoneRuleBroker latest =
509 findLatestPriorRule(era.zonePolicy(), year);
510 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
511 logging::printf(
"addTransitionsPriorToYear(): adding latest prior ");
512 if (latest.isNull()) {
513 logging::printf(
"ZR(null)\n");
515 logging::printf(
"ZR[%d,%d]\n", latest.fromYear(), latest.toYear());
518 addTransition(year - 1, 0 , era, latest);
528 static typename D::ZoneRuleBroker findLatestPriorRule(
529 const typename D::ZonePolicyBroker& zonePolicy, int16_t year) {
530 typename D::ZoneRuleBroker latest;
531 if (zonePolicy.isNull())
return latest;
533 uint8_t numRules = zonePolicy.numRules();
534 for (uint8_t i = 0; i < numRules; i++) {
535 const typename D::ZoneRuleBroker rule = zonePolicy.rule(i);
537 if (rule.fromYear() < year) {
538 if ((latest.isNull()) ||
539 compareRulesBeforeYear(year, rule, latest) > 0) {
549 static int8_t compareRulesBeforeYear(
551 const typename D::ZoneRuleBroker& a,
552 const typename D::ZoneRuleBroker& b) {
553 return basic::compareYearMonth(
554 priorYearOfRule(year, a), a.inMonth(),
555 priorYearOfRule(year, b), b.inMonth());
566 static int16_t priorYearOfRule(int16_t year,
567 const typename D::ZoneRuleBroker& rule) {
568 if (rule.toYear() < year) {
569 return rule.toYear();
578 typename D::ZoneEraBroker addTransitionsForYear(
579 int16_t year,
const typename D::ZoneEraBroker& priorEra)
const {
580 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
581 logging::printf(
"addTransitionsForYear(): %d\n", year);
584 const typename D::ZoneEraBroker era = findZoneEra(mZoneInfoBroker, year);
588 const typename D::ZonePolicyBroker zonePolicy = era.zonePolicy();
589 if (zonePolicy.isNull()) {
590 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
591 logging::printf(
"addTransitionsForYear(): adding ZE.untilY=%d\n",
594 addTransition(year, 0 , era,
typename D::ZoneRuleBroker());
598 if (! era.equals(priorEra)) {
602 typename D::ZoneRuleBroker latestPrior =
603 findLatestPriorRule(era.zonePolicy(), year);
604 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
606 "addTransitionsForYear(): adding latest prior ");
607 if (latestPrior.isNull()) {
608 logging::printf(
"ZR(null)\n");
610 logging::printf(
"ZR[%d,%d]\n",
611 latestPrior.fromYear(), latestPrior.toYear());
614 addTransition(year, 1 , era, latestPrior);
620 uint8_t numRules = zonePolicy.numRules();
621 for (uint8_t i = 0; i < numRules; i++) {
622 const typename D::ZoneRuleBroker rule = zonePolicy.rule(i);
623 if ((rule.fromYear() <= year) && (year <= rule.toYear())) {
624 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
626 "addTransitionsForYear(): adding rule ");
628 logging::printf(
"ZR(null)\n");
630 logging::printf(
"ZR[%d,%d]\n", rule.fromYear(), rule.toYear());
633 addTransition(year, 0 , era, rule);
641 void addTransitionAfterYear(
642 int16_t year,
const typename D::ZoneEraBroker& currentEra)
const {
643 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
644 logging::printf(
"addTransitionAfterYear(): %d\n", year);
647 const typename D::ZoneEraBroker eraAfter =
648 findZoneEra(mZoneInfoBroker, year + 1);
653 if (currentEra.equals(eraAfter)) {
660 typename D::ZoneRuleBroker latest =
661 findLatestPriorRule(eraAfter.zonePolicy(), year + 1);
662 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
664 "addTransitionsAfterYear(): adding latest prior ");
665 if (latest.isNull()) {
666 logging::printf(
"ZR(null)\n");
668 logging::printf(
"ZR[%d,%d]\n", latest.fromYear(), latest.toYear());
671 addTransition(year + 1, 1 , eraAfter, latest);
697 void addTransition(int16_t year, uint8_t month,
698 const typename D::ZoneEraBroker& era,
699 const typename D::ZoneRuleBroker& rule)
const {
717 if (mNumTransitions >= kMaxCacheEntries)
return;
727 mTransitions[mNumTransitions] = createTransition(year, month, era, rule);
731 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
735 if (basic::compareYearMonth(left.year, left.month,
736 right.year, right.month) > 0) {
752 const typename D::ZoneEraBroker& era,
753 const typename D::ZoneRuleBroker& rule) {
756 int16_t deltaMinutes;
760 deltaMinutes = era.deltaSeconds() / kSecPerMin;
761 transition.abbrev[0] =
'\0';
763 mon = rule.inMonth();
764 deltaMinutes = rule.deltaSeconds() / kSecPerMin;
765 ace_common::strncpy_T(
766 transition.abbrev, rule.letter(), kAbbrevSize - 1);
773 int16_t offsetMinutes = era.offsetSeconds() / kSecPerMin;
775 transition.era = era;
776 transition.rule = rule;
777 transition.startEpochSeconds = 0;
778 transition.offsetMinutes = offsetMinutes;
779 transition.deltaMinutes = deltaMinutes;
780 transition.year = year;
781 transition.month = mon;
789 static typename D::ZoneEraBroker findZoneEra(
790 const typename D::ZoneInfoBroker& info,
792 for (uint8_t i = 0; i < info.numEras(); i++) {
793 const typename D::ZoneEraBroker era = info.era(i);
794 if (year < era.untilYear())
return era;
797 return info.era(info.numEras() - 1);
807 void calcTransitions()
const {
808 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
809 logging::printf(
"calcTransitions():\n");
813 Transition* prevTransition = &mTransitions[0];
816 for (uint8_t i = 1; i < mNumTransitions; i++) {
818 const int16_t year = transition.
year;
820 if (transition.rule.isNull()) {
832 const int16_t prevTotalOffsetMinutes = prevTransition->offsetMinutes
833 + prevTransition->deltaMinutes;
837 transition.startEpochSeconds = startDateTime.toEpochSeconds();
845 const MonthDay monthDay = calcStartDayOfMonth(
846 year, transition.month, transition.rule.onDayOfWeek(),
847 transition.rule.onDayOfMonth());
851 const int16_t prevTotalOffsetMinutes = calcRuleOffsetMinutes(
852 prevTransition->offsetMinutes + prevTransition->deltaMinutes,
853 transition.era.offsetSeconds() / kSecPerMin,
854 transition.rule.atTimeSuffix());
857 const uint16_t minutes = transition.rule.atTimeSeconds() / 60;
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 prevTotalOffsetMinutes,
878 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
880 return prevTotalOffsetMinutes;
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++) {
899 transition->era.format(),
900 transition->offsetMinutes * kSecPerMin,
901 transition->deltaMinutes * kSecPerMin,
907 const Transition* findMatch(acetime_t epochSeconds)
const {
909 for (uint8_t i = 0; i < mNumTransitions; i++) {
911 if (closestMatch ==
nullptr || m->startEpochSeconds <= epochSeconds) {
919 static const int32_t kSecPerMin = 60;
921 const typename D::ZoneInfoStore* mZoneInfoStore;
922 typename D::ZoneInfoBroker mZoneInfoBroker;
924 mutable uint8_t mNumTransitions = 0;
925 mutable Transition mTransitions[kMaxCacheEntries];
940 kTypeBasic, &mZoneInfoStore, (uintptr_t) zoneInfo)
An implementation of ZoneProcessor that supports a subset of the zones containing in the TZ Database.
FindResult findByEpochSeconds(acetime_t epochSeconds) const override
Return the search results at given epochSeconds.
void log() const
Used only for debugging.
void setZoneInfoStore(const typename D::ZoneInfoStore *zoneInfoStore)
Set the zone info store at runtime.
basic::TransitionTemplate< D > Transition
Exposed only for testing purposes.
bool equalsZoneKey(uintptr_t zoneKey) const override
Return true if ZoneProcessor is associated with the given opaque zoneKey.
FindResult findByLocalDateTime(const LocalDateTime &ldt) const override
Return the search results at given LocalDateTime.
uint32_t getZoneId() const override
Return the unique stable zoneId.
void printShortNameTo(Print &printer) const override
Print a short human-readable identifier (e.g.
void printTargetNameTo(Print &printer) const override
Print the full identifier (e.g.
void printNameTo(Print &printer) const override
Print a human-readable identifier (e.g.
BasicZoneProcessorTemplate(uint8_t type, const typename D::ZoneInfoStore *zoneInfoStore, uintptr_t zoneKey)
Constructor.
void setZoneKey(uintptr_t zoneKey) override
Set the opaque zoneKey of this object to a new value, reseting any internally cached information.
bool isLink() const override
Return true if timezone is a Link entry pointing to a Zone entry.
A specific implementation of BasicZoneProcessorTemplate that uses ZoneXxxBrokers which read from zone...
static const uint8_t kTypeBasic
Unique TimeZone type identifier for BasicZoneProcessor.
static int16_t currentEpochYear()
Get the current epoch year.
Result of a search for transition at a specific epochSeconds or a specific LocalDateTime.
int32_t stdOffsetSeconds
STD offset of the resulting OffsetDateTime.
int32_t dstOffsetSeconds
DST offset of the resulting OffsetDateTime.
int32_t reqDstOffsetSeconds
DST offset of the Transition which matched the epochSeconds requested by findByEpochSeconds(),...
const char * abbrev
Pointer to the abbreviation stored in the transient Transition::abbrev variable.
int32_t reqStdOffsetSeconds
STD offset of the Transition which matched the epochSeconds requested by findByEpochSeconds(),...
uint8_t type
Result of the findByEpochSeconds() or findByLocalDateTime() search methods.
Class that holds the date-time as the components (year, month, day, hour, minute, second) without reg...
const LocalDate & localDate() const
Return the LocalDate.
acetime_t toEpochSeconds() const
Return seconds since the current AceTime epoch defined by Epoch::currentEpochYear().
static const int16_t kMaxYear
The largest year that is expected to be handled by LocalDate.
static const int16_t kMinYear
The smallest year that is expected to be handled by LocalDate.
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since the current epoch year given by currentEpochYear().
static const int16_t kInvalidYear
Sentinel year which indicates one or more of the following conditions:
static OffsetDateTime forLocalDateTimeAndOffset(const LocalDateTime &localDateTime, TimeOffset timeOffset)
Factory method from LocalDateTime and TimeOffset.
static OffsetDateTime forComponents(int16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, TimeOffset timeOffset, uint8_t fold=0)
Factory method using separated date, time, and UTC offset fields.
static TimeOffset forSeconds(int32_t seconds)
Create TimeOffset from seconds from 00:00.
static TimeOffset forMinutes(int16_t minutes)
Create TimeOffset from minutes from 00:00.
A storage object that creates an ZoneInfoBroker from a key that identifies the ZoneInfo.
Base interface for ZoneProcessor classes.
int16_t mYear
Year that was used to calculate the transitions in the current cache.
bool isFilled(int16_t year) const
Check if the Transition cache is filled for the given year and current epochYear.
int16_t mEpochYear
Epoch year that was used to calculate the transitions in the current cache.
Identifiers used by implementation code which need to be publically exported.
const uint8_t kAbbrevSize
Size of the c-string buffer needed to hold a time zone abbreviation.
int32_t acetime_t
Type for the number of seconds from epoch.
static const uint8_t kSuffixW
Represents 'w' or wall time.
static const uint8_t kSuffixS
Represents 's' or standard time.
Representation of a given time zone, implemented as an array of ZoneEra records.
Data structure that defines the start of a specific UTC offset as described by the matching ZoneEra a...
acetime_t startEpochSeconds
The calculated transition time of the given rule.
uint8_t month
Month of the transition.
D::ZoneRuleBroker rule
The Zone transition rule that matched for the the given year.
int16_t deltaMinutes
The deltaMinutes from "standard time" at the start of transition.
void log() const
Used only for debugging.
char abbrev[kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
int16_t offsetMinutes
The standard time offset minutes at the start of transition, not including DST offset.
int16_t year
Year of the Transition.
D::ZoneEraBroker era
The ZoneEra that matched the given year.