6 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_H 7 #define ACE_TIME_BASIC_ZONE_PROCESSOR_H 11 #include "internal/ZonePolicy.h" 12 #include "internal/ZoneInfo.h" 14 #include "common/logging.h" 15 #include "TimeOffset.h" 16 #include "LocalDate.h" 17 #include "OffsetDateTime.h" 18 #include "ZoneProcessor.h" 20 class BasicZoneProcessorTest_init_primitives;
21 class BasicZoneProcessorTest_init;
22 class BasicZoneProcessorTest_setZoneInfo;
23 class BasicZoneProcessorTest_createAbbreviation;
24 class BasicZoneProcessorTest_calcStartDayOfMonth;
25 class BasicZoneProcessorTest_calcRuleOffsetCode;
29 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
102 if (
sizeof(acetime_t) ==
sizeof(
int)) {
103 logging::println(
"startEpochSeconds: %d", startEpochSeconds);
105 logging::println(
"startEpochSeconds: %ld", startEpochSeconds);
107 logging::println(
"offsetCode: %d", offsetCode);
108 logging::println(
"abbrev: %s", abbrev);
109 if (rule.isNotNull()) {
110 logging::println(
"Rule.fromYear: %d", rule.fromYearTiny());
111 logging::println(
"Rule.toYear: %d", rule.toYearTiny());
112 logging::println(
"Rule.inMonth: %d", rule.inMonth());
113 logging::println(
"Rule.onDayOfMonth: %d", rule.onDayOfMonth());
185 mZoneInfo(zoneInfo) {}
189 return mZoneInfo.zoneInfo();
192 uint32_t
getZoneId()
const override {
return mZoneInfo.zoneId(); }
196 int8_t code = (transition)
203 int8_t code = (transition)
208 const char*
getAbbrev(acetime_t epochSeconds)
const override {
210 return (transition) ? transition->
abbrev :
"";
250 auto offset0 = getUtcOffset(epochSeconds0);
255 auto offset1 = getUtcOffset(epochSeconds1);
260 auto offset2 = getUtcOffset(epochSeconds2);
264 if (offset1.toOffsetCode() == offset2.toOffsetCode()) {
268 acetime_t epochSeconds;
270 if (epochSeconds1 > epochSeconds2) {
271 epochSeconds = epochSeconds1;
274 epochSeconds = epochSeconds2;
286 void printTo(Print& printer)
const override;
288 void printShortTo(Print& printer)
const override;
293 logging::println(
"*not initialized*");
296 logging::println(
"mYear: %d", mYear);
297 logging::println(
"mNumTransitions: %d", mNumTransitions);
298 logging::println(
"---- PrevTransition");
299 mPrevTransition.log();
300 for (
int i = 0; i < mNumTransitions; i++) {
301 logging::println(
"---- Transition: %d", i);
302 mTransitions[i].log();
325 uint8_t onDayOfWeek, int8_t onDayOfMonth) {
326 if (onDayOfWeek == 0)
return {month, (uint8_t) onDayOfMonth};
328 if (onDayOfMonth >= 0) {
331 if (onDayOfMonth == 0) {
332 onDayOfMonth = daysInMonth - 6;
336 uint8_t dayOfWeekShift = (onDayOfWeek - limitDate.dayOfWeek() + 7) % 7;
337 uint8_t day = (uint8_t) (onDayOfMonth + dayOfWeekShift);
338 if (day > daysInMonth) {
345 onDayOfMonth = -onDayOfMonth;
347 int8_t dayOfWeekShift = (limitDate.dayOfWeek() - onDayOfWeek + 7) % 7;
348 int8_t day = onDayOfMonth - dayOfWeekShift;
353 day += daysInPrevMonth;
355 return {month, (uint8_t) day};
360 friend class ::BasicZoneProcessorTest_init_primitives;
361 friend class ::BasicZoneProcessorTest_init;
362 friend class ::BasicZoneProcessorTest_setZoneInfo;
363 friend class ::BasicZoneProcessorTest_createAbbreviation;
364 friend class ::BasicZoneProcessorTest_calcStartDayOfMonth;
365 friend class ::BasicZoneProcessorTest_calcRuleOffsetCode;
367 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
371 static const uint8_t kMaxCacheEntries = 4;
378 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
390 void setZoneInfo(
const void* zoneInfo)
override {
391 if (mZoneInfo.zoneInfo() == zoneInfo)
return;
402 bool success = init(ld);
403 return (success) ? findMatch(epochSeconds) :
nullptr;
435 int16_t year = ld.
year();
436 if (ld.
month() == 1 && ld.
day() == 1) {
439 if (isFilled(year))
return true;
444 if (year < mZoneInfo.startYear() - 1 || mZoneInfo.untilYear() < year) {
448 addRulePriorToYear(year);
449 addRulesForYear(year);
458 bool isFilled(int16_t year)
const {
459 return mIsFilled && (year == mYear);
466 void addRulePriorToYear(int16_t year)
const {
468 int8_t priorYearTiny = yearTiny - 1;
478 if (zonePolicy.isNotNull()) {
482 uint8_t numRules = zonePolicy.numRules();
483 for (uint8_t i = 0; i < numRules; i++) {
486 if (rule.fromYearTiny() <
yearTiny) {
487 if ((latest.isNull()) || compareZoneRule(year, rule, latest) > 0) {
494 mPrevTransition = createTransition(era, latest, priorYearTiny);
509 offsetCode = era.offsetCode();
512 deltaCode = rule.deltaCode();
513 offsetCode = era.offsetCode() +
deltaCode;
514 letter = rule.letter();
527 static int8_t compareZoneRule(int16_t year,
529 int16_t aYear = effectiveRuleYear(year, a);
530 int16_t bYear = effectiveRuleYear(year, b);
531 if (aYear < bYear)
return -1;
532 if (aYear > bYear)
return 1;
533 if (a.inMonth() < b.inMonth())
return -1;
534 if (a.inMonth() > b.inMonth())
return 1;
542 static int16_t effectiveRuleYear(int16_t year,
548 if (rule.fromYearTiny() <
yearTiny) {
555 void addRulesForYear(int16_t year)
const {
561 if (zonePolicy.isNull()) {
570 uint8_t numRules = zonePolicy.numRules();
571 for (uint8_t i = 0; i < numRules; i++) {
573 if ((rule.fromYearTiny() <=
yearTiny) &&
574 (yearTiny <= rule.toYearTiny())) {
575 addRule(year, era, rule);
614 if (mNumTransitions >= kMaxCacheEntries)
return;
618 mTransitions[mNumTransitions] = createTransition(era, rule, yearTiny);
622 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
626 if ((left.
rule.isNotNull() && right.
rule.isNotNull() &&
627 left.
rule.inMonth() > right.
rule.inMonth())
628 || (left.
rule.isNotNull() && right.
rule.isNull())) {
642 for (uint8_t i = 0; i < mZoneInfo.numEras(); i++) {
647 return mZoneInfo.era(mZoneInfo.numEras() - 1);
662 for (uint8_t i = 0; i < mZoneInfo.numEras(); i++) {
667 return mZoneInfo.era(mZoneInfo.numEras() - 1);
677 void calcTransitions()
const {
679 mPrevTransition.startEpochSeconds = kMinEpochSeconds;
682 for (uint8_t i = 0; i < mNumTransitions; i++) {
686 if (transition.
rule.isNull()) {
698 const int8_t prevOffsetCode = prevTransition->
offsetCode;
711 year, transition.
rule.inMonth(), transition.
rule.onDayOfWeek(),
712 transition.
rule.onDayOfMonth());
716 const int8_t prevOffsetCode = calcRuleOffsetCode(
718 transition.
era.offsetCode(),
719 transition.
rule.atTimeModifier());
722 const uint8_t timeCode = transition.
rule.atTimeCode();
723 const uint8_t atHour = timeCode / 4;
724 const uint8_t atMinute = (timeCode % 4) * 15;
726 year, monthDay.month, monthDay.day,
727 atHour, atMinute, 0 ,
732 prevTransition = &transition;
742 static int8_t calcRuleOffsetCode(int8_t prevEffectiveOffsetCode,
743 int8_t currentBaseOffsetCode, uint8_t atModifier) {
744 if (atModifier ==
'w') {
745 return prevEffectiveOffsetCode;
746 }
else if (atModifier ==
's') {
747 return currentBaseOffsetCode;
754 void calcAbbreviations()
const {
755 calcAbbreviation(&mPrevTransition);
756 for (uint8_t i = 0; i < mNumTransitions; i++) {
757 calcAbbreviation(&mTransitions[i]);
766 transition->
era.format(),
806 static void createAbbreviation(
char* dest, uint8_t destSize,
807 const char* format, uint8_t
deltaCode,
char letter) {
809 if (deltaCode == 0 && letter ==
'\0') {
810 strncpy(dest, format, destSize);
811 dest[destSize - 1] =
'\0';
816 if (strchr(format,
'%') !=
nullptr) {
817 copyAndReplace(dest, destSize, format,
'%', letter);
820 const char* slashPos = strchr(format,
'/');
821 if (slashPos !=
nullptr) {
822 if (deltaCode == 0) {
823 uint8_t headLength = (slashPos - format);
824 if (headLength >= destSize) headLength = destSize - 1;
825 memcpy(dest, format, headLength);
826 dest[headLength] =
'\0';
828 uint8_t tailLength = strlen(slashPos+1);
829 if (tailLength >= destSize) tailLength = destSize - 1;
830 memcpy(dest, slashPos+1, tailLength);
831 dest[tailLength] =
'\0';
835 strncpy(dest, format, destSize);
836 dest[destSize - 1] =
'\0';
846 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
847 char oldChar,
char newChar) {
848 while (*src !=
'\0' && dstSize > 0) {
849 if (*src == oldChar) {
850 if (newChar ==
'-') {
873 for (uint8_t i = 0; i < mNumTransitions; i++) {
884 mutable int16_t mYear = 0;
885 mutable bool mIsFilled =
false;
886 mutable uint8_t mNumTransitions = 0;
Base interface for ZoneProcessor classes.
ZoneEraBroker era
The ZoneEra that matched the given year.
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
void log() const
Used only for debugging.
A cache of ZoneProcessors that provides a ZoneProcessor to the TimeZone upon request.
static const uint8_t kAbbrevSize
Longest abbreviation currently seems to be 5 characters (https://www.timeanddate.com/time/zones/) but...
char abbrev[kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
static OffsetDateTime forLocalDateTimeAndOffset(const LocalDateTime &localDateTime, TimeOffset timeOffset)
Factory method from LocalDateTime and TimeOffset.
BasicZoneProcessor(const basic::ZoneInfo *zoneInfo=nullptr)
Constructor.
Representation of a given time zone, implemented as an array of ZoneEra records.
The result of calcStartDayOfMonth().
virtual const void * getZoneInfo() const =0
Return the opaque zoneInfo.
uint32_t getZoneId() const override
Return the unique stable zoneId.
static OffsetDateTime forComponents(int16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, TimeOffset timeOffset)
Factory method using separated date, time, and UTC offset fields.
acetime_t startEpochSeconds
The calculated transition time of the given rule.
uint8_t day() const
Return the day of the month.
int8_t yearTiny
Year which applies to the ZoneEra or ZoneRule.
static OffsetDateTime forEpochSeconds(acetime_t epochSeconds, TimeOffset timeOffset)
Factory method.
ZoneRuleBroker rule
The Zone transition rule that matched for the the given year.
acetime_t toEpochSeconds() const
Return seconds since AceTime epoch 2000-01-01 00:00:00Z, after assuming that the date and time compon...
const LocalDate & localDate() const
Return the LocalDate.
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
const void * getZoneInfo() const override
Return the underlying ZoneInfo.
void log() const
Used only for debugging.
static const int8_t kErrorCode
Sentinel value that represents an error.
An implementation of ZoneProcessor that supports a subset of the zones containing in the TZ Database...
static LocalDate forEpochSeconds(acetime_t epochSeconds)
Factory method using the number of seconds since AceTime epoch of 2000-01-01.
static TimeOffset forOffsetCode(int8_t offsetCode)
Create TimeOffset from the offset code.
The classes provide a thin layer of indirection for accessing the zoneinfo files stored in the zonedb...
The date (year, month, day), time (hour, minute, second) and offset from UTC (timeOffset).
Data structure that defines the start of a specific UTC offset as described by the matching ZoneEra a...
OffsetDateTime getOffsetDateTime(const LocalDateTime &ldt) const override
acetime_t toEpochSeconds() const
Return seconds since AceTime epoch (2000-01-01 00:00:00Z), taking into account the offset zone...
A thin wrapper that represents a time offset from a reference point, usually 00:00 at UTC...
static LocalDate forComponents(int16_t year, uint8_t month, uint8_t day)
Factory method using separated year, month and day fields.
The date (year, month, day) representing the date without regards to time zone.
static const int16_t kEpochYear
Base year of epoch.
int8_t offsetCode
The total effective UTC offsetCode at the start of transition, including DST offset.
static uint8_t daysInMonth(int16_t year, uint8_t month)
Return the number of days in the current month.
int8_t deltaCode
The delta offsetCode from "standard time" at the start of transition.
uint8_t month() const
Return the month with January=1, December=12.
int16_t year() const
Return the full year instead of just the last 2 digits.
static basic::MonthDay calcStartDayOfMonth(int16_t year, uint8_t month, uint8_t onDayOfWeek, int8_t onDayOfMonth)
Calculate the actual (month, day) of the expresssion (onDayOfWeek >= onDayOfMonth) or (onDayOfWeek <=...
Data broker for accessing ZonePolicy in PROGMEM.
static OffsetDateTime forError()
Factory method that returns an instance whose isError() is true.
Class that holds the date-time as the components (year, month, day, hour, minute, second) without reg...
TimeOffset getUtcOffset(acetime_t epochSeconds) const override
Return the total UTC offset at epochSeconds, including DST offset.