1 #ifndef ACE_TIME_BASIC_ZONE_SPECIFIER_H 2 #define ACE_TIME_BASIC_ZONE_SPECIFIER_H 7 #include "common/ZonePolicy.h" 8 #include "common/ZoneInfo.h" 9 #include "common/logger.h" 10 #include "TimeOffset.h" 11 #include "LocalDate.h" 12 #include "OffsetDateTime.h" 13 #include "ZoneSpecifier.h" 15 class BasicZoneSpecifierTest_init_primitives;
16 class BasicZoneSpecifierTest_init;
17 class BasicZoneSpecifierTest_createAbbreviation;
18 class BasicZoneSpecifierTest_calcStartDayOfMonth;
19 class BasicZoneSpecifierTest_calcRuleOffsetCode;
71 if (
sizeof(acetime_t) ==
sizeof(
int)) {
72 logging::println(
"startEpochSeconds: %d", startEpochSeconds);
74 logging::println(
"startEpochSeconds: %ld", startEpochSeconds);
76 logging::println(
"offsetCode: %d", offsetCode);
77 logging::println(
"abbrev: %s", abbrev);
78 if (rule !=
nullptr) {
79 logging::println(
"Rule.fromYear: %d", rule->
fromYearTiny);
80 logging::println(
"Rule.toYear: %d", rule->
toYearTiny);
81 logging::println(
"Rule.inMonth: %d", rule->
inMonth);
82 logging::println(
"Rule.onDayOfMonth: %d", rule->
onDayOfMonth);
148 mZoneInfo(zoneInfo) {}
166 const char*
getAbbrev(acetime_t epochSeconds)
const override {
168 if (!transition)
return "";
169 return transition->
abbrev;
188 init(ldt.getLocalDate());
192 acetime_t initialEpochSeconds =
194 TimeOffset initialTimeOffset = getUtcOffset(initialEpochSeconds);
199 return getUtcOffset(epochSeconds);
202 void printTo(Print& printer)
const override;
207 logging::println(
"*not initialized*");
210 logging::println(
"mYear: %d", mYear);
211 logging::println(
"mNumTransitions: %d", mNumTransitions);
212 logging::println(
"---- PrevTransition");
213 mPrevTransition.log();
214 for (
int i = 0; i < mNumTransitions; i++) {
215 logging::println(
"---- Transition: %d", i);
216 mTransitions[i].log();
221 friend class ::BasicZoneSpecifierTest_init_primitives;
222 friend class ::BasicZoneSpecifierTest_init;
223 friend class ::BasicZoneSpecifierTest_createAbbreviation;
224 friend class ::BasicZoneSpecifierTest_calcStartDayOfMonth;
225 friend class ::BasicZoneSpecifierTest_calcRuleOffsetCode;
229 static const uint8_t kMaxCacheEntries = 4;
236 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
244 return getZoneInfo() == that.getZoneInfo();
251 return (mIsOutOfBounds) ?
nullptr : findMatch(epochSeconds);
280 int16_t year = ld.
year();
281 if (ld.
month() == 1 && ld.
day() == 1) {
284 if (isFilled(year))
return;
289 if (year < mZoneInfo->zoneContext->startYear - 1
290 || mZoneInfo->zoneContext->untilYear < year) {
291 mIsOutOfBounds =
true;
295 addRulePriorToYear(year);
296 addRulesForYear(year);
301 mIsOutOfBounds =
false;
305 bool isFilled(int16_t year)
const {
306 return mIsFilled && (year == mYear);
313 void addRulePriorToYear(int16_t year)
const {
315 int8_t priorYearTiny = yearTiny - 1;
325 if (zonePolicy !=
nullptr) {
329 for (uint8_t i = 0; i < zonePolicy->numRules; i++) {
333 if ((latest ==
nullptr)
334 || compareZoneRule(year, rule, latest) > 0) {
352 static int8_t compareZoneRule(int16_t year,
354 int16_t aYear = effectiveRuleYear(year, a);
355 int16_t bYear = effectiveRuleYear(year, b);
356 if (aYear < bYear)
return -1;
357 if (aYear > bYear)
return 1;
367 static int16_t effectiveRuleYear(int16_t year,
380 void addRulesForYear(int16_t year)
const {
386 if (zonePolicy ==
nullptr) {
387 addRule(year, era,
nullptr);
395 for (uint8_t i = 0; i < zonePolicy->numRules; i++) {
398 (yearTiny <= rule->toYearTiny)) {
399 addRule(year, era, rule);
438 if (mNumTransitions >= kMaxCacheEntries)
return;
442 mTransitions[mNumTransitions] = {
453 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
457 if ((left.
rule !=
nullptr && right.
rule !=
nullptr &&
459 || (left.
rule !=
nullptr && right.
rule ==
nullptr)) {
473 for (uint8_t i = 0; i < mZoneInfo->numEras; i++) {
478 return &mZoneInfo->eras[mZoneInfo->numEras-1];
493 for (uint8_t i = 0; i < mZoneInfo->numEras; i++) {
498 return &mZoneInfo->eras[mZoneInfo->numEras-1];
502 void calcTransitions()
const {
504 mPrevTransition.startEpochSeconds = kMinEpochSeconds;
505 int8_t deltaCode = (mPrevTransition.rule ==
nullptr)
507 mPrevTransition.offsetCode = mPrevTransition.era->offsetCode + deltaCode;
513 for (uint8_t i = 0; i < mNumTransitions; i++) {
517 if (transition.
rule ==
nullptr) {
529 const int8_t
offsetCode = prevTransition->offsetCode;
542 const uint8_t startDayOfMonth = calcStartDayOfMonth(
549 prevTransition->offsetCode,
558 atHour, atMinute, 0 ,
567 prevTransition = &transition;
577 static uint8_t calcStartDayOfMonth(int16_t year, uint8_t month,
578 uint8_t onDayOfWeek, uint8_t onDayOfMonth) {
579 if (onDayOfWeek == 0)
return onDayOfMonth;
583 if (onDayOfMonth == 0) {
588 year, month, onDayOfMonth);
589 uint8_t dayOfWeekShift = (onDayOfWeek - limitDate.
dayOfWeek() + 7) % 7;
590 return onDayOfMonth + dayOfWeekShift;
599 static int8_t calcRuleOffsetCode(int8_t prevEffectiveOffsetCode,
600 int8_t currentBaseOffsetCode, uint8_t atModifier) {
601 if (atModifier ==
'w') {
602 return prevEffectiveOffsetCode;
603 }
else if (atModifier ==
's') {
604 return currentBaseOffsetCode;
611 void calcAbbreviations()
const {
612 calcAbbreviation(&mPrevTransition);
613 for (uint8_t i = 0; i < mNumTransitions; i++) {
614 calcAbbreviation(&mTransitions[i]);
663 static void createAbbreviation(
char* dest, uint8_t destSize,
664 const char* format, uint8_t deltaCode,
char letter) {
666 if (deltaCode == 0 && letter ==
'\0') {
667 strncpy(dest, format, destSize);
668 dest[destSize - 1] =
'\0';
673 if (strchr(format,
'%') !=
nullptr) {
674 copyAndReplace(dest, destSize, format,
'%', letter);
677 const char* slashPos = strchr(format,
'/');
678 if (slashPos !=
nullptr) {
679 if (deltaCode == 0) {
680 uint8_t headLength = (slashPos - format);
681 if (headLength >= destSize) headLength = destSize - 1;
682 memcpy(dest, format, headLength);
683 dest[headLength] =
'\0';
685 uint8_t tailLength = strlen(slashPos+1);
686 if (tailLength >= destSize) tailLength = destSize - 1;
687 memcpy(dest, slashPos+1, tailLength);
688 dest[tailLength] =
'\0';
692 strncpy(dest, format, destSize);
693 dest[destSize - 1] =
'\0';
703 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
704 char oldChar,
char newChar) {
705 while (*src !=
'\0' && dstSize > 0) {
706 if (*src == oldChar) {
707 if (newChar ==
'-') {
730 for (uint8_t i = 0; i < mNumTransitions; i++) {
741 mutable int16_t mYear = 0;
742 mutable bool mIsFilled =
false;
743 mutable bool mIsOutOfBounds =
false;
744 mutable uint8_t mNumTransitions = 0;
static TimeOffset forError()
Return an error indicator.
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
An entry in ZoneInfo which describes which ZonePolicy was being followed during a particular time per...
const ZoneEra * era
The ZoneEra that matched the given year.
static const uint8_t kAbbrevSize
Longest abbreviation currently seems to be 5 characters (https://www.timeanddate.com/time/zones/) but...
uint8_t month() const
Return the month with January=1, December=12.
static OffsetDateTime forComponents(int16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, TimeOffset timeOffset=TimeOffset())
Factory method using separated date, time, and UTC offset fields.
char abbrev[kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
A collection of transition rules which describe the DST rules of a given administrative region...
Representation of a given time zone, implemented as an array of ZoneEra records.
An implementation of ZoneSpecifier that works for all zones defined by the TZ Database (with some zon...
uint8_t const inMonth
Determined by the IN column.
acetime_t toEpochSeconds() const
Return seconds since AceTime epoch (2000-01-01 00:00:00Z), taking into account the offset zone...
acetime_t startEpochSeconds
The calculated transition time of the given rule.
int8_t const deltaCode
Determined by the SAVE column, containing the offset from UTC, in 15-min increments.
int8_t yearTiny
Year which applies to the ZoneEra or ZoneRule.
Base interface for ZoneSpecifier classes.
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
int16_t year() const
Return the year.
int8_t const deltaCode
If zonePolicy is nullptr, then this indicates the DST offset in 15 minute increments.
const ZoneRule * rule
The Zone transition rule that matched for the the given year.
int16_t year() const
Return the full year instead of just the last 2 digits.
uint8_t const atTimeModifier
Determined by the suffix in the AT column: 'w'=wall; 's'=standard; 'u'=meridian ('g' and 'z' mean the...
const basic::ZoneInfo * getZoneInfo() const
Return the underlying ZoneInfo.
void log() const
Used only for debugging.
uint8_t day() const
Return the day of the month.
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.
const ZonePolicy *const zonePolicy
Zone policy, determined by the RULES column.
TimeOffset getUtcOffset(acetime_t epochSeconds) const override
Return the total UTC offset at epochSeconds, including DST offset.
The date (year, month, day) and time (hour, minute, second) fields representing the time with an offs...
Data structure that defines the start of a specific UTC offset as described by the matching ZoneEra a...
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.
uint8_t const onDayOfWeek
Determined by the ON column.
The date (year, month, day) representing the date without regards to time zone.
A time zone transition rule.
int8_t const fromYearTiny
FROM year as an offset from year 2000 stored as a single byte.
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.
uint8_t dayOfWeek() const
Calculate the day of week given the (year, month, day).
int8_t const offsetCode
UTC offset in 15 min increments.
BasicZoneSpecifier(const basic::ZoneInfo *zoneInfo)
Constructor.
TimeOffset getUtcOffsetForDateTime(const LocalDateTime &ldt) const override
Return the UTC offset matching the given the date/time components.
int8_t const toYearTiny
TO year as an offset from year 2000 stored as a single byte.
uint8_t const atTimeCode
Determined by the AT column in units of 15-minutes from 00:00.
uint8_t const letter
Determined by the LETTER column.
uint8_t const onDayOfMonth
Determined by the ON column.
An implementation of ZoneSpecifier that supports a subset of the zones containing in the TZ Database...
void log() const
Used only for debugging.
const char *const format
Zone abbreviations (e.g.