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) {}
155 int8_t code = (transition)
165 }
else if (transition->
rule ==
nullptr) {
173 const char*
getAbbrev(acetime_t epochSeconds)
const override {
175 return (transition) ? transition->
abbrev :
"";
201 auto offset0 = getUtcOffset(epochSeconds0);
206 auto offset1 = getUtcOffset(epochSeconds1);
211 auto offset2 = getUtcOffset(epochSeconds2);
215 if (offset1.toOffsetCode() == offset2.toOffsetCode()) {
219 acetime_t epochSeconds;
221 if (epochSeconds1 > epochSeconds2) {
222 epochSeconds = epochSeconds1;
225 epochSeconds = epochSeconds2;
238 void printTo(Print& printer)
const override;
243 logging::println(
"*not initialized*");
246 logging::println(
"mYear: %d", mYear);
247 logging::println(
"mNumTransitions: %d", mNumTransitions);
248 logging::println(
"---- PrevTransition");
249 mPrevTransition.log();
250 for (
int i = 0; i < mNumTransitions; i++) {
251 logging::println(
"---- Transition: %d", i);
252 mTransitions[i].log();
265 uint8_t onDayOfWeek, uint8_t onDayOfMonth) {
266 if (onDayOfWeek == 0)
return onDayOfMonth;
270 if (onDayOfMonth == 0) {
275 year, month, onDayOfMonth);
276 uint8_t dayOfWeekShift = (onDayOfWeek - limitDate.
dayOfWeek() + 7) % 7;
277 return onDayOfMonth + dayOfWeekShift;
281 friend class ::BasicZoneSpecifierTest_init_primitives;
282 friend class ::BasicZoneSpecifierTest_init;
283 friend class ::BasicZoneSpecifierTest_createAbbreviation;
284 friend class ::BasicZoneSpecifierTest_calcStartDayOfMonth;
285 friend class ::BasicZoneSpecifierTest_calcRuleOffsetCode;
288 static const uint8_t kMaxCacheEntries = 4;
295 static const acetime_t kMinEpochSeconds = INT32_MIN + 1;
303 return getZoneInfo() == that.getZoneInfo();
309 bool success = init(ld);
310 return (success) ? findMatch(epochSeconds) :
nullptr;
342 int16_t year = ld.
year();
343 if (ld.
month() == 1 && ld.
day() == 1) {
346 if (isFilled(year))
return true;
351 if (year < mZoneInfo->zoneContext->startYear - 1
352 || mZoneInfo->zoneContext->untilYear < year) {
356 addRulePriorToYear(year);
357 addRulesForYear(year);
366 bool isFilled(int16_t year)
const {
367 return mIsFilled && (year == mYear);
374 void addRulePriorToYear(int16_t year)
const {
376 int8_t priorYearTiny = yearTiny - 1;
386 if (zonePolicy !=
nullptr) {
390 for (uint8_t i = 0; i < zonePolicy->numRules; i++) {
394 if ((latest ==
nullptr)
395 || compareZoneRule(year, rule, latest) > 0) {
413 static int8_t compareZoneRule(int16_t year,
415 int16_t aYear = effectiveRuleYear(year, a);
416 int16_t bYear = effectiveRuleYear(year, b);
417 if (aYear < bYear)
return -1;
418 if (aYear > bYear)
return 1;
428 static int16_t effectiveRuleYear(int16_t year,
441 void addRulesForYear(int16_t year)
const {
447 if (zonePolicy ==
nullptr) {
448 addRule(year, era,
nullptr);
456 for (uint8_t i = 0; i < zonePolicy->numRules; i++) {
459 (yearTiny <= rule->toYearTiny)) {
460 addRule(year, era, rule);
499 if (mNumTransitions >= kMaxCacheEntries)
return;
503 mTransitions[mNumTransitions] = {
514 for (uint8_t i = mNumTransitions - 1; i > 0; i--) {
518 if ((left.
rule !=
nullptr && right.
rule !=
nullptr &&
520 || (left.
rule !=
nullptr && right.
rule ==
nullptr)) {
534 for (uint8_t i = 0; i < mZoneInfo->numEras; i++) {
539 return &mZoneInfo->eras[mZoneInfo->numEras - 1];
554 for (uint8_t i = 0; i < mZoneInfo->numEras; i++) {
559 return &mZoneInfo->eras[mZoneInfo->numEras - 1];
563 void calcTransitions()
const {
565 mPrevTransition.startEpochSeconds = kMinEpochSeconds;
566 int8_t deltaCode = (mPrevTransition.rule ==
nullptr)
568 mPrevTransition.offsetCode = mPrevTransition.era->offsetCode + deltaCode;
574 for (uint8_t i = 0; i < mNumTransitions; i++) {
578 if (transition.
rule ==
nullptr) {
590 const int8_t
offsetCode = prevTransition->offsetCode;
603 const uint8_t startDayOfMonth = calcStartDayOfMonth(
610 prevTransition->offsetCode,
619 atHour, atMinute, 0 ,
628 prevTransition = &transition;
638 static int8_t calcRuleOffsetCode(int8_t prevEffectiveOffsetCode,
639 int8_t currentBaseOffsetCode, uint8_t atModifier) {
640 if (atModifier ==
'w') {
641 return prevEffectiveOffsetCode;
642 }
else if (atModifier ==
's') {
643 return currentBaseOffsetCode;
650 void calcAbbreviations()
const {
651 calcAbbreviation(&mPrevTransition);
652 for (uint8_t i = 0; i < mNumTransitions; i++) {
653 calcAbbreviation(&mTransitions[i]);
702 static void createAbbreviation(
char* dest, uint8_t destSize,
703 const char* format, uint8_t deltaCode,
char letter) {
705 if (deltaCode == 0 && letter ==
'\0') {
706 strncpy(dest, format, destSize);
707 dest[destSize - 1] =
'\0';
712 if (strchr(format,
'%') !=
nullptr) {
713 copyAndReplace(dest, destSize, format,
'%', letter);
716 const char* slashPos = strchr(format,
'/');
717 if (slashPos !=
nullptr) {
718 if (deltaCode == 0) {
719 uint8_t headLength = (slashPos - format);
720 if (headLength >= destSize) headLength = destSize - 1;
721 memcpy(dest, format, headLength);
722 dest[headLength] =
'\0';
724 uint8_t tailLength = strlen(slashPos+1);
725 if (tailLength >= destSize) tailLength = destSize - 1;
726 memcpy(dest, slashPos+1, tailLength);
727 dest[tailLength] =
'\0';
731 strncpy(dest, format, destSize);
732 dest[destSize - 1] =
'\0';
742 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
743 char oldChar,
char newChar) {
744 while (*src !=
'\0' && dstSize > 0) {
745 if (*src == oldChar) {
746 if (newChar ==
'-') {
769 for (uint8_t i = 0; i < mNumTransitions; i++) {
780 mutable int16_t mYear = 0;
781 mutable bool mIsFilled =
false;
782 mutable uint8_t mNumTransitions = 0;
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
static uint8_t calcStartDayOfMonth(int16_t year, uint8_t month, uint8_t onDayOfWeek, uint8_t onDayOfMonth)
Calculate the actual dayOfMonth of the expresssion (onDayOfWeek >= onDayOfMonth). ...
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.
void log() const
Used only for debugging.
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.
A collection of transition rules which describe the DST rules of a given administrative region...
static OffsetDateTime forLocalDateTimeAndOffset(const LocalDateTime &localDateTime, TimeOffset timeOffset)
Factory method from LocalDateTime and TimeOffset.
OffsetDateTime getOffsetDateTime(const LocalDateTime &ldt) const override
Representation of a given time zone, implemented as an array of ZoneEra records.
uint8_t const inMonth
Determined by the IN column.
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.
int8_t const deltaCode
Determined by the SAVE column, containing the offset from UTC, in 15-min increments.
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.
Base interface for ZoneSpecifier classes.
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
acetime_t toEpochSeconds() const
Return seconds since AceTime epoch 2000-01-01 00:00:00Z, after assuming that the date and time compon...
int8_t const deltaCode
If zonePolicy is nullptr, then this indicates the DST offset in 15 minute increments.
const LocalDate & localDate() const
Return the LocalDate.
const ZoneRule * rule
The Zone transition rule that matched for the the given year.
uint8_t const atTimeModifier
Determined by the suffix in the AT column: 'w'=wall; 's'=standard; 'u'=meridian ('g' and 'z' mean the...
static const int8_t kErrorCode
Sentinel value that represents an error.
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...
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.
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.
const basic::ZoneInfo * getZoneInfo() const
Return the underlying ZoneInfo.
static uint8_t daysInMonth(int16_t year, uint8_t month)
Return the number of days in the current month.
int8_t const offsetCode
UTC offset in 15 min increments.
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.
BasicZoneSpecifier(const basic::ZoneInfo *zoneInfo)
Constructor.
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...
static OffsetDateTime forError()
Factory method that returns an instance whose isError() is true.
void log() const
Used only for debugging.
uint8_t dayOfWeek() const
Calculate the day of week given the (year, month, day).
const char *const format
Zone abbreviations (e.g.