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"
12 #include "../zoneinfo/brokers.h"
14 #include "common/logging.h"
15 #include "TimeOffset.h"
16 #include "LocalDate.h"
17 #include "OffsetDateTime.h"
18 #include "ZoneProcessor.h"
20 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG
21 #define ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG 0
24 class BasicZoneProcessorTest_priorYearOfRule;
25 class BasicZoneProcessorTest_compareRulesBeforeYear;
26 class BasicZoneProcessorTest_findLatestPriorRule;
27 class BasicZoneProcessorTest_findZoneEra;
28 class BasicZoneProcessorTest_init_primitives;
29 class BasicZoneProcessorTest_initForLocalDate;
30 class BasicZoneProcessorTest_setZoneKey;
31 class BasicZoneProcessorTest_calcRuleOffsetMinutes;
58 template <
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
111 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
119 logging::printf(
"; abbrev: %s",
abbrev);
120 if (!
rule.isNull()) {
121 logging::printf(
"; r.fromYear: %d",
rule.fromYear());
122 logging::printf(
"; r.toYear: %d",
rule.toYear());
123 logging::printf(
"; r.inMonth: %d",
rule.inMonth());
124 logging::printf(
"; r.onDayOfMonth: %d",
rule.onDayOfMonth());
126 logging::printf(
"\n");
132 inline int8_t compareYearMonth(int16_t aYear, uint8_t aMonth,
133 int16_t bYear, uint8_t bMonth) {
134 if (aYear < bYear)
return -1;
135 if (aYear > bYear)
return 1;
136 if (aMonth < bMonth)
return -1;
137 if (aMonth > bMonth)
return 1;
200 template <
typename ZIS,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
207 return ! mZoneInfoBroker.targetInfo().isNull();
211 return mZoneInfoBroker.zoneId();
245 bool success = initForLocalDate(ldt.
localDate());
246 if (!success)
return result;
251 if (result0.type == FindResult::kTypeNotFound)
return result;
253 result0.reqStdOffsetSeconds + result0.reqDstOffsetSeconds);
257 acetime_t epochSeconds1 = odt.toEpochSeconds();
259 if (result1.type == FindResult::kTypeNotFound)
return result;
261 result1.reqStdOffsetSeconds + result1.reqDstOffsetSeconds);
265 acetime_t epochSeconds2 = odt.toEpochSeconds();
267 if (result2.type == FindResult::kTypeNotFound)
return result;
269 result2.reqStdOffsetSeconds + result2.reqDstOffsetSeconds);
273 if (offset1 == offset2) {
283 if (epochSeconds1 > epochSeconds2) {
288 result.
type = FindResult::kTypeGap;
296 const Transition* transition = getTransition(epochSeconds);
297 if (!transition)
return result;
303 result.
type = FindResult::kTypeExact;
310 mZoneInfoBroker.printNameTo(printer);
314 mZoneInfoBroker.printShortNameTo(printer);
319 mZoneInfoBroker.targetInfo().printNameTo(printer);
324 if (! mZoneInfoStore)
return;
325 if (mZoneInfoBroker.equals(zoneKey))
return;
327 mZoneInfoBroker = mZoneInfoStore->createZoneInfoBroker(zoneKey);
333 return mZoneInfoBroker.equals(zoneKey);
338 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
339 logging::printf(
"BasicZoneProcessor:\n");
340 logging::printf(
" mEpochYear: %d\n",
mEpochYear);
341 logging::printf(
" mYear: %d\n",
mYear);
342 logging::printf(
" mNumTransitions: %d\n", mNumTransitions);
343 for (
int i = 0; i < mNumTransitions; i++) {
344 logging::printf(
" mT[%d]=", i);
345 mTransitions[i].
log();
357 mZoneInfoStore = zoneInfoStore;
374 const ZIS* zoneInfoStore ,
378 mZoneInfoStore(zoneInfoStore)
384 friend class ::BasicZoneProcessorTest_priorYearOfRule;
385 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
386 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
387 friend class ::BasicZoneProcessorTest_findZoneEra;
388 friend class ::BasicZoneProcessorTest_init_primitives;
389 friend class ::BasicZoneProcessorTest_initForLocalDate;
390 friend class ::BasicZoneProcessorTest_setZoneKey;
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 {
424 bool success = initForEpochSeconds(epochSeconds);
425 return (success) ? findMatch(epochSeconds) : nullptr;
456 bool initForLocalDate(
const LocalDate& ld)
const {
457 int16_t year = ld.year();
458 if (ld.month() == 1 && ld.day() == 1) {
468 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
469 logging::printf(
"initForLocalDate(): %d (new year %d)\n",
477 ZEB priorEra = addTransitionPriorToYear(year);
478 ZEB currentEra = addTransitionsForYear(year, priorEra);
479 addTransitionAfterYear(year, currentEra);
483 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
495 bool initForEpochSeconds(acetime_t epochSeconds)
const {
497 return initForLocalDate(ld);
506 ZEB addTransitionPriorToYear(int16_t year)
const {
507 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
508 logging::printf(
"addTransitionPriorToYear(): %d\n", year);
511 const ZEB era = findZoneEra(mZoneInfoBroker, year - 1);
515 ZRB latest = findLatestPriorRule(era.zonePolicy(), year);
516 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
517 logging::printf(
"addTransitionsPriorToYear(): adding latest prior ");
518 if (latest.isNull()) {
519 logging::printf(
"ZR(null)\n");
521 logging::printf(
"ZR[%d,%d]\n", latest.fromYear(), latest.toYear());
524 addTransition(year - 1, 0 , era, latest);
534 static ZRB findLatestPriorRule(
const ZPB& zonePolicy, int16_t year) {
536 if (zonePolicy.isNull())
return latest;
538 uint8_t numRules = zonePolicy.numRules();
539 for (uint8_t i = 0; i < numRules; i++) {
540 const ZRB rule = zonePolicy.rule(i);
542 if (rule.fromYear() < year) {
543 if ((latest.isNull()) ||
544 compareRulesBeforeYear(year, rule, latest) > 0) {
554 static int8_t compareRulesBeforeYear(
555 int16_t year,
const ZRB& a,
const ZRB& b) {
556 return basic::compareYearMonth(
557 priorYearOfRule(year, a), a.inMonth(),
558 priorYearOfRule(year, b), b.inMonth());
569 static int16_t priorYearOfRule(int16_t year,
const ZRB& rule) {
570 if (rule.toYear() < year) {
571 return rule.toYear();
580 ZEB addTransitionsForYear(int16_t year,
const ZEB& priorEra)
const {
581 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
582 logging::printf(
"addTransitionsForYear(): %d\n", year);
585 const ZEB era = findZoneEra(mZoneInfoBroker, year);
589 const ZPB zonePolicy = era.zonePolicy();
590 if (zonePolicy.isNull()) {
591 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
592 logging::printf(
"addTransitionsForYear(): adding ZE.untilY=%d\n",
595 addTransition(year, 0 , era, ZRB());
599 if (! era.equals(priorEra)) {
603 ZRB latestPrior = 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 ZRB 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(int16_t year,
const ZEB& currentEra)
const {
642 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
643 logging::printf(
"addTransitionAfterYear(): %d\n", year);
646 const ZEB eraAfter = findZoneEra(mZoneInfoBroker, year + 1);
651 if (currentEra.equals(eraAfter)) {
658 ZRB latest = findLatestPriorRule(eraAfter.zonePolicy(), year + 1);
659 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
661 "addTransitionsAfterYear(): adding latest prior ");
662 if (latest.isNull()) {
663 logging::printf(
"ZR(null)\n");
665 logging::printf(
"ZR[%d,%d]\n", latest.fromYear(), latest.toYear());
668 addTransition(year + 1, 1 , eraAfter, latest);
694 void addTransition(int16_t year, uint8_t month,
const ZEB& era,
695 const ZRB& rule)
const {
713 if (mNumTransitions >= kMaxCacheEntries)
return;
723 mTransitions[mNumTransitions] = createTransition(year, month, era, rule);
727 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
731 if (basic::compareYearMonth(left.year, left.month,
732 right.year, right.month) > 0) {
745 static Transition createTransition(int16_t year, uint8_t month,
746 const ZEB& era,
const ZRB& rule) {
749 int16_t deltaMinutes;
753 deltaMinutes = era.deltaSeconds() / kSecPerMin;
754 transition.abbrev[0] =
'\0';
756 mon = rule.inMonth();
757 deltaMinutes = rule.deltaSeconds() / kSecPerMin;
758 ace_common::strncpy_T(
759 transition.abbrev, rule.letter(), internal::kAbbrevSize - 1);
760 transition.abbrev[internal::kAbbrevSize - 1] =
'\0';
766 int16_t offsetMinutes = era.offsetSeconds() / kSecPerMin;
768 transition.era = era;
769 transition.rule = rule;
770 transition.startEpochSeconds = 0;
771 transition.offsetMinutes = offsetMinutes;
772 transition.deltaMinutes = deltaMinutes;
773 transition.year = year;
774 transition.month = mon;
782 static ZEB findZoneEra(
const ZIB& info, int16_t year) {
783 for (uint8_t i = 0; i < info.numEras(); i++) {
784 const ZEB era = info.era(i);
785 if (year < era.untilYear())
return era;
788 return info.era(info.numEras() - 1);
798 void calcTransitions()
const {
799 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
800 logging::printf(
"calcTransitions():\n");
804 Transition* prevTransition = &mTransitions[0];
807 for (uint8_t i = 1; i < mNumTransitions; i++) {
809 const int16_t year = transition.
year;
811 if (transition.rule.isNull()) {
823 const int16_t prevTotalOffsetMinutes = prevTransition->offsetMinutes
824 + prevTransition->deltaMinutes;
828 transition.startEpochSeconds = startDateTime.toEpochSeconds();
836 const internal::MonthDay monthDay = internal::calcStartDayOfMonth(
837 year, transition.month, transition.rule.onDayOfWeek(),
838 transition.rule.onDayOfMonth());
842 const int16_t prevTotalOffsetMinutes = calcRuleOffsetMinutes(
843 prevTransition->offsetMinutes + prevTransition->deltaMinutes,
844 transition.era.offsetSeconds() / kSecPerMin,
845 transition.rule.atTimeSuffix());
848 const uint16_t minutes = transition.rule.atTimeSeconds() / 60;
849 const uint8_t atHour = minutes / 60;
850 const uint8_t atMinute = minutes % 60;
852 year, monthDay.month, monthDay.day,
853 atHour, atMinute, 0 ,
855 transition.startEpochSeconds = startDateTime.toEpochSeconds();
858 prevTransition = &transition;
868 static int16_t calcRuleOffsetMinutes(int16_t prevTotalOffsetMinutes,
869 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
871 return prevTotalOffsetMinutes;
873 return currentBaseOffsetMinutes;
880 void calcAbbreviations()
const {
881 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
882 logging::printf(
"calcAbbreviations():\n");
885 for (uint8_t i = 0; i < mNumTransitions; i++) {
887 internal::createAbbreviation(
889 internal::kAbbrevSize,
890 transition->era.format(),
891 transition->offsetMinutes * kSecPerMin,
892 transition->deltaMinutes * kSecPerMin,
898 const Transition* findMatch(acetime_t epochSeconds)
const {
900 for (uint8_t i = 0; i < mNumTransitions; i++) {
902 if (closestMatch ==
nullptr || m->startEpochSeconds <= epochSeconds) {
910 static const int32_t kSecPerMin = 60;
912 const ZIS* mZoneInfoStore;
915 mutable uint8_t mNumTransitions = 0;
916 mutable Transition mTransitions[kMaxCacheEntries];
924 basic::ZoneInfoStore,
925 basic::ZoneInfoBroker,
926 basic::ZoneEraBroker,
927 basic::ZonePolicyBroker,
928 basic::ZoneRuleBroker> {
936 basic::ZoneInfoStore,
937 basic::ZoneInfoBroker,
938 basic::ZoneEraBroker,
939 basic::ZonePolicyBroker,
940 basic::ZoneRuleBroker>(
941 kTypeBasic, &mZoneInfoStore, (uintptr_t) zoneInfo)
An implementation of ZoneProcessor that supports a subset of the zones containing in the TZ Database.
bool isLink() const override
Return true if timezone is a Link entry pointing to a Zone entry.
basic::TransitionTemplate< ZIB, ZEB, ZPB, ZRB > Transition
Exposed only for testing purposes.
void setZoneKey(uintptr_t zoneKey) override
Set the opaque zoneKey of this object to a new value, reseting any internally cached information.
void printNameTo(Print &printer) const override
Print a human-readable identifier (e.g.
FindResult findByEpochSeconds(acetime_t epochSeconds) const override
Return the search results at given epochSeconds.
void setZoneInfoStore(const ZIS *zoneInfoStore)
Set the zone info store at runtime.
void log() const
Used only for debugging.
BasicZoneProcessorTemplate(uint8_t type, const ZIS *zoneInfoStore, uintptr_t zoneKey)
Constructor.
void printShortNameTo(Print &printer) const override
Print a short human-readable identifier (e.g.
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 printTargetNameTo(Print &printer) const override
Print the full identifier (e.g.
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.
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.
int32_t acetime_t
Type for the number of seconds from epoch.
Data structure that defines the start of a specific UTC offset as described by the matching ZoneEra a...
uint8_t month
Month of the transition.
acetime_t startEpochSeconds
The calculated transition time of the given rule.
ZRB rule
The Zone transition rule that matched for the the given year.
ZEB era
The ZoneEra that matched the given year.
void log() const
Used only for debugging.
int16_t year
Year of the Transition.
int16_t offsetMinutes
The standard time offset minutes at the start of transition, not including DST offset.
char abbrev[internal::kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
int16_t deltaMinutes
The deltaMinutes from "standard time" at the start of transition.
static const uint8_t kSuffixS
Represents 's' or standard time.
static const uint8_t kSuffixW
Represents 'w' or wall time.
Representation of a given time zone, implemented as an array of ZoneEra records.