6 #ifndef ACE_TIME_BASIC_ZONE_PROCESSOR_H 7 #define ACE_TIME_BASIC_ZONE_PROCESSOR_H 12 #include "internal/ZonePolicy.h" 13 #include "internal/ZoneInfo.h" 15 #include "common/logger.h" 16 #include "TimeOffset.h" 17 #include "LocalDate.h" 18 #include "OffsetDateTime.h" 19 #include "ZoneProcessor.h" 21 class BasicZoneProcessorTest_init_primitives;
22 class BasicZoneProcessorTest_init;
23 class BasicZoneProcessorTest_setZoneInfo;
24 class BasicZoneProcessorTest_createAbbreviation;
25 class BasicZoneProcessorTest_calcStartDayOfMonth;
26 class BasicZoneProcessorTest_calcRuleOffsetCode;
30 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
103 if (
sizeof(acetime_t) ==
sizeof(
int)) {
104 logging::println(
"startEpochSeconds: %d", startEpochSeconds);
106 logging::println(
"startEpochSeconds: %ld", startEpochSeconds);
108 logging::println(
"offsetCode: %d", offsetCode);
109 logging::println(
"abbrev: %s", abbrev);
110 if (rule.isNotNull()) {
111 logging::println(
"Rule.fromYear: %d", rule.fromYearTiny());
112 logging::println(
"Rule.toYear: %d", rule.toYearTiny());
113 logging::println(
"Rule.inMonth: %d", rule.inMonth());
114 logging::println(
"Rule.onDayOfMonth: %d", rule.onDayOfMonth());
186 mZoneInfo(zoneInfo) {}
190 return mZoneInfo.zoneInfo();
193 uint32_t
getZoneId()
const override {
return mZoneInfo.zoneId(); }
197 int8_t code = (transition)
204 int8_t code = (transition)
209 const char*
getAbbrev(acetime_t epochSeconds)
const override {
211 return (transition) ? transition->
abbrev :
"";
251 auto offset0 = getUtcOffset(epochSeconds0);
256 auto offset1 = getUtcOffset(epochSeconds1);
261 auto offset2 = getUtcOffset(epochSeconds2);
265 if (offset1.toOffsetCode() == offset2.toOffsetCode()) {
269 acetime_t epochSeconds;
271 if (epochSeconds1 > epochSeconds2) {
272 epochSeconds = epochSeconds1;
275 epochSeconds = epochSeconds2;
287 void printTo(Print& printer)
const override;
289 void printShortTo(Print& printer)
const override;
294 logging::println(
"*not initialized*");
297 logging::println(
"mYear: %d", mYear);
298 logging::println(
"mNumTransitions: %d", mNumTransitions);
299 logging::println(
"---- PrevTransition");
300 mPrevTransition.log();
301 for (
int i = 0; i < mNumTransitions; i++) {
302 logging::println(
"---- Transition: %d", i);
303 mTransitions[i].log();
326 uint8_t onDayOfWeek, int8_t onDayOfMonth) {
327 if (onDayOfWeek == 0)
return {month, (uint8_t) onDayOfMonth};
329 if (onDayOfMonth >= 0) {
332 if (onDayOfMonth == 0) {
333 onDayOfMonth = daysInMonth - 6;
337 uint8_t dayOfWeekShift = (onDayOfWeek - limitDate.dayOfWeek() + 7) % 7;
338 uint8_t day = (uint8_t) (onDayOfMonth + dayOfWeekShift);
339 if (day > daysInMonth) {
346 onDayOfMonth = -onDayOfMonth;
348 int8_t dayOfWeekShift = (limitDate.dayOfWeek() - onDayOfWeek + 7) % 7;
349 int8_t day = onDayOfMonth - dayOfWeekShift;
354 day += daysInPrevMonth;
356 return {month, (uint8_t) day};
361 friend class ::BasicZoneProcessorTest_init_primitives;
362 friend class ::BasicZoneProcessorTest_init;
363 friend class ::BasicZoneProcessorTest_setZoneInfo;
364 friend class ::BasicZoneProcessorTest_createAbbreviation;
365 friend class ::BasicZoneProcessorTest_calcStartDayOfMonth;
366 friend class ::BasicZoneProcessorTest_calcRuleOffsetCode;
368 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
372 static const uint8_t kMaxCacheEntries = 4;
379 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
391 void setZoneInfo(
const void* zoneInfo)
override {
392 if (mZoneInfo.zoneInfo() == zoneInfo)
return;
403 bool success = init(ld);
404 return (success) ? findMatch(epochSeconds) :
nullptr;
436 int16_t year = ld.
year();
437 if (ld.
month() == 1 && ld.
day() == 1) {
440 if (isFilled(year))
return true;
445 if (year < mZoneInfo.startYear() - 1 || mZoneInfo.untilYear() < year) {
449 addRulePriorToYear(year);
450 addRulesForYear(year);
459 bool isFilled(int16_t year)
const {
460 return mIsFilled && (year == mYear);
467 void addRulePriorToYear(int16_t year)
const {
469 int8_t priorYearTiny = yearTiny - 1;
479 if (zonePolicy.isNotNull()) {
483 uint8_t numRules = zonePolicy.numRules();
484 for (uint8_t i = 0; i < numRules; i++) {
487 if (rule.fromYearTiny() <
yearTiny) {
488 if ((latest.isNull()) || compareZoneRule(year, rule, latest) > 0) {
495 mPrevTransition = createTransition(era, latest, priorYearTiny);
510 offsetCode = era.offsetCode();
513 deltaCode = rule.deltaCode();
514 offsetCode = era.offsetCode() +
deltaCode;
515 letter = rule.letter();
528 static int8_t compareZoneRule(int16_t year,
530 int16_t aYear = effectiveRuleYear(year, a);
531 int16_t bYear = effectiveRuleYear(year, b);
532 if (aYear < bYear)
return -1;
533 if (aYear > bYear)
return 1;
534 if (a.inMonth() < b.inMonth())
return -1;
535 if (a.inMonth() > b.inMonth())
return 1;
543 static int16_t effectiveRuleYear(int16_t year,
549 if (rule.fromYearTiny() <
yearTiny) {
556 void addRulesForYear(int16_t year)
const {
562 if (zonePolicy.isNull()) {
571 uint8_t numRules = zonePolicy.numRules();
572 for (uint8_t i = 0; i < numRules; i++) {
574 if ((rule.fromYearTiny() <=
yearTiny) &&
575 (yearTiny <= rule.toYearTiny())) {
576 addRule(year, era, rule);
615 if (mNumTransitions >= kMaxCacheEntries)
return;
619 mTransitions[mNumTransitions] = createTransition(era, rule, yearTiny);
623 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
627 if ((left.
rule.isNotNull() && right.
rule.isNotNull() &&
628 left.
rule.inMonth() > right.
rule.inMonth())
629 || (left.
rule.isNotNull() && right.
rule.isNull())) {
643 for (uint8_t i = 0; i < mZoneInfo.numEras(); i++) {
648 return mZoneInfo.era(mZoneInfo.numEras() - 1);
663 for (uint8_t i = 0; i < mZoneInfo.numEras(); i++) {
668 return mZoneInfo.era(mZoneInfo.numEras() - 1);
678 void calcTransitions()
const {
680 mPrevTransition.startEpochSeconds = kMinEpochSeconds;
683 for (uint8_t i = 0; i < mNumTransitions; i++) {
687 if (transition.
rule.isNull()) {
699 const int8_t prevOffsetCode = prevTransition->
offsetCode;
712 year, transition.
rule.inMonth(), transition.
rule.onDayOfWeek(),
713 transition.
rule.onDayOfMonth());
717 const int8_t prevOffsetCode = calcRuleOffsetCode(
719 transition.
era.offsetCode(),
720 transition.
rule.atTimeModifier());
723 const uint8_t timeCode = transition.
rule.atTimeCode();
724 const uint8_t atHour = timeCode / 4;
725 const uint8_t atMinute = (timeCode % 4) * 15;
727 year, monthDay.month, monthDay.day,
728 atHour, atMinute, 0 ,
733 prevTransition = &transition;
743 static int8_t calcRuleOffsetCode(int8_t prevEffectiveOffsetCode,
744 int8_t currentBaseOffsetCode, uint8_t atModifier) {
745 if (atModifier ==
'w') {
746 return prevEffectiveOffsetCode;
747 }
else if (atModifier ==
's') {
748 return currentBaseOffsetCode;
755 void calcAbbreviations()
const {
756 calcAbbreviation(&mPrevTransition);
757 for (uint8_t i = 0; i < mNumTransitions; i++) {
758 calcAbbreviation(&mTransitions[i]);
767 transition->
era.format(),
807 static void createAbbreviation(
char* dest, uint8_t destSize,
808 const char* format, uint8_t
deltaCode,
char letter) {
810 if (deltaCode == 0 && letter ==
'\0') {
811 strncpy(dest, format, destSize);
812 dest[destSize - 1] =
'\0';
817 if (strchr(format,
'%') !=
nullptr) {
818 copyAndReplace(dest, destSize, format,
'%', letter);
821 const char* slashPos = strchr(format,
'/');
822 if (slashPos !=
nullptr) {
823 if (deltaCode == 0) {
824 uint8_t headLength = (slashPos - format);
825 if (headLength >= destSize) headLength = destSize - 1;
826 memcpy(dest, format, headLength);
827 dest[headLength] =
'\0';
829 uint8_t tailLength = strlen(slashPos+1);
830 if (tailLength >= destSize) tailLength = destSize - 1;
831 memcpy(dest, slashPos+1, tailLength);
832 dest[tailLength] =
'\0';
836 strncpy(dest, format, destSize);
837 dest[destSize - 1] =
'\0';
847 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
848 char oldChar,
char newChar) {
849 while (*src !=
'\0' && dstSize > 0) {
850 if (*src == oldChar) {
851 if (newChar ==
'-') {
874 for (uint8_t i = 0; i < mNumTransitions; i++) {
885 mutable int16_t mYear = 0;
886 mutable bool mIsFilled =
false;
887 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.