AceTime  3.0.0
Date and time classes for Arduino that support timezones from the TZ Database.
ZoneInfoLow.h
Go to the documentation of this file.
1 /*
2  * MIT License
3  * Copyright (c) 2023 Brian T. Park
4  */
5 
6 #ifndef ACE_TIME_ZONE_INFO_LOW_H
7 #define ACE_TIME_ZONE_INFO_LOW_H
8 
27 #include <stdint.h>
28 #include <Arduino.h> // pgm_read_xxx()
29 #include <AceCommon.h> // KString
30 #include "compat.h" // ACE_TIME_USE_PROGMEM
31 #include "BrokerCommon.h"
32 
33 class __FlashStringHelper;
34 class Print;
35 
36 namespace ace_time{
37 
38 class ZoneInfoLow {
39 public:
40 
45 struct ZoneContext {
47  static const int16_t kInvalidYear = -32768;
48 
50  static const int8_t kInvalidYearTiny = -128;
51 
58  static const int16_t kMaxUntilYear = 32767;
59 
61  static const int8_t kMaxUntilYearTiny = 127;
62 
69  static const int16_t kMaxYear = kMaxUntilYear - 1;
70 
72  static const int8_t kMaxYearTiny = kMaxUntilYearTiny - 1;
73 
82  static const int16_t kMinYear = -32767;
83 
85  static const int8_t kMinYearTiny = -127;
86 
88  static const uint8_t kSuffixW = 0x00;
89 
91  static const uint8_t kSuffixS = 0x10;
92 
94  static const uint8_t kSuffixU = 0x20;
95 
97  int16_t const startYear;
98 
100  int16_t const untilYear;
101 
103  int16_t const startYearAccurate;
104 
106  int16_t const untilYearAccurate;
107 
109  int16_t const baseYear;
110 
112  int16_t const maxTransitions;
113 
115  const char* const tzVersion;
116 
118  uint8_t const numFragments;
119 
121  uint8_t const numLetters;;
122 
124  const char* const* const fragments;
125 
127  const char* const* const letters;
128 };
129 
135 struct ZoneRule {
137  int8_t const fromYear;
138 
140  int8_t const toYear;
141 
143  uint8_t const inMonth;
144 
155  uint8_t const onDayOfWeek;
156 
161  int8_t const onDayOfMonth;
162 
167  uint8_t const atTimeCode;
168 
178  uint8_t const atTimeModifier;
179 
190  uint8_t const deltaCode;
191 
204  uint8_t const letterIndex;
205 };
206 
213 struct ZonePolicy {
214  const ZoneRule* const rules;
215  uint8_t const numRules;
216 };
217 
231 struct ZoneEra {
236  const ZonePolicy* const zonePolicy;
237 
262  const char* const format;
263 
265  int8_t const offsetCode;
266 
284  uint8_t const deltaCode;
285 
289  int8_t const untilYear;
290 
292  uint8_t const untilMonth;
293 
299  uint8_t const untilDay;
300 
305  uint8_t const untilTimeCode;
306 
317  uint8_t const untilTimeModifier;
318 };
319 
324 struct ZoneInfo {
326  const char* const name;
327 
333  uint32_t const zoneId;
334 
336  const ZoneContext* const zoneContext;
337 
353  uint8_t const numEras;
354 
359  const ZoneEra* const eras;
360 
362  const ZoneInfo* const targetInfo;
363 };
364 
365 //-----------------------------------------------------------------------------
366 // Brokers are wrappers around the above data objects so that outside code
367 // can use the data objects with a consistent API.
368 //-----------------------------------------------------------------------------
369 
380 static int16_t toDeltaMinutes(uint8_t deltaCode) {
381  return ((int16_t)(deltaCode & 0x0f) - 4) * 15;
382 }
383 
390 static int16_t toOffsetMinutes(uint8_t offsetCode, uint8_t deltaCode) {
391  return ((int8_t)offsetCode * 15) + ((deltaCode & 0xf0) >> 4);
392 }
393 
394 
401 static uint16_t timeCodeToMinutes(uint8_t code, uint8_t modifier) {
402  return code * (uint16_t) 15 + (modifier & 0x0f);
403 }
404 
410 static uint8_t toSuffix(uint8_t modifier) {
411  return modifier & 0xf0;
412 }
413 
414 //-----------------------------------------------------------------------------
415 
420  public:
421  explicit ZoneContextBroker(const ZoneContext* zoneContext = nullptr)
422  : mZoneContext(zoneContext)
423  {}
424 
425  // use the default copy constructor
426  ZoneContextBroker(const ZoneContextBroker&) = default;
427 
428  // use the default assignment operator
429  ZoneContextBroker& operator=(const ZoneContextBroker&) = default;
430 
431  bool isNull() const { return mZoneContext == nullptr; }
432 
433  const ZoneContext* raw() const { return mZoneContext; }
434 
435  int16_t startYear() const {
436  return (int16_t) pgm_read_word(&mZoneContext->startYear);
437  }
438 
439  int16_t untilYear() const {
440  return (int16_t) pgm_read_word(&mZoneContext->untilYear);
441  }
442 
443  int16_t startYearAccurate() const {
444  return (int16_t) pgm_read_word(&mZoneContext->startYearAccurate);
445  }
446 
447  int16_t untilYearAccurate() const {
448  return (int16_t) pgm_read_word(&mZoneContext->untilYearAccurate);
449  }
450 
451  int16_t baseYear() const {
452  return (int16_t) pgm_read_word(&mZoneContext->baseYear);
453  }
454 
455  int16_t maxTransitions() const {
456  return (int16_t) pgm_read_word(&mZoneContext->maxTransitions);
457  }
458 
459  const __FlashStringHelper* tzVersion() const {
460  return (const __FlashStringHelper*)
461  pgm_read_ptr(&mZoneContext->tzVersion);
462  }
463 
464  uint8_t numFragments() const {
465  return (uint8_t) pgm_read_byte(&mZoneContext->numFragments);
466  }
467 
468  uint8_t numLetters() const {
469  return (uint8_t) pgm_read_byte(&mZoneContext->numLetters);
470  }
471 
472  const __FlashStringHelper* const* fragments() const {
473  return (const __FlashStringHelper* const*)
474  pgm_read_ptr(&mZoneContext->fragments);
475  }
476 
477  const __FlashStringHelper* letter(uint8_t i) const {
478  const char * const* letters = (const char* const*)
479  pgm_read_ptr(&mZoneContext->letters);
480  const char* letter = (const char*) pgm_read_ptr(letters + i);
481  return (const __FlashStringHelper*) letter;
482  }
483 
484  private:
485  const ZoneContext* mZoneContext;
486 };
487 
488 //-----------------------------------------------------------------------------
489 
494  public:
495  explicit ZoneRuleBroker(
496  const ZoneContext* zoneContext = nullptr,
497  const ZoneRule* zoneRule = nullptr)
498  : mZoneContext(zoneContext)
499  , mZoneRule(zoneRule)
500  {}
501 
502  // use the default copy constructor
503  ZoneRuleBroker(const ZoneRuleBroker&) = default;
504 
505  // use the default assignment operator
506  ZoneRuleBroker& operator=(const ZoneRuleBroker&) = default;
507 
508  bool isNull() const { return mZoneRule == nullptr; }
509 
510  int16_t fromYear() const {
511  int8_t yearTiny = (int8_t) pgm_read_byte(&mZoneRule->fromYear);
512  int16_t baseYear = ZoneContextBroker(mZoneContext).baseYear();
513  return toYearFromTiny(yearTiny, baseYear);
514  }
515 
516  int16_t toYear() const {
517  int8_t yearTiny = (int8_t) pgm_read_byte(&mZoneRule->toYear);
518  int16_t baseYear = ZoneContextBroker(mZoneContext).baseYear();
519  return toYearFromTiny(yearTiny, baseYear);
520  }
521 
522  static int16_t toYearFromTiny(int8_t yearTiny, int16_t baseYear) {
523  if (yearTiny == ZoneContext::kInvalidYearTiny)
525  if (yearTiny == ZoneContext::kMinYearTiny)
526  return ZoneContext::kMinYear;
527 
528  // yearTiny should be a maximum of 126, and should never be as high as
529  // 127. But if it does have this value, what should we do? Let's peg the
530  // return value at 32766, so that it's always less than the 32767 maximum
531  // returned by untilYear().
532  if (yearTiny >= ZoneContext::kMaxYearTiny) return ZoneContext::kMaxYear;
533 
534  return baseYear + yearTiny;
535  }
536 
537  uint8_t inMonth() const {
538  return pgm_read_byte(&mZoneRule->inMonth);
539  }
540 
541  uint8_t onDayOfWeek() const {
542  return pgm_read_byte(&mZoneRule->onDayOfWeek);
543  }
544 
545  int8_t onDayOfMonth() const {
546  return pgm_read_byte(&mZoneRule->onDayOfMonth);
547  }
548 
549  uint32_t atTimeSeconds() const {
550  return 60 * timeCodeToMinutes(
551  pgm_read_byte(&mZoneRule->atTimeCode),
552  pgm_read_byte(&mZoneRule->atTimeModifier));
553  }
554 
555  uint8_t atTimeSuffix() const {
556  return toSuffix(pgm_read_byte(&mZoneRule->atTimeModifier));
557  }
558 
559  int32_t deltaSeconds() const {
560  return 60 * toDeltaMinutes(pgm_read_byte(&mZoneRule->deltaCode));
561  }
562 
563  const __FlashStringHelper* letter() const {
564  uint8_t index = pgm_read_byte(&mZoneRule->letterIndex);
565  return ZoneContextBroker(mZoneContext).letter(index);
566  }
567 
568  private:
569  const ZoneContext* mZoneContext;
570  const ZoneRule* mZoneRule;
571 };
572 
577  public:
578  explicit ZonePolicyBroker(
579  const ZoneContext* zoneContext,
580  const ZonePolicy* zonePolicy)
581  : mZoneContext(zoneContext)
582  , mZonePolicy(zonePolicy)
583  {}
584 
585  // use default copy constructor
586  ZonePolicyBroker(const ZonePolicyBroker&) = default;
587 
588  // use default assignment operator
589  ZonePolicyBroker& operator=(const ZonePolicyBroker&) = default;
590 
591  bool isNull() const { return mZonePolicy == nullptr; }
592 
593  uint8_t numRules() const {
594  return pgm_read_byte(&mZonePolicy->numRules);
595  }
596 
597  const ZoneRuleBroker rule(uint8_t i) const {
598  const ZoneRule* rules =
599  (const ZoneRule*) pgm_read_ptr(&mZonePolicy->rules);
600  return ZoneRuleBroker(mZoneContext, &rules[i]);
601  }
602 
603  private:
604  const ZoneContext* mZoneContext;
605  const ZonePolicy* mZonePolicy;
606 };
607 
608 //-----------------------------------------------------------------------------
609 
614  public:
615  explicit ZoneEraBroker(
616  const ZoneContext* zoneContext = nullptr,
617  const ZoneEra* zoneEra = nullptr)
618  : mZoneContext(zoneContext)
619  , mZoneEra(zoneEra)
620  {}
621 
622  // use default copy constructor
623  ZoneEraBroker(const ZoneEraBroker&) = default;
624 
625  // use default assignment operator
626  ZoneEraBroker& operator=(const ZoneEraBroker&) = default;
627 
628  bool isNull() const { return mZoneEra == nullptr; }
629 
630  bool equals(const ZoneEraBroker& other) const {
631  return mZoneEra == other.mZoneEra;
632  }
633 
634  const ZonePolicyBroker zonePolicy() const {
635  return ZonePolicyBroker(
636  mZoneContext,
637  (const ZonePolicy*) pgm_read_ptr(&mZoneEra->zonePolicy));
638  }
639 
640  int32_t offsetSeconds() const {
641  return 60 * toOffsetMinutes(
642  pgm_read_byte(&mZoneEra->offsetCode),
643  pgm_read_byte(&mZoneEra->deltaCode));
644  }
645 
646  int32_t deltaSeconds() const {
647  return 60 * toDeltaMinutes(pgm_read_byte(&mZoneEra->deltaCode));
648  }
649 
650  const char* format() const {
651  return (const char*) pgm_read_ptr(&mZoneEra->format);
652  }
653 
654  int16_t untilYear() const {
655  int8_t yearTiny = (int8_t) pgm_read_byte(&mZoneEra->untilYear);
656  int16_t baseYear = ZoneContextBroker(mZoneContext).baseYear();
657  return toUntilYearFromTiny(yearTiny, baseYear);
658  }
659 
660  static int16_t toUntilYearFromTiny(int8_t yearTiny, int16_t baseYear) {
661  if (yearTiny == ZoneContext::kInvalidYearTiny)
663  if (yearTiny == ZoneContext::kMinYearTiny)
664  return ZoneContext::kMinYear;
665  if (yearTiny == ZoneContext::kMaxUntilYearTiny)
667  return baseYear + yearTiny;
668  }
669 
670  uint8_t untilMonth() const {
671  return pgm_read_byte(&mZoneEra->untilMonth);
672  }
673 
674  uint8_t untilDay() const {
675  return pgm_read_byte(&mZoneEra->untilDay);
676  }
677 
678  uint32_t untilTimeSeconds() const {
679  return 60 * timeCodeToMinutes(
680  pgm_read_byte(&mZoneEra->untilTimeCode),
681  pgm_read_byte(&mZoneEra->untilTimeModifier));
682  }
683 
684  uint8_t untilTimeSuffix() const {
685  return toSuffix(pgm_read_byte(&mZoneEra->untilTimeModifier));
686  }
687 
688  private:
689  const ZoneContext* mZoneContext;
690  const ZoneEra* mZoneEra;
691 };
692 
697  public:
698  explicit ZoneInfoBroker(const ZoneInfo* zoneInfo = nullptr):
699  mZoneInfo(zoneInfo) {}
700 
701  // use default copy constructor
702  ZoneInfoBroker(const ZoneInfoBroker&) = default;
703 
704  // use default assignment operator
705  ZoneInfoBroker& operator=(const ZoneInfoBroker&) = default;
706 
711  bool equals(uintptr_t zoneKey) const {
712  return mZoneInfo == (const ZoneInfo*) zoneKey;
713  }
714 
715  bool equals(const ZoneInfoBroker& zoneInfoBroker) const {
716  return mZoneInfo == zoneInfoBroker.mZoneInfo;
717  }
718 
719  bool isNull() const { return mZoneInfo == nullptr; }
720 
721  const ZoneContextBroker zoneContext() const {
722  const ZoneContext* context =
723  (const ZoneContext*) pgm_read_ptr(&mZoneInfo->zoneContext);
724  return ZoneContextBroker(context);
725  }
726 
727  const __FlashStringHelper* name() const {
728  return FPSTR(pgm_read_ptr(&mZoneInfo->name));
729  }
730 
731  uint32_t zoneId() const {
732  return pgm_read_dword(&mZoneInfo->zoneId);
733  }
734 
735  uint8_t numEras() const {
736  return pgm_read_byte(&mZoneInfo->numEras);
737  }
738 
739  const ZoneEraBroker era(uint8_t i) const {
740  auto eras = (const ZoneEra*) pgm_read_ptr(&mZoneInfo->eras);
741  return ZoneEraBroker(zoneContext().raw(), &eras[i]);
742  }
743 
744  bool isLink() const {
745  return mZoneInfo->targetInfo != nullptr;
746  }
747 
748  ZoneInfoBroker targetInfo() const {
749  return ZoneInfoBroker(
750  (const ZoneInfo*) pgm_read_ptr(&mZoneInfo->targetInfo));
751  }
752 
754  void printNameTo(Print& printer) const {
755  ZoneContextBroker zc = zoneContext();
756  ace_common::KString kname(name(), zc.fragments(), zc.numFragments());
757  kname.printTo(printer);
758  }
759 
764  void printShortNameTo(Print& printer) const {
765  ace_common::printReplaceCharTo(
766  printer, zoneinfo::findShortName(name()), '_', ' ');
767  }
768 
769  private:
770  const ZoneInfo* mZoneInfo;
771 };
772 
773 //-----------------------------------------------------------------------------
774 
780  public:
781  ZoneRegistryBroker(const ZoneInfo* const* zoneRegistry):
782  mZoneRegistry(zoneRegistry) {}
783 
784  // use default copy constructor
785  ZoneRegistryBroker(const ZoneRegistryBroker&) = default;
786 
787  // use default assignment operator
788  ZoneRegistryBroker& operator=(const ZoneRegistryBroker&) = default;
789 
790  const ZoneInfo* zoneInfo(uint16_t i) const {
791  return (const ZoneInfo*) pgm_read_ptr(&mZoneRegistry[i]);
792  }
793 
794  private:
795  const ZoneInfo* const* mZoneRegistry;
796 };
797 
798 //-----------------------------------------------------------------------------
799 // A factory class for a ZoneInfoBroker.
800 //-----------------------------------------------------------------------------
801 
808  public:
813  ZoneInfoBroker createZoneInfoBroker(uintptr_t zoneKey) const {
814  return ZoneInfoBroker((const ZoneInfo*) zoneKey);
815  }
816 };
817 
818 }; // ZoneInfoLow
819 
820 } // ace_time
821 
822 #endif
Helper functions are used in both Basic brokers and Extended brokers.
Data broker for accessing a ZoneContext.
Definition: ZoneInfoLow.h:419
Data broker for accessing ZoneEra.
Definition: ZoneInfoLow.h:613
Data broker for accessing ZoneInfo.
Definition: ZoneInfoLow.h:696
bool equals(uintptr_t zoneKey) const
Definition: ZoneInfoLow.h:711
void printNameTo(Print &printer) const
Print a human-readable identifier (e.g.
Definition: ZoneInfoLow.h:754
void printShortNameTo(Print &printer) const
Print a short human-readable identifier (e.g.
Definition: ZoneInfoLow.h:764
A storage object that creates an ZoneInfoBroker from a key that identifies the ZoneInfo.
Definition: ZoneInfoLow.h:807
ZoneInfoBroker createZoneInfoBroker(uintptr_t zoneKey) const
Definition: ZoneInfoLow.h:813
Data broker for accessing ZonePolicy.
Definition: ZoneInfoLow.h:576
Data broker for accessing the ZoneRegistry.
Definition: ZoneInfoLow.h:779
Data broker for accessing ZoneRule.
Definition: ZoneInfoLow.h:493
static uint16_t timeCodeToMinutes(uint8_t code, uint8_t modifier)
Convert (code, modifier) fields representing the UNTIL time in ZoneInfo or AT time in ZoneRule in one...
Definition: ZoneInfoLow.h:401
static int16_t toOffsetMinutes(uint8_t offsetCode, uint8_t deltaCode)
Convert the offsetCode and deltaCode holding the STDOFF field of the ZoneEra into minutes.
Definition: ZoneInfoLow.h:390
static int16_t toDeltaMinutes(uint8_t deltaCode)
Convert the deltaCode holding the RULES/DSTOFF field in ZoneEra or the SAVE field in ZoneRule to the ...
Definition: ZoneInfoLow.h:380
static uint8_t toSuffix(uint8_t modifier)
Extract the 'w', 's' 'u' suffix from the 'modifier' field, so that they can be compared against kSuff...
Definition: ZoneInfoLow.h:410
Macros and definitions that provide a consistency layer among the various Arduino boards for compatib...
Metadata about the zone database.
Definition: ZoneInfoLow.h:45
int16_t const maxTransitions
Max number of transitions required in TransitionStorage.
Definition: ZoneInfoLow.h:112
static const int8_t kMaxYearTiny
Maximum value of fromYearTiny or toYearTiny.
Definition: ZoneInfoLow.h:72
static const uint8_t kSuffixW
Represents 'w' or wall time.
Definition: ZoneInfoLow.h:88
static const int8_t kInvalidYearTiny
Sentinel value for an invalid 8-bit year field.
Definition: ZoneInfoLow.h:50
static const uint8_t kSuffixS
Represents 's' or standard time.
Definition: ZoneInfoLow.h:91
static const int16_t kMinYear
The minimum value of fromYear and toYear.
Definition: ZoneInfoLow.h:82
static const int16_t kMaxUntilYear
The maximum value of untilYear.
Definition: ZoneInfoLow.h:58
int16_t const untilYearAccurate
Until year of accurate transitions.
Definition: ZoneInfoLow.h:106
static const uint8_t kSuffixU
Represents 'u' or UTC time.
Definition: ZoneInfoLow.h:94
uint8_t const numFragments
Number of fragments.
Definition: ZoneInfoLow.h:118
static const int16_t kInvalidYear
Sentinel value for an invalid 16-bit year field.
Definition: ZoneInfoLow.h:47
int16_t const baseYear
Base year for tiny years.
Definition: ZoneInfoLow.h:109
const char *const tzVersion
TZ Database version which generated the zone info.
Definition: ZoneInfoLow.h:115
const char *const *const letters
Zone Rule letters list.
Definition: ZoneInfoLow.h:127
uint8_t const numLetters
Number of fragments.
Definition: ZoneInfoLow.h:121
static const int16_t kMaxYear
The maximum value fromYear and toYear.
Definition: ZoneInfoLow.h:69
int16_t const startYearAccurate
Start year of accurate transitions.
Definition: ZoneInfoLow.h:103
static const int8_t kMinYearTiny
The smallest value of a tiny year field.
Definition: ZoneInfoLow.h:85
static const int8_t kMaxUntilYearTiny
Maximum value of untilYearTiny.
Definition: ZoneInfoLow.h:61
int16_t const untilYear
Until year of the zone files as requested.
Definition: ZoneInfoLow.h:100
const char *const *const fragments
Zone Name fragment list.
Definition: ZoneInfoLow.h:121
int16_t const startYear
Start year of the zone files as requested.
Definition: ZoneInfoLow.h:97
An entry in ZoneInfo which describes which ZonePolicy was being followed during a particular time per...
Definition: ZoneInfoLow.h:231
uint8_t const untilTimeModifier
The untilTimeModifier is a packed field containing 2 pieces of info:
Definition: ZoneInfoLow.h:317
uint8_t const deltaCode
This is a composite of two 4-bit fields:
Definition: ZoneInfoLow.h:284
int8_t const untilYear
Era is valid until currentTime < untilYear.
Definition: ZoneInfoLow.h:289
int8_t const offsetCode
UTC offset in 15 min increments.
Definition: ZoneInfoLow.h:265
uint8_t const untilDay
The day field in UNTIL (1-31).
Definition: ZoneInfoLow.h:299
const ZonePolicy *const zonePolicy
Zone policy, determined by the RULES column.
Definition: ZoneInfoLow.h:236
uint8_t const untilMonth
The month field in UNTIL (1-12).
Definition: ZoneInfoLow.h:292
const char *const format
Zone abbreviations (e.g.
Definition: ZoneInfoLow.h:262
uint8_t const untilTimeCode
The time field of UNTIL field in 15-minute increments.
Definition: ZoneInfoLow.h:305
Representation of a given time zone, implemented as an array of ZoneEra records.
Definition: ZoneInfoLow.h:324
const ZoneContext *const zoneContext
ZoneContext metadata.
Definition: ZoneInfoLow.h:336
const char *const name
Full name of zone (e.g.
Definition: ZoneInfoLow.h:326
uint8_t const numEras
Number of ZoneEra entries.
Definition: ZoneInfoLow.h:353
const ZoneEra *const eras
A const ZoneEras* pointer to numEras ZoneEra entries in increasing order of UNTIL time.
Definition: ZoneInfoLow.h:359
uint32_t const zoneId
Unique, stable ID of the zone name, created from a hash of the name.
Definition: ZoneInfoLow.h:333
const ZoneInfo *const targetInfo
If Link, points to the target zone info.
Definition: ZoneInfoLow.h:362
A collection of transition rules which describe the DST rules of a given administrative region.
Definition: ZoneInfoLow.h:213
A time zone transition rule.
Definition: ZoneInfoLow.h:135
int8_t const toYear
TO year.
Definition: ZoneInfoLow.h:140
int8_t const fromYear
FROM year.
Definition: ZoneInfoLow.h:137
uint8_t const letterIndex
Determined by the LETTER column.
Definition: ZoneInfoLow.h:204
uint8_t const atTimeModifier
The atTimeModifier is a packed field containing 2 pieces of info:
Definition: ZoneInfoLow.h:178
int8_t const onDayOfMonth
Determined by the ON column.
Definition: ZoneInfoLow.h:161
uint8_t const onDayOfWeek
Determined by the ON column.
Definition: ZoneInfoLow.h:155
uint8_t const deltaCode
Determined by the SAVE column and contains the offset from UTC, in 15-min increments.
Definition: ZoneInfoLow.h:190
uint8_t const inMonth
Determined by the IN column.
Definition: ZoneInfoLow.h:143
uint8_t const atTimeCode
Determined by the AT column in units of 15-minutes from 00:00.
Definition: ZoneInfoLow.h:167