1 #ifndef ACE_TIME_EXTENDED_ZONE_SPECIFIER_H 2 #define ACE_TIME_EXTENDED_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" 14 #include "BasicZoneSpecifier.h" 15 #include "local_date_mutation.h" 19 class ExtendedZoneSpecifierTest_compareEraToYearMonth;
20 class ExtendedZoneSpecifierTest_createMatch;
21 class ExtendedZoneSpecifierTest_findMatches_simple;
22 class ExtendedZoneSpecifierTest_findMatches_named;
23 class ExtendedZoneSpecifierTest_findCandidateTransitions;
24 class ExtendedZoneSpecifierTest_findTransitionsFromNamedMatch;
25 class ExtendedZoneSpecifierTest_getTransitionTime;
26 class ExtendedZoneSpecifierTest_createTransitionForYear;
27 class ExtendedZoneSpecifierTest_normalizeDateTuple;
28 class ExtendedZoneSpecifierTest_expandDateTuple;
29 class ExtendedZoneSpecifierTest_calcInteriorYears;
30 class ExtendedZoneSpecifierTest_getMostRecentPriorYear;
31 class ExtendedZoneSpecifierTest_compareTransitionToMatchFuzzy;
32 class ExtendedZoneSpecifierTest_compareTransitionToMatch;
33 class ExtendedZoneSpecifierTest_processActiveTransition;
34 class ExtendedZoneSpecifierTest_fixTransitionTimes_generateStartUntilTimes;
35 class ExtendedZoneSpecifierTest_createAbbreviation;
36 class TransitionStorageTest_getFreeAgent;
37 class TransitionStorageTest_getFreeAgent2;
38 class TransitionStorageTest_addFreeAgentToActivePool;
39 class TransitionStorageTest_reservePrior;
40 class TransitionStorageTest_addFreeAgentToCandidatePool;
41 class TransitionStorageTest_setFreeAgentAsPrior;
42 class TransitionStorageTest_addActiveCandidatesToActivePool;
43 class TransitionStorageTest_resetCandidatePool;
63 logging::print(
"DateTuple(%d-%u-%uT%d'%c')",
70 if (a.yearTiny < b.yearTiny)
return true;
71 if (a.yearTiny > b.yearTiny)
return false;
72 if (a.month < b.month)
return true;
73 if (a.month > b.month)
return false;
74 if (a.day < b.day)
return true;
75 if (a.day > b.day)
return false;
76 if (a.timeCode < b.timeCode)
return true;
77 if (a.timeCode > b.timeCode)
return false;
95 return a.yearTiny == b.yearTiny
98 && a.timeCode == b.timeCode
99 && a.modifier == b.modifier;
123 logging::print(
"ZoneMatch(");
124 logging::print(
"Start:"); startDateTime.
log();
125 logging::print(
"; Until:"); untilDateTime.
log();
126 logging::print(
"; Era: %snull", (era) ?
"!" :
"");
205 char abbrev[kAbbrevSize];
216 const char* format()
const {
243 static char letterBuf[2];
253 if (rule->
letter ==
'-') {
256 letterBuf[0] = rule->
letter;
266 uint8_t numLetters = policy->numLetters;
267 if (rule->
letter >= numLetters) {
275 return policy->letters[rule->
letter];
285 logging::print(
"Transition(");
286 if (
sizeof(acetime_t) ==
sizeof(
int)) {
287 logging::print(
"sE: %d", startEpochSeconds);
289 logging::print(
"sE: %ld", startEpochSeconds);
291 logging::print(
"; match: %snull", (match) ?
"!" :
"");
292 logging::print(
"; era: %snull", (match && match->
era) ?
"!" :
"");
293 logging::print(
"; oCode: %d", offsetCode());
294 logging::print(
"; dCode: %d", deltaCode());
295 logging::print(
"; tt: "); transitionTime.
log();
296 if (rule !=
nullptr) {
298 logging::print(
"; R.tY: %d", rule->
toYearTiny);
299 logging::print(
"; R.M: %d", rule->
inMonth);
332 template<u
int8_t SIZE>
340 for (uint8_t i = 0; i < SIZE; i++) {
341 mTransitions[i] = &mPool[i];
344 mIndexCandidates = 0;
367 mIndexCandidates = mIndexPrior;
368 mIndexFree = mIndexPrior;
372 return &mTransitions[mIndexCandidates];
375 return &mTransitions[mIndexFree];
378 Transition** getActivePoolBegin() {
return &mTransitions[0]; }
379 Transition** getActivePoolEnd() {
return &mTransitions[mIndexFree]; }
389 if (mIndexFree > mHighWater) {
390 mHighWater = mIndexFree;
393 if (mIndexFree < SIZE) {
394 return mTransitions[mIndexFree];
396 return mTransitions[SIZE - 1];
408 if (mIndexFree >= SIZE)
return;
410 mIndexPrior = mIndexFree;
411 mIndexCandidates = mIndexFree;
422 return &mTransitions[mIndexPrior];
427 swap(&mTransitions[mIndexPrior], &mTransitions[mIndexFree]);
446 if (mIndexFree >= SIZE)
return;
447 for (uint8_t i = mIndexFree; i > mIndexCandidates; i--) {
451 mTransitions[i] = prev;
452 mTransitions[i - 1] = curr;
462 if (DEBUG) logging::println(
"addActiveCandidatesToActivePool()");
463 uint8_t iActive = mIndexPrior;
464 uint8_t iCandidate = mIndexCandidates;
465 for (; iCandidate < mIndexFree; iCandidate++) {
466 if (mTransitions[iCandidate]->active) {
467 if (iActive != iCandidate) {
468 swap(&mTransitions[iActive], &mTransitions[iCandidate]);
473 mIndexPrior = iActive;
474 mIndexCandidates = iActive;
475 mIndexFree = iActive;
487 if (DEBUG) logging::println(
488 "findTransition(): mIndexFree: %d", mIndexFree);
491 for (uint8_t i = 0; i < mIndexFree; i++) {
492 const Transition* candidate = mTransitions[i];
524 if (DEBUG) logging::println(
525 "findTransitionForDateTime(): mIndexFree: %d", mIndexFree);
530 (int8_t) (ldt.
hour() * 4 + ldt.
minute() / 15),
'w' };
532 for (uint8_t i = 0; i < mIndexFree; i++) {
533 const Transition* candidate = mTransitions[i];
542 logging::println(
"TransitionStorage:");
543 logging::println(
" mIndexPrior: %d", mIndexPrior);
544 logging::println(
" mIndexCandidates: %d", mIndexCandidates);
545 logging::println(
" mIndexFree: %d", mIndexFree);
546 if (mIndexPrior != 0) {
547 logging::println(
" Actives:");
548 for (uint8_t i = 0; i < mIndexPrior; i++) {
549 mTransitions[i]->log();
553 if (mIndexPrior != mIndexCandidates) {
554 logging::print(
" Prior: ");
555 mTransitions[mIndexPrior]->log();
558 if (mIndexCandidates != mIndexFree) {
559 logging::println(
" Candidates:");
560 for (uint8_t i = mIndexCandidates; i < mIndexFree; i++) {
561 mTransitions[i]->log();
578 friend class ::TransitionStorageTest_getFreeAgent;
579 friend class ::TransitionStorageTest_getFreeAgent2;
580 friend class ::TransitionStorageTest_addFreeAgentToActivePool;
581 friend class ::TransitionStorageTest_reservePrior;
582 friend class ::TransitionStorageTest_addFreeAgentToCandidatePool;
583 friend class ::TransitionStorageTest_setFreeAgentAsPrior;
584 friend class ::TransitionStorageTest_addActiveCandidatesToActivePool;
585 friend class ::TransitionStorageTest_resetCandidatePool;
588 Transition* getTransition(uint8_t i) {
return mTransitions[i]; }
593 uint8_t mIndexCandidates;
597 uint8_t mHighWater = 0;
638 mZoneInfo(zoneInfo) {}
644 bool success = init(epochSeconds);
654 bool success = init(epochSeconds);
660 const char*
getAbbrev(acetime_t epochSeconds)
const override {
661 bool success = init(epochSeconds);
662 if (!success)
return "";
664 return transition->
abbrev;
672 mTransitionStorage.findTransitionForDateTime(ldt);
673 offset = (transition)
692 acetime_t epochSeconds = odt.toEpochSeconds();
694 mTransitionStorage.findTransition(epochSeconds);
695 offset = (transition)
704 void printTo(Print& printer)
const override;
708 logging::println(
"ExtendedZoneSpecifier:");
709 logging::println(
" mYear: %d", mYear);
710 logging::println(
" mNumMatches: %d", mNumMatches);
711 for (
int i = 0; i < mNumMatches; i++) {
712 logging::print(
" Match %d: ", i);
716 mTransitionStorage.log();
721 mTransitionStorage.resetHighWater();
726 return mTransitionStorage.getHighWater();
730 friend class ::ExtendedZoneSpecifierTest_compareEraToYearMonth;
731 friend class ::ExtendedZoneSpecifierTest_createMatch;
732 friend class ::ExtendedZoneSpecifierTest_findMatches_simple;
733 friend class ::ExtendedZoneSpecifierTest_findMatches_named;
734 friend class ::ExtendedZoneSpecifierTest_findCandidateTransitions;
735 friend class ::ExtendedZoneSpecifierTest_findTransitionsFromNamedMatch;
736 friend class ::ExtendedZoneSpecifierTest_getTransitionTime;
737 friend class ::ExtendedZoneSpecifierTest_createTransitionForYear;
738 friend class ::ExtendedZoneSpecifierTest_normalizeDateTuple;
739 friend class ::ExtendedZoneSpecifierTest_expandDateTuple;
740 friend class ::ExtendedZoneSpecifierTest_calcInteriorYears;
741 friend class ::ExtendedZoneSpecifierTest_getMostRecentPriorYear;
742 friend class ::ExtendedZoneSpecifierTest_compareTransitionToMatchFuzzy;
743 friend class ::ExtendedZoneSpecifierTest_compareTransitionToMatch;
744 friend class ::ExtendedZoneSpecifierTest_processActiveTransition;
745 friend class ::ExtendedZoneSpecifierTest_fixTransitionTimes_generateStartUntilTimes;
746 friend class ::ExtendedZoneSpecifierTest_createAbbreviation;
752 static const uint8_t kMaxMatches = 4;
761 static const uint8_t kMaxTransitions = 8;
767 static const uint8_t kMaxInteriorYears = 4;
778 return getZoneInfo() == that.getZoneInfo();
786 return mTransitionStorage.findTransition(epochSeconds);
790 bool init(acetime_t epochSeconds)
const {
800 int16_t year = ld.
year();
801 if (isFilled(year))
return true;
802 if (DEBUG) logging::println(
"init(): %d", year);
806 mTransitionStorage.init();
808 if (year < mZoneInfo->zoneContext->startYear - 1
809 || mZoneInfo->zoneContext->untilYear < year) {
818 mNumMatches = findMatches(mZoneInfo, startYm, untilYm, mMatches,
821 findTransitions(mTransitionStorage, mMatches, mNumMatches);
824 fixTransitionTimes(begin, end);
825 generateStartUntilTimes(begin, end);
826 calcAbbreviations(begin, end);
833 bool isFilled(int16_t year)
const {
834 return mIsFilled && (year == mYear);
848 if (DEBUG) logging::println(
"findMatches()");
851 for (uint8_t iEra = 0; iEra < zoneInfo->
numEras; iEra++) {
853 if (eraOverlapsInterval(prev, era, startYm, untilYm)) {
854 if (iMatch < maxMatches) {
855 matches[iMatch] = createMatch(prev, era, startYm, untilYm);
874 static bool eraOverlapsInterval(
879 return compareEraToYearMonth(prev, untilYm.yearTiny, untilYm.month) < 0
880 && compareEraToYearMonth(era, startYm.yearTiny, startYm.month) > 0;
885 int8_t yearTiny, uint8_t month) {
912 startYm.yearTiny, startYm.month, 1, 0,
'w' 914 if (startDate < lowerBound) {
915 startDate = lowerBound;
923 untilYm.yearTiny, untilYm.month, 1, 0,
'w' 925 if (upperBound < untilDate) {
926 untilDate = upperBound;
929 return {startDate, untilDate, era};
936 static void findTransitions(
939 uint8_t numMatches) {
940 if (DEBUG) logging::println(
"findTransitions()");
941 for (uint8_t i = 0; i < numMatches; i++) {
942 findTransitionsForMatch(transitionStorage, &matches[i]);
947 static void findTransitionsForMatch(
950 if (DEBUG) logging::println(
"findTransitionsForMatch()");
952 if (policy ==
nullptr) {
953 findTransitionsFromSimpleMatch(transitionStorage, match);
955 findTransitionsFromNamedMatch(transitionStorage, match);
959 static void findTransitionsFromSimpleMatch(
962 if (DEBUG) logging::println(
"findTransitionsFromSimpleMatch()");
964 freeTransition->
match = match;
965 freeTransition->
rule =
nullptr;
971 static void findTransitionsFromNamedMatch(
974 if (DEBUG) logging::println(
"findTransitionsFromNamedMatch()");
976 if (DEBUG) { match->log(); logging::println(); }
977 findCandidateTransitions(transitionStorage, match);
978 if (DEBUG) { transitionStorage.
log(); logging::println(); }
980 transitionStorage.getCandidatePoolBegin(),
981 transitionStorage.getCandidatePoolEnd());
982 selectActiveTransitions(transitionStorage, match);
983 if (DEBUG) { transitionStorage.
log(); logging::println(); }
986 if (DEBUG) { transitionStorage.
log(); logging::println(); }
989 static void findCandidateTransitions(
993 logging::print(
"findCandidateTransitions(): ");
998 uint8_t numRules = policy->numRules;
1004 (*prior)->
active =
false;
1005 for (uint8_t r = 0; r < numRules; r++) {
1009 int8_t interiorYears[kMaxInteriorYears];
1010 uint8_t numYears = calcInteriorYears(interiorYears, kMaxInteriorYears,
1012 for (uint8_t y = 0; y < numYears; y++) {
1013 int8_t year = interiorYears[y];
1015 createTransitionForYear(t, year, rule, match);
1016 int8_t status = compareTransitionToMatchFuzzy(t, match);
1018 setAsPriorTransition(transitionStorage, t);
1019 }
else if (status == 1) {
1025 int8_t priorYear = getMostRecentPriorYear(
1028 if (DEBUG) logging::println(
1029 "findCandidateTransitions(): priorYear: %d", priorYear);
1031 createTransitionForYear(t, priorYear, rule, match);
1032 setAsPriorTransition(transitionStorage, t);
1038 if ((*prior)->active) {
1039 if (DEBUG) logging::println(
1040 "findCandidateTransitions(): adding prior to Candidate pool");
1049 static uint8_t calcInteriorYears(int8_t* interiorYears,
1050 uint8_t maxInteriorYears, int8_t fromYear, int8_t toYear,
1051 int8_t startYear, int8_t endYear) {
1053 for (int8_t year = startYear; year <= endYear; year++) {
1054 if (fromYear <= year && year <= toYear) {
1055 interiorYears[i] = year;
1057 if (i >= maxInteriorYears)
break;
1077 static int8_t getMostRecentPriorYear(int8_t fromYear, int8_t toYear,
1078 int8_t startYear, int8_t ) {
1079 if (fromYear < startYear) {
1080 if (toYear < startYear) {
1083 return startYear - 1;
1095 return {yearTiny, rule->
inMonth, dayOfMonth,
1109 static int8_t compareTransitionToMatchFuzzy(
1114 int16_t matchStartMonths = match->
startDateTime.yearTiny * 12
1116 if (ttMonths < matchStartMonths - 1)
return -1;
1118 int16_t matchUntilMonths = match->
untilDateTime.yearTiny * 12
1120 if (matchUntilMonths + 2 <= ttMonths)
return 2;
1126 static void setAsPriorTransition(
1129 if (DEBUG) logging::println(
"setAsPriorTransition()");
1150 static void fixTransitionTimes(
1152 if (DEBUG) logging::println(
"fixTransitionTimes(): #transitions: %d;",
1153 (
int) (end - begin));
1159 logging::println(
"fixTransitionTimes(): LOOP");
1168 if (DEBUG) logging::println(
"fixTransitionTimes(): END");
1178 int8_t offsetCode, int8_t deltaCode) {
1179 if (DEBUG) logging::println(
"expandDateTuple()");
1180 if (tt->modifier ==
's') {
1182 *ttu = {tt->yearTiny, tt->month, tt->day,
1183 (int8_t) (tt->timeCode - offsetCode),
'u'};
1184 *tt = {tt->yearTiny, tt->month, tt->day,
1185 (int8_t) (tt->timeCode + deltaCode),
'w'};
1186 }
else if (tt->modifier ==
'u') {
1188 *tts = {tt->yearTiny, tt->month, tt->day,
1189 (int8_t) (tt->timeCode + offsetCode),
's'};
1190 *tt = {tt->yearTiny, tt->month, tt->day,
1191 (int8_t) (tt->timeCode + offsetCode + deltaCode),
'w'};
1195 *tts = {tt->yearTiny, tt->month, tt->day,
1196 (int8_t) (tt->timeCode - deltaCode),
's'};
1197 *ttu = {tt->yearTiny, tt->month, tt->day,
1198 (int8_t) (tt->timeCode - deltaCode - offsetCode),
'u'};
1201 if (DEBUG) logging::println(
"expandDateTuple(): normalizeDateTuple(): 1");
1202 normalizeDateTuple(tt);
1203 if (DEBUG) logging::println(
"expandDateTuple(): normalizeDateTuple(): 2");
1204 normalizeDateTuple(tts);
1205 if (DEBUG) logging::println(
"expandDateTuple(): normalizeDateTuple(): 3");
1206 normalizeDateTuple(ttu);
1211 const int8_t kOneDayAsCode = 4 * 24;
1212 if (dt->timeCode <= -kOneDayAsCode) {
1214 dt->yearTiny, dt->month, dt->day);
1215 local_date_mutation::decrementOneDay(ld);
1217 dt->month = ld.
month();
1219 dt->timeCode += kOneDayAsCode;
1220 }
else if (kOneDayAsCode <= dt->timeCode) {
1222 dt->yearTiny, dt->month, dt->day);
1223 local_date_mutation::incrementOneDay(ld);
1225 dt->month = ld.
month();
1227 dt->timeCode -= kOneDayAsCode;
1237 static void selectActiveTransitions(
1242 if (DEBUG) logging::println(
"selectActiveTransitions(): #candidates: %d",
1243 (
int) (end - begin));
1247 processActiveTransition(match, transition, &prior);
1253 if (DEBUG) logging::println(
1254 "selectActiveTransitions(): found latest prior");
1267 static void processActiveTransition(
1271 int8_t status = compareTransitionToMatch(transition, match);
1273 transition->
active =
false;
1274 }
else if (status == 1) {
1275 transition->
active =
true;
1276 }
else if (status == 0) {
1278 (*prior)->active =
false;
1280 transition->
active =
true;
1281 (*prior) = transition;
1285 (*prior)->
active =
false;
1286 transition->
active =
true;
1287 (*prior) = transition;
1290 transition->
active =
true;
1291 (*prior) = transition;
1310 static int8_t compareTransitionToMatch(
1316 if (matchStart.modifier ==
's') {
1318 }
else if (matchStart.modifier ==
'u') {
1323 if (*transitionTime < matchStart)
return -1;
1324 if (*transitionTime == matchStart)
return 0;
1327 if (matchUntil.modifier ==
's') {
1329 }
else if (matchUntil.modifier ==
'u') {
1334 if (*transitionTime < matchUntil)
return 1;
1343 static void generateStartUntilTimes(
1345 if (DEBUG) logging::println(
1346 "generateStartUntilTimes(): #transitions: %d;",
1347 (
int) (end - begin));
1350 bool isAfterFirst =
false;
1365 t->
startDateTime = {tt.yearTiny, tt.month, tt.day, code, tt.modifier};
1379 const acetime_t offsetSeconds = (acetime_t) 900
1382 st.yearTiny, st.month, st.day);
1386 isAfterFirst =
true;
1393 expandDateTuple(&untilTime, &untilTimeS, &untilTimeU,
1401 static void calcAbbreviations(
1403 if (DEBUG) logging::println(
"calcAbbreviations(): #transitions: %d;",
1404 (
int) (end - begin));
1407 const char* format = t->format();
1409 const char* letter = t->
letter();
1411 format, deltaCode, letter);
1449 static void createAbbreviation(
char* dest, uint8_t destSize,
1450 const char* format, uint8_t deltaCode,
const char* letterString) {
1453 if (letterString ==
nullptr) {
1454 strncpy(dest, format, destSize);
1455 dest[destSize - 1] =
'\0';
1460 if (strchr(format,
'%') !=
nullptr) {
1461 copyAndReplace(dest, destSize, format,
'%', letterString);
1464 const char* slashPos = strchr(format,
'/');
1465 if (slashPos !=
nullptr) {
1466 if (deltaCode == 0) {
1467 uint8_t headLength = (slashPos - format);
1468 if (headLength >= destSize) headLength = destSize - 1;
1469 memcpy(dest, format, headLength);
1470 dest[headLength] =
'\0';
1472 uint8_t tailLength = strlen(slashPos+1);
1473 if (tailLength >= destSize) tailLength = destSize - 1;
1474 memcpy(dest, slashPos+1, tailLength);
1475 dest[tailLength] =
'\0';
1479 strncpy(dest, format, destSize);
1480 dest[destSize - 1] =
'\0';
1490 static void copyAndReplace(
char* dst, uint8_t dstSize,
const char* src,
1491 char oldChar,
const char* newString) {
1492 while (*src !=
'\0' && dstSize > 0) {
1493 if (*src == oldChar) {
1494 while (*newString !=
'\0' && dstSize > 0) {
1495 *dst++ = *newString++;
1513 mutable int16_t mYear = 0;
1514 mutable bool mIsFilled =
false;
1516 mutable uint8_t mNumMatches = 0;
void addFreeAgentToActivePool()
Immediately add the free agent Transition at index mIndexFree to the Active pool. ...
static TimeOffset forError()
Return an error indicator.
A heap manager which is specialized and tuned to manage a collection of Transitions, keeping track of unused, used, and active states, using a fixed array of Transitions.
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). ...
uint8_t minute() const
Return the minute.
void addActiveCandidatesToActivePool()
Add active candidates into the Active pool, and collapse the Candidate pool.
uint8_t const untilMonth
The month field in UNTIL (1-12).
DateTuple transitionTimeU
Version of transitionTime in 'u' mode, using the UTC offset of the previous transition.
int8_t const toYearTiny
TO year as an offset from year 2000 stored as a single byte.
void resetCandidatePool()
Empty the Candidate pool by resetting the various indexes.
const char * getAbbrev(acetime_t epochSeconds) const override
Return the time zone abbreviation at epochSeconds.
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
uint8_t const onDayOfMonth
Determined by the ON column.
DateTuple startDateTime
The effective start time of the matching ZoneEra.
Transition * getFreeAgent()
Return a pointer to the first Transition in the free pool.
static const uint8_t kAbbrevSize
Longest abbreviation currently seems to be 5 characters (https://www.timeanddate.com/time/zones/) but...
const ZoneMatch * match
The match which generated this Transition.
uint8_t getHighWater() const
Return the high water mark.
bool isError() const
Return true if this TimeOffset represents an error.
TransitionStorage()
Constructor.
static OffsetDateTime forLocalDateTimeAndOffset(const LocalDateTime &localDateTime, TimeOffset timeOffset)
Factory method from LocalDateTime and TimeOffset.
DateTuple originalTransitionTime
If the transition is shifted to the beginning of a ZoneMatch, this is set to the transitionTime for d...
An implementation of ZoneSpecifier that works for all zones defined by the TZ Database (with some zon...
uint8_t day() const
Return the day of the month.
A collection of transition rules which describe the DST rules of a given administrative region...
int8_t yearTiny() const
Return the single-byte year offset from year 2000.
void log() const
Used only for debugging.
const extended::ZoneRule * rule
The Zone transition rule that matched for the the given year.
int8_t const offsetCode
UTC offset in 15 min increments.
Represents an interval of time where the time zone obeyed a certain UTC offset and DST delta...
void log() const
Verify that the indexes are valid.
void addPriorToCandidatePool()
Add the current prior into the Candidates pool.
Representation of a given time zone, implemented as an array of ZoneEra records.
const ZonePolicy *const zonePolicy
Zone policy, determined by the RULES column.
void setFreeAgentAsPrior()
Swap the Free agrent transition with the current Prior transition.
Transition * getPrior()
Return the current prior transition.
static LocalDate forTinyComponents(int8_t yearTiny, uint8_t month, uint8_t day)
Factory method using components with an int8_t yearTiny.
char abbrev[kAbbrevSize]
The calculated effective time zone abbreviation, e.g.
const char * letter() const
Return the letter string.
void resetTransitionHighWater()
Reset the TransitionStorage high water mark.
An entry in ZoneInfo which describes which ZonePolicy was being followed during a particular time per...
uint8_t day() const
Return the day of the month.
uint8_t month() const
Return the month with January=1, December=12.
void init()
Initialize all pools.
static OffsetDateTime forEpochSeconds(acetime_t epochSeconds, TimeOffset timeOffset)
Factory method.
uint8_t const untilTimeModifier
UNTIL time modifier suffix: 'w', 's' or 'u'.
void addFreeAgentToCandidatePool()
Add the free agent Transition at index mIndexFree to the Candidate pool, sorted by transitionTime...
Base interface for ZoneSpecifier classes.
DateTuple transitionTime
The original transition time, usually 'w' but sometimes 's' or 'u'.
acetime_t toEpochSeconds() const
Return the number of seconds since AceTime epoch (2000-01-01 00:00:00).
uint8_t const untilTimeCode
The time field of UNTIL field in 15-minute increments.
A time zone transition rule.
const LocalDate & localDate() const
Return the LocalDate.
static const uint8_t kAbbrevSize
Size of the timezone abbreviation.
const Transition * findTransitionForDateTime(const LocalDateTime &ldt) const
Return the Transition matching the given dateTime.
uint8_t const onDayOfWeek
Determined by the ON column.
uint8_t hour() const
Return the hour.
uint8_t const numEras
Number of ZoneEra entries.
uint8_t const untilDay
The day field in UNTIL (1-31).
DateTuple transitionTimeS
Version of transitionTime in 's' mode, using the UTC offset of the previous Transition.
int8_t const untilYearTiny
Era is valid until currentTime < untilYear.
acetime_t startEpochSeconds
The calculated transition time of the given rule.
A simple tuple to represent a year/month pair.
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 extended::ZoneEra * era
The ZoneEra that matched the given year.
bool active
Determines if this transition is valid.
The date (year, month, day) and time (hour, minute, second) fields representing the time with an offs...
uint8_t getTransitionHighWater() const
Get the TransitionStorage high water mark.
void log() const
Used only for debugging.
Transition ** reservePrior()
Allocate one Transition just after the Active pool, but before the Candidate pool, to keep the most recent prior Transition.
static const int8_t kInvalidYearTiny
Sentinel yearTiny which indicates an error condition or sometimes a year that 'does not exist'...
ExtendedZoneSpecifier(const extended::ZoneInfo *zoneInfo)
Constructor.
void resetHighWater()
Reset the high water mark.
TimeOffset getDeltaOffset(acetime_t epochSeconds) const override
Return the DST delta offset at epochSeconds.
A thin wrapper that represents a time offset from a reference point, usually 00:00 at UTC...
uint8_t const inMonth
Determined by the IN column.
uint8_t const atTimeCode
Determined by the AT column in units of 15-minutes from 00:00.
int8_t offsetCode() const
The base offset code, not the total effective UTC offset.
void swap(Transition **a, Transition **b)
Swap 2 transitions.
The date (year, month, day) representing the date without regards to time zone.
DateTuple untilDateTime
Until time expressed using the UTC offset of the current Transition.
const extended::ZoneInfo * getZoneInfo() const
Return the underlying ZoneInfo.
void log() const
Used only for debugging.
int8_t const fromYearTiny
FROM year as an offset from year 2000 stored as a single byte.
OffsetDateTime getOffsetDateTime(const LocalDateTime &ldt) const override
Return the best estimate of the OffsetDateTime at the given LocalDateTime for the timezone of the cur...
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 int16_t kEpochYear
Base year of epoch.
DateTuple untilDateTime
The effective until time of the matching ZoneEra.
uint8_t const letter
Determined by the LETTER column.
DateTuple startDateTime
Start time expressed using the UTC offset of the current 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.
const ZoneEra *const eras
ZoneEra entries in increasing order of UNTIL time.
A tuple that represents a date and time, using a timeCode that tracks the time component using 15-min...
const Transition * findTransition(acetime_t epochSeconds) const
Return the Transition matching the given epochSeconds.
TimeOffset getUtcOffset(acetime_t epochSeconds) const override
Return the total UTC offset at epochSeconds, including DST offset.
int8_t const deltaCode
Determined by the SAVE column, containing the offset from UTC, in 15-min increments.
const char *const format
Zone abbreviations (e.g.
int8_t deltaCode() const
The DST offset code.
Data structure that captures the matching ZoneEra and its ZoneRule transitions for a given year...
int8_t const deltaCode
If zonePolicy is nullptr, then this indicates the DST offset in 15 minute increments.