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 #define ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG 0 22 class BasicZoneProcessorTest_init_primitives;
23 class BasicZoneProcessorTest_init;
24 class BasicZoneProcessorTest_setZoneInfo;
25 class BasicZoneProcessorTest_createAbbreviation;
26 class BasicZoneProcessorTest_calcStartDayOfMonth;
27 class BasicZoneProcessorTest_calcRuleOffsetCode;
31 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
107 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
108 if (
sizeof(acetime_t) ==
sizeof(
int)) {
109 logging::printf(
"startEpochSeconds: %d\n", startEpochSeconds);
111 logging::printf(
"startEpochSeconds: %ld\n", startEpochSeconds);
113 logging::printf(
"offsetCode: %d\n", offsetCode);
114 logging::printf(
"abbrev: %s\n", abbrev);
115 if (rule.isNotNull()) {
116 logging::printf(
"Rule.fromYear: %d\n", rule.fromYearTiny());
117 logging::printf(
"Rule.toYear: %d\n", rule.toYearTiny());
118 logging::printf(
"Rule.inMonth: %d\n", rule.inMonth());
119 logging::printf(
"Rule.onDayOfMonth: %d\n", rule.onDayOfMonth());
192 mZoneInfo(zoneInfo) {}
196 return mZoneInfo.zoneInfo();
199 uint32_t
getZoneId()
const override {
return mZoneInfo.zoneId(); }
203 int8_t code = (transition)
210 int8_t code = (transition)
215 const char*
getAbbrev(acetime_t epochSeconds)
const override {
217 return (transition) ? transition->
abbrev :
"";
257 auto offset0 = getUtcOffset(epochSeconds0);
262 auto offset1 = getUtcOffset(epochSeconds1);
267 auto offset2 = getUtcOffset(epochSeconds2);
271 if (offset1.toOffsetCode() == offset2.toOffsetCode()) {
275 acetime_t epochSeconds;
277 if (epochSeconds1 > epochSeconds2) {
278 epochSeconds = epochSeconds1;
281 epochSeconds = epochSeconds2;
293 void printTo(Print& printer)
const override;
295 void printShortTo(Print& printer)
const override;
299 if (ACE_TIME_BASIC_ZONE_PROCESSOR_DEBUG) {
301 logging::printf(
"*not initialized*\n");
304 logging::printf(
"mYear: %d\n", mYear);
305 logging::printf(
"mNumTransitions: %d\n", mNumTransitions);
306 logging::printf(
"---- PrevTransition\n");
307 mPrevTransition.log();
308 for (
int i = 0; i < mNumTransitions; i++) {
309 logging::printf(
"---- Transition: %d\n", i);
310 mTransitions[i].log();
335 uint8_t onDayOfWeek, int8_t onDayOfMonth) {
336 if (onDayOfWeek == 0)
return {month, (uint8_t) onDayOfMonth};
338 if (onDayOfMonth >= 0) {
341 if (onDayOfMonth == 0) {
342 onDayOfMonth = daysInMonth - 6;
346 uint8_t dayOfWeekShift = (onDayOfWeek - limitDate.dayOfWeek() + 7) % 7;
347 uint8_t day = (uint8_t) (onDayOfMonth + dayOfWeekShift);
348 if (day > daysInMonth) {
355 onDayOfMonth = -onDayOfMonth;
357 int8_t dayOfWeekShift = (limitDate.dayOfWeek() - onDayOfWeek + 7) % 7;
358 int8_t day = onDayOfMonth - dayOfWeekShift;
363 day += daysInPrevMonth;
365 return {month, (uint8_t) day};
370 friend class ::BasicZoneProcessorTest_init_primitives;
371 friend class ::BasicZoneProcessorTest_init;
372 friend class ::BasicZoneProcessorTest_setZoneInfo;
373 friend class ::BasicZoneProcessorTest_createAbbreviation;
374 friend class ::BasicZoneProcessorTest_calcStartDayOfMonth;
375 friend class ::BasicZoneProcessorTest_calcRuleOffsetCode;
377 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
381 static const uint8_t kMaxCacheEntries = 4;
388 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
410 void setZoneInfo(
const void* zoneInfo)
override {
411 if (mZoneInfo.zoneInfo() == zoneInfo)
return;
422 bool success = init(ld);
423 return (success) ? findMatch(epochSeconds) :
nullptr;
455 int16_t year = ld.
year();
456 if (ld.
month() == 1 && ld.
day() == 1) {
459 if (isFilled(year))
return true;
464 if (year < mZoneInfo.startYear() - 1 || mZoneInfo.untilYear() < year) {
468 addTransitionPriorToYear(year);
469 addTransitionsForYear(year);
478 bool isFilled(int16_t year)
const {
479 return mIsFilled && (year == mYear);
486 void addTransitionPriorToYear(int16_t year)
const {
488 int8_t priorYearTiny = yearTiny - 1;
498 if (zonePolicy.isNotNull()) {
502 uint8_t numRules = zonePolicy.numRules();
503 for (uint8_t i = 0; i < numRules; i++) {
506 if (rule.fromYearTiny() <
yearTiny) {
507 if ((latest.isNull()) || compareZoneRule(year, rule, latest) > 0) {
514 mPrevTransition = createTransition(era, latest, priorYearTiny);
527 deltaCode = era.deltaCode();
530 deltaCode = rule.deltaCode();
531 letter = rule.letter();
547 static int8_t compareZoneRule(int16_t year,
549 int16_t aYear = effectiveRuleYear(year, a);
550 int16_t bYear = effectiveRuleYear(year, b);
551 if (aYear < bYear)
return -1;
552 if (aYear > bYear)
return 1;
553 if (a.inMonth() < b.inMonth())
return -1;
554 if (a.inMonth() > b.inMonth())
return 1;
562 static int16_t effectiveRuleYear(int16_t year,
568 if (rule.fromYearTiny() <
yearTiny) {
575 void addTransitionsForYear(int16_t year)
const {
581 if (zonePolicy.isNull()) {
590 uint8_t numRules = zonePolicy.numRules();
591 for (uint8_t i = 0; i < numRules; i++) {
593 if ((rule.fromYearTiny() <=
yearTiny) &&
594 (yearTiny <= rule.toYearTiny())) {
595 addTransition(year, era, rule);
633 if (mNumTransitions >= kMaxCacheEntries)
return;
637 mTransitions[mNumTransitions] = createTransition(era, rule, yearTiny);
641 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
645 if ((left.
rule.isNotNull() && right.
rule.isNotNull() &&
646 left.
rule.inMonth() > right.
rule.inMonth())
647 || (left.
rule.isNotNull() && right.
rule.isNull())) {
661 for (uint8_t i = 0; i < mZoneInfo.numEras(); i++) {
666 return mZoneInfo.era(mZoneInfo.numEras() - 1);
681 for (uint8_t i = 0; i < mZoneInfo.numEras(); i++) {
686 return mZoneInfo.era(mZoneInfo.numEras() - 1);
696 void calcTransitions()
const {
698 mPrevTransition.startEpochSeconds = kMinEpochSeconds;
701 for (uint8_t i = 0; i < mNumTransitions; i++) {
705 if (transition.
rule.isNull()) {
717 const int8_t prevOffsetCode = prevTransition->
offsetCode;
730 year, transition.
rule.inMonth(), transition.
rule.onDayOfWeek(),
731 transition.
rule.onDayOfMonth());
735 const int8_t prevOffsetCode = calcRuleOffsetCode(
737 transition.
era.offsetCode(),
738 transition.
rule.atTimeSuffix());
741 const uint16_t minutes = transition.
rule.atTimeMinutes();
742 const uint8_t atHour = minutes / 60;
743 const uint8_t atMinute = minutes % 60;
745 year, monthDay.month, monthDay.day,
746 atHour, atMinute, 0 ,
751 prevTransition = &transition;
761 static int8_t calcRuleOffsetCode(int8_t prevEffectiveOffsetCode,
762 int8_t currentBaseOffsetCode, uint8_t atSuffix) {
764 return prevEffectiveOffsetCode;
766 return currentBaseOffsetCode;
773 void calcAbbreviations()
const {
774 calcAbbreviation(&mPrevTransition);
775 for (uint8_t i = 0; i < mNumTransitions; i++) {
776 calcAbbreviation(&mTransitions[i]);
785 transition->
era.format(),
836 static void createAbbreviation(
char* dest, uint8_t destSize,
837 const char* format, uint8_t
deltaCode,
char letter) {
839 if (strchr(format,
'%') !=
nullptr) {
841 if (letter ==
'\0') {
842 strncpy(dest, format, destSize - 1);
843 dest[destSize - 1] =
'\0';
845 copyAndReplace(dest, destSize, format,
'%', letter);
849 const char* slashPos = strchr(format,
'/');
850 if (slashPos !=
nullptr) {
851 if (deltaCode == 0) {
852 uint8_t headLength = (slashPos - format);
853 if (headLength >= destSize) headLength = destSize - 1;
854 memcpy(dest, format, headLength);
855 dest[headLength] =
'\0';
857 uint8_t tailLength = strlen(slashPos+1);
858 if (tailLength >= destSize) tailLength = destSize - 1;
859 memcpy(dest, slashPos+1, tailLength);
860 dest[tailLength] =
'\0';
864 strncpy(dest, format, destSize - 1);
865 dest[destSize - 1] =
'\0';
875 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
876 char oldChar,
char newChar) {
877 while (*src !=
'\0' && dstSize > 0) {
878 if (*src == oldChar) {
879 if (newChar ==
'-') {
902 for (uint8_t i = 0; i < mNumTransitions; i++) {
913 mutable int16_t mYear = 0;
914 mutable bool mIsFilled =
false;
915 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...
Data broker for accessing ZonePolicy in PROGMEM.
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 uint8_t TIME_SUFFIX_S
Represents 's' or standard time.
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 <=...
static const uint8_t TIME_SUFFIX_W
Represents 'w' or wall time.
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.