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_createAbbreviation;
32 class BasicZoneProcessorTest_calcRuleOffsetMinutes;
59 template <
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
113 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
121 logging::printf(
"; abbrev: %s",
abbrev);
122 if (!
rule.isNull()) {
123 logging::printf(
"; r.fromYear: %d",
rule.fromYear());
124 logging::printf(
"; r.toYear: %d",
rule.toYear());
125 logging::printf(
"; r.inMonth: %d",
rule.inMonth());
126 logging::printf(
"; r.onDayOfMonth: %d",
rule.onDayOfMonth());
128 logging::printf(
"\n");
134 inline int8_t compareYearMonth(int16_t aYear, uint8_t aMonth,
135 int16_t bYear, uint8_t bMonth) {
136 if (aYear < bYear)
return -1;
137 if (aYear > bYear)
return 1;
138 if (aMonth < bMonth)
return -1;
139 if (aMonth > bMonth)
return 1;
202 template <
typename ZIS,
typename ZIB,
typename ZEB,
typename ZPB,
typename ZRB>
209 return ! mZoneInfoBroker.targetInfo().isNull();
213 return mZoneInfoBroker.zoneId();
247 bool success = initForLocalDate(ldt.
localDate());
248 if (!success)
return result;
253 if (result0.type == FindResult::kTypeNotFound)
return result;
255 result0.reqStdOffsetSeconds + result0.reqDstOffsetSeconds);
259 acetime_t epochSeconds1 = odt.toEpochSeconds();
261 if (result1.type == FindResult::kTypeNotFound)
return result;
263 result1.reqStdOffsetSeconds + result1.reqDstOffsetSeconds);
267 acetime_t epochSeconds2 = odt.toEpochSeconds();
269 if (result2.type == FindResult::kTypeNotFound)
return result;
271 result2.reqStdOffsetSeconds + result2.reqDstOffsetSeconds);
275 if (offset1 == offset2) {
285 if (epochSeconds1 > epochSeconds2) {
290 result.
type = FindResult::kTypeGap;
298 const Transition* transition = getTransition(epochSeconds);
299 if (!transition)
return result;
306 result.
type = FindResult::kTypeExact;
313 mZoneInfoBroker.printNameTo(printer);
317 mZoneInfoBroker.printShortNameTo(printer);
322 mZoneInfoBroker.targetInfo().printNameTo(printer);
327 if (! mZoneInfoStore)
return;
328 if (mZoneInfoBroker.equals(zoneKey))
return;
330 mZoneInfoBroker = mZoneInfoStore->createZoneInfoBroker(zoneKey);
336 return mZoneInfoBroker.equals(zoneKey);
341 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
342 logging::printf(
"BasicZoneProcessor:\n");
343 logging::printf(
" mEpochYear: %d\n",
mEpochYear);
344 logging::printf(
" mYear: %d\n",
mYear);
345 logging::printf(
" mNumTransitions: %d\n", mNumTransitions);
346 for (
int i = 0; i < mNumTransitions; i++) {
347 logging::printf(
" mT[%d]=", i);
348 mTransitions[i].
log();
360 mZoneInfoStore = zoneInfoStore;
377 const ZIS* zoneInfoStore ,
381 mZoneInfoStore(zoneInfoStore)
387 friend class ::BasicZoneProcessorTest_priorYearOfRule;
388 friend class ::BasicZoneProcessorTest_compareRulesBeforeYear;
389 friend class ::BasicZoneProcessorTest_findLatestPriorRule;
390 friend class ::BasicZoneProcessorTest_findZoneEra;
391 friend class ::BasicZoneProcessorTest_init_primitives;
392 friend class ::BasicZoneProcessorTest_initForLocalDate;
393 friend class ::BasicZoneProcessorTest_setZoneKey;
394 friend class ::BasicZoneProcessorTest_createAbbreviation;
395 friend class ::BasicZoneProcessorTest_calcRuleOffsetMinutes;
407 static const uint8_t kMaxCacheEntries = 5;
414 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
422 return mZoneInfoBroker.equals(
427 const Transition* getTransition(acetime_t epochSeconds)
const {
428 bool success = initForEpochSeconds(epochSeconds);
429 return (success) ? findMatch(epochSeconds) : nullptr;
460 bool initForLocalDate(
const LocalDate& ld)
const {
461 int16_t year = ld.year();
462 if (ld.month() == 1 && ld.day() == 1) {
472 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
473 logging::printf(
"initForLocalDate(): %d (new year %d)\n",
481 ZEB priorEra = addTransitionPriorToYear(year);
482 ZEB currentEra = addTransitionsForYear(year, priorEra);
483 addTransitionAfterYear(year, currentEra);
487 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
499 bool initForEpochSeconds(acetime_t epochSeconds)
const {
501 return initForLocalDate(ld);
510 ZEB addTransitionPriorToYear(int16_t year)
const {
511 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
512 logging::printf(
"addTransitionPriorToYear(): %d\n", year);
515 const ZEB era = findZoneEra(mZoneInfoBroker, year - 1);
519 ZRB latest = findLatestPriorRule(era.zonePolicy(), year);
520 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
521 logging::printf(
"addTransitionsPriorToYear(): adding latest prior ");
522 if (latest.isNull()) {
523 logging::printf(
"ZR(null)\n");
525 logging::printf(
"ZR[%d,%d]\n", latest.fromYear(), latest.toYear());
528 addTransition(year - 1, 0 , era, latest);
538 static ZRB findLatestPriorRule(
const ZPB& zonePolicy, int16_t year) {
540 if (zonePolicy.isNull())
return latest;
542 uint8_t numRules = zonePolicy.numRules();
543 for (uint8_t i = 0; i < numRules; i++) {
544 const ZRB rule = zonePolicy.rule(i);
546 if (rule.fromYear() < year) {
547 if ((latest.isNull()) ||
548 compareRulesBeforeYear(year, rule, latest) > 0) {
558 static int8_t compareRulesBeforeYear(
559 int16_t year,
const ZRB& a,
const ZRB& b) {
560 return basic::compareYearMonth(
561 priorYearOfRule(year, a), a.inMonth(),
562 priorYearOfRule(year, b), b.inMonth());
573 static int16_t priorYearOfRule(int16_t year,
const ZRB& rule) {
574 if (rule.toYear() < year) {
575 return rule.toYear();
584 ZEB addTransitionsForYear(int16_t year,
const ZEB& priorEra)
const {
585 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
586 logging::printf(
"addTransitionsForYear(): %d\n", year);
589 const ZEB era = findZoneEra(mZoneInfoBroker, year);
593 const ZPB zonePolicy = era.zonePolicy();
594 if (zonePolicy.isNull()) {
595 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
596 logging::printf(
"addTransitionsForYear(): adding ZE.untilY=%d\n",
599 addTransition(year, 0 , era, ZRB());
603 if (! era.equals(priorEra)) {
607 ZRB latestPrior = findLatestPriorRule(era.zonePolicy(), year);
608 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
610 "addTransitionsForYear(): adding latest prior ");
611 if (latestPrior.isNull()) {
612 logging::printf(
"ZR(null)\n");
614 logging::printf(
"ZR[%d,%d]\n",
615 latestPrior.fromYear(), latestPrior.toYear());
618 addTransition(year, 1 , era, latestPrior);
624 uint8_t numRules = zonePolicy.numRules();
625 for (uint8_t i = 0; i < numRules; i++) {
626 const ZRB rule = zonePolicy.rule(i);
627 if ((rule.fromYear() <= year) && (year <= rule.toYear())) {
628 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
630 "addTransitionsForYear(): adding rule ");
632 logging::printf(
"ZR(null)\n");
634 logging::printf(
"ZR[%d,%d]\n", rule.fromYear(), rule.toYear());
637 addTransition(year, 0 , era, rule);
645 void addTransitionAfterYear(int16_t year,
const ZEB& currentEra)
const {
646 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
647 logging::printf(
"addTransitionAfterYear(): %d\n", year);
650 const ZEB eraAfter = findZoneEra(mZoneInfoBroker, year + 1);
655 if (currentEra.equals(eraAfter)) {
662 ZRB latest = findLatestPriorRule(eraAfter.zonePolicy(), year + 1);
663 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
665 "addTransitionsAfterYear(): adding latest prior ");
666 if (latest.isNull()) {
667 logging::printf(
"ZR(null)\n");
669 logging::printf(
"ZR[%d,%d]\n", latest.fromYear(), latest.toYear());
672 addTransition(year + 1, 1 , eraAfter, latest);
698 void addTransition(int16_t year, uint8_t month,
const ZEB& era,
699 const ZRB& 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) {
749 static Transition createTransition(int16_t year, uint8_t month,
750 const ZEB& era,
const ZRB& rule) {
753 int16_t deltaMinutes;
757 deltaMinutes = era.deltaSeconds() / kSecPerMin;
758 transition.abbrev[0] =
'\0';
760 mon = rule.inMonth();
761 deltaMinutes = rule.deltaSeconds() / kSecPerMin;
762 ace_common::strncpy_T(
763 transition.abbrev, rule.letter(), internal::kAbbrevSize - 1);
764 transition.abbrev[internal::kAbbrevSize - 1] =
'\0';
770 int16_t offsetMinutes = era.offsetSeconds() / kSecPerMin + deltaMinutes;
772 transition.era = era;
773 transition.rule = rule;
774 transition.startEpochSeconds = 0;
775 transition.offsetMinutes = offsetMinutes;
776 transition.deltaMinutes = deltaMinutes;
777 transition.year = year;
778 transition.month = mon;
786 static ZEB findZoneEra(
const ZIB& info, int16_t year) {
787 for (uint8_t i = 0; i < info.numEras(); i++) {
788 const ZEB era = info.era(i);
789 if (year < era.untilYear())
return era;
792 return info.era(info.numEras() - 1);
802 void calcTransitions()
const {
803 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
804 logging::printf(
"calcTransitions():\n");
808 Transition* prevTransition = &mTransitions[0];
811 for (uint8_t i = 1; i < mNumTransitions; i++) {
813 const int16_t year = transition.
year;
815 if (transition.rule.isNull()) {
827 const int16_t prevOffsetMinutes = prevTransition->offsetMinutes;
831 transition.startEpochSeconds = startDateTime.toEpochSeconds();
839 const internal::MonthDay monthDay = internal::calcStartDayOfMonth(
840 year, transition.month, transition.rule.onDayOfWeek(),
841 transition.rule.onDayOfMonth());
845 const int16_t prevOffsetMinutes = calcRuleOffsetMinutes(
846 prevTransition->offsetMinutes,
847 transition.era.offsetSeconds() / kSecPerMin,
848 transition.rule.atTimeSuffix());
851 const uint16_t minutes = transition.rule.atTimeSeconds() / 60;
852 const uint8_t atHour = minutes / 60;
853 const uint8_t atMinute = minutes % 60;
855 year, monthDay.month, monthDay.day,
856 atHour, atMinute, 0 ,
858 transition.startEpochSeconds = startDateTime.toEpochSeconds();
861 prevTransition = &transition;
871 static int16_t calcRuleOffsetMinutes(int16_t prevEffectiveOffsetMinutes,
872 int16_t currentBaseOffsetMinutes, uint8_t atSuffix) {
874 return prevEffectiveOffsetMinutes;
876 return currentBaseOffsetMinutes;
883 void calcAbbreviations()
const {
884 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
885 logging::printf(
"calcAbbreviations():\n");
888 for (uint8_t i = 0; i < mNumTransitions; i++) {
889 calcAbbreviation(&mTransitions[i]);
894 static void calcAbbreviation(
Transition* transition) {
895 internal::createAbbreviation(
897 internal::kAbbrevSize,
898 transition->era.format(),
899 transition->deltaMinutes,
904 const Transition* findMatch(acetime_t epochSeconds)
const {
906 for (uint8_t i = 0; i < mNumTransitions; i++) {
908 if (closestMatch ==
nullptr || m->startEpochSeconds <= epochSeconds) {
916 static const int32_t kSecPerMin = 60;
918 const ZIS* mZoneInfoStore;
921 mutable uint8_t mNumTransitions = 0;
922 mutable Transition mTransitions[kMaxCacheEntries];
930 basic::ZoneInfoStore,
931 basic::ZoneInfoBroker,
932 basic::ZoneEraBroker,
933 basic::ZonePolicyBroker,
934 basic::ZoneRuleBroker> {
942 basic::ZoneInfoStore,
943 basic::ZoneInfoBroker,
944 basic::ZoneEraBroker,
945 basic::ZonePolicyBroker,
946 basic::ZoneRuleBroker>(
947 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 total effective UTC offset minutes at the start of transition, 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.