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();
334 uint8_t onDayOfWeek, int8_t onDayOfMonth) {
335 if (onDayOfWeek == 0)
return {month, (uint8_t) onDayOfMonth};
337 if (onDayOfMonth >= 0) {
340 if (onDayOfMonth == 0) {
341 onDayOfMonth = daysInMonth - 6;
345 uint8_t dayOfWeekShift = (onDayOfWeek - limitDate.dayOfWeek() + 7) % 7;
346 uint8_t day = (uint8_t) (onDayOfMonth + dayOfWeekShift);
347 if (day > daysInMonth) {
354 onDayOfMonth = -onDayOfMonth;
356 int8_t dayOfWeekShift = (limitDate.dayOfWeek() - onDayOfWeek + 7) % 7;
357 int8_t day = onDayOfMonth - dayOfWeekShift;
362 day += daysInPrevMonth;
364 return {month, (uint8_t) day};
369 friend class ::BasicZoneProcessorTest_init_primitives;
370 friend class ::BasicZoneProcessorTest_init;
371 friend class ::BasicZoneProcessorTest_setZoneInfo;
372 friend class ::BasicZoneProcessorTest_createAbbreviation;
373 friend class ::BasicZoneProcessorTest_calcStartDayOfMonth;
374 friend class ::BasicZoneProcessorTest_calcRuleOffsetCode;
376 template<u
int8_t SIZE, u
int8_t TYPE,
typename ZS,
typename ZI,
typename ZIB>
380 static const uint8_t kMaxCacheEntries = 4;
387 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
399 void setZoneInfo(
const void* zoneInfo)
override {
400 if (mZoneInfo.zoneInfo() == zoneInfo)
return;
411 bool success = init(ld);
412 return (success) ? findMatch(epochSeconds) :
nullptr;
444 int16_t year = ld.
year();
445 if (ld.
month() == 1 && ld.
day() == 1) {
448 if (isFilled(year))
return true;
453 if (year < mZoneInfo.startYear() - 1 || mZoneInfo.untilYear() < year) {
457 addRulePriorToYear(year);
458 addRulesForYear(year);
467 bool isFilled(int16_t year)
const {
468 return mIsFilled && (year == mYear);
475 void addRulePriorToYear(int16_t year)
const {
477 int8_t priorYearTiny = yearTiny - 1;
487 if (zonePolicy.isNotNull()) {
491 uint8_t numRules = zonePolicy.numRules();
492 for (uint8_t i = 0; i < numRules; i++) {
495 if (rule.fromYearTiny() <
yearTiny) {
496 if ((latest.isNull()) || compareZoneRule(year, rule, latest) > 0) {
503 mPrevTransition = createTransition(era, latest, priorYearTiny);
518 offsetCode = era.offsetCode();
521 deltaCode = rule.deltaCode();
522 offsetCode = era.offsetCode() +
deltaCode;
523 letter = rule.letter();
538 static int8_t compareZoneRule(int16_t year,
540 int16_t aYear = effectiveRuleYear(year, a);
541 int16_t bYear = effectiveRuleYear(year, b);
542 if (aYear < bYear)
return -1;
543 if (aYear > bYear)
return 1;
544 if (a.inMonth() < b.inMonth())
return -1;
545 if (a.inMonth() > b.inMonth())
return 1;
553 static int16_t effectiveRuleYear(int16_t year,
559 if (rule.fromYearTiny() <
yearTiny) {
566 void addRulesForYear(int16_t year)
const {
572 if (zonePolicy.isNull()) {
581 uint8_t numRules = zonePolicy.numRules();
582 for (uint8_t i = 0; i < numRules; i++) {
584 if ((rule.fromYearTiny() <=
yearTiny) &&
585 (yearTiny <= rule.toYearTiny())) {
586 addRule(year, era, rule);
625 if (mNumTransitions >= kMaxCacheEntries)
return;
629 mTransitions[mNumTransitions] = createTransition(era, rule, yearTiny);
633 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
637 if ((left.
rule.isNotNull() && right.
rule.isNotNull() &&
638 left.
rule.inMonth() > right.
rule.inMonth())
639 || (left.
rule.isNotNull() && right.
rule.isNull())) {
653 for (uint8_t i = 0; i < mZoneInfo.numEras(); i++) {
658 return mZoneInfo.era(mZoneInfo.numEras() - 1);
673 for (uint8_t i = 0; i < mZoneInfo.numEras(); i++) {
678 return mZoneInfo.era(mZoneInfo.numEras() - 1);
688 void calcTransitions()
const {
690 mPrevTransition.startEpochSeconds = kMinEpochSeconds;
693 for (uint8_t i = 0; i < mNumTransitions; i++) {
697 if (transition.
rule.isNull()) {
709 const int8_t prevOffsetCode = prevTransition->
offsetCode;
722 year, transition.
rule.inMonth(), transition.
rule.onDayOfWeek(),
723 transition.
rule.onDayOfMonth());
727 const int8_t prevOffsetCode = calcRuleOffsetCode(
729 transition.
era.offsetCode(),
730 transition.
rule.atTimeModifier());
733 const uint16_t minutes = transition.
rule.atTimeMinutes();
734 const uint8_t atHour = minutes / 60;
735 const uint8_t atMinute = minutes % 60;
737 year, monthDay.month, monthDay.day,
738 atHour, atMinute, 0 ,
743 prevTransition = &transition;
753 static int8_t calcRuleOffsetCode(int8_t prevEffectiveOffsetCode,
754 int8_t currentBaseOffsetCode, uint8_t atModifier) {
756 return prevEffectiveOffsetCode;
758 return currentBaseOffsetCode;
765 void calcAbbreviations()
const {
766 calcAbbreviation(&mPrevTransition);
767 for (uint8_t i = 0; i < mNumTransitions; i++) {
768 calcAbbreviation(&mTransitions[i]);
777 transition->
era.format(),
817 static void createAbbreviation(
char* dest, uint8_t destSize,
818 const char* format, uint8_t
deltaCode,
char letter) {
820 if (deltaCode == 0 && letter ==
'\0') {
821 strncpy(dest, format, destSize);
822 dest[destSize - 1] =
'\0';
827 if (strchr(format,
'%') !=
nullptr) {
828 copyAndReplace(dest, destSize, format,
'%', letter);
831 const char* slashPos = strchr(format,
'/');
832 if (slashPos !=
nullptr) {
833 if (deltaCode == 0) {
834 uint8_t headLength = (slashPos - format);
835 if (headLength >= destSize) headLength = destSize - 1;
836 memcpy(dest, format, headLength);
837 dest[headLength] =
'\0';
839 uint8_t tailLength = strlen(slashPos+1);
840 if (tailLength >= destSize) tailLength = destSize - 1;
841 memcpy(dest, slashPos+1, tailLength);
842 dest[tailLength] =
'\0';
846 strncpy(dest, format, destSize);
847 dest[destSize - 1] =
'\0';
857 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
858 char oldChar,
char newChar) {
859 while (*src !=
'\0' && dstSize > 0) {
860 if (*src == oldChar) {
861 if (newChar ==
'-') {
884 for (uint8_t i = 0; i < mNumTransitions; i++) {
895 mutable int16_t mYear = 0;
896 mutable bool mIsFilled =
false;
897 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_MODIFIER_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_MODIFIER_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.