Skip to content

Commit 09175d4

Browse files
authored
Merge pull request #102 from bxparks/develop
merge 2.0 into master
2 parents bdd831d + 27a1430 commit 09175d4

File tree

599 files changed

+67224
-61955
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

599 files changed

+67224
-61955
lines changed

CHANGELOG.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,75 @@
11
# Changelog
22

33
* Unreleased
4+
* 2.0 (2022-11-04, TZDB 2022f) **Breaking Change** See
5+
[Migrating to 2.0.0](MIGRATING.md#MigratingToVersion200)
6+
* Change internal storage type of `year` component from `int8_t` to
7+
`int16_t`, extending the range of valid years from [-1873,2127] to
8+
[1,9999].
9+
* Remove `yearTiny()` getters and setters from `LocalDate`,
10+
`LocalDateTime`, `OffsetDateTime`, and `ZonedDateTime`.
11+
* They were not documented except in doxygen docs.
12+
* Remove from `LocalDate`:
13+
* `kInvalidYearTiny`, replaced with `kInvalidYear`
14+
* `kMinYearTiny`, replaced with `kMinYear`
15+
* `kMaxYearTiny`, replaced with `kMaxYear`
16+
* `forTinyComponents()`
17+
* Remove from `LocalDateTime`
18+
* `forTinyComponents()`
19+
* Update [AceTimeTools](https://github.com/bxparks/AceTimeTools)
20+
to generate `src/zonedb` and `src/zonedbx` using `int16_t` year types.
21+
* Extend `untilYear` of [zonedb](src/ace_time/zonedb) and
22+
[zonedbx](src/ace_time/zonedbx) databases to 10000
23+
* databases now valid over the years `[2000,10000)`
24+
* `zonedbx` adds 75 additional Rules for `kPolicyMorocco` (e.g.
25+
zone "Africe/Casablanca") due to the precalculated DST shifts which
26+
are listed in the IANA TZ DB up to the year 2087.
27+
* `zonedb` remains unchanged
28+
* Change epoch seconds conversion algorithm
29+
* Extract different epoch date conversion algorithms to be used/tested.
30+
Two of them are `EpochConverterJulian` and `EpochConverterHinnant`
31+
* `EpochConverterJulian` implements the algorithms found in
32+
wikipedia article https://en.wikipedia.org/wiki/Julian_day.
33+
* `EpochConverterHinnant` implements the algorithms found in
34+
https://howardhinnant.github.io/date_algorithms.html.
35+
* Migrate `LocalDate` to use the `EpochConverterHinnant` instead of
36+
`EpochConverterJulian`.
37+
* The primary reason is that I am able to fully understand the
38+
algorithms described in `EpochConverterHinnant`.
39+
* In contrast, I have almost no understanding of the algorithms
40+
implemented by `EpochConverterJulian`.
41+
* Configurable epoch year using new `Epoch` utility class
42+
* Add `Epoch::currentEpochYear()` which allows customization of the
43+
internal epoch year at startup.
44+
* Expected to be rarely used in user applications, but somewhat
45+
common in unit testing.
46+
* Add `Epoch::epochValidYearLower()` and `Epoch::epochValidYearUpper()`
47+
* Defines the 100-year interval which is +/- 50 years from the
48+
`currentEpochYear()` where the epoch seconds and time zone
49+
transition algorithms are guaranteed to be valid.
50+
* Add cache invalidation methods which must be called if
51+
`currentEpochYear()` is changed at runtime.
52+
* `ZoneProcessor::resetTransitionCache()`
53+
* `ZoneProcessorCache::resetZoneProcessors()`
54+
* `ZoneManager::resetZoneProcessors()`
55+
* Remove `toUnixSeconds()` and `forUnixSeconds()` which use the 32-bit
56+
versions of unix epoch seconds.
57+
* They will become invalid in the year 2038, and it's now the year 2022
58+
so it does not seem worth maintaining these.
59+
* The 64-bit versions `toUnixSeconds64()` and `forUnixSeconds64()` are
60+
retained.
61+
* Flash usage increases (see [MemoryBenchmark](examples/MemoryBenchmark) for
62+
more details:
63+
* AVR:
64+
* BasicZoneManager increases ~200 bytes
65+
* ExtendedZoneManager increases ~500 bytes
66+
* `zonedb` increases ~1.5 kiB
67+
* `zonedbx` increases ~3 kiB
68+
* ESP8266
69+
* BasicZoneManager increases ~50 bytes
70+
* ExtendedZoneManager increases ~150 bytes
71+
* `zonedb` increases ~300 bytes
72+
* `zonedbx` increases ~1.5 kiB
473
* 1.11.7 (2022-11-02, TZDB 2022f)
574
* Upgrade TZDB from 2022e to 2022f
675
* https://mm.icann.org/pipermail/tz-announce/2022-October/000075.html

MIGRATING.md

Lines changed: 187 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Table of Contents
44

5+
* [Migrating to v2.0.0](#MigratingToVersion200)
6+
* [High Level](#HighLevel200)
7+
* [Details](#Details200)
8+
* [Background Motivation](#Motivation200)
59
* [Migrating to v1.9.0](#MigratingToVersion190)
610
* [Configuring the Zone Managers](#ConfiguringZoneManagers)
711
* [Using the Zone Managers](#UsingZoneManagers)
@@ -11,6 +15,188 @@
1115
* [Migrating the DS3231Clock](#MigratingTheDS3231Clock)
1216
* [Migrating to LinkManagers](#MigratingToLinkManagers)
1317

18+
<a name="MigratingToVersion200"></a>
19+
## Migrating to v2.0
20+
21+
<a name="HighLevel200"></a>
22+
### High Level
23+
24+
The primary purpose of AceTime v2 is to extend the range of years supported by
25+
the library from `[2000,2050)` to `[2000,10000)`, while remaining compact enough
26+
to be useful on resource constrained environments like 8-bit AVR processors.
27+
AceTime uses a 32-bit integer to represent the "epoch seconds". This means that
28+
it has a range about 136 years. In version v1, the epoch was hardcoded to be
29+
2000-01-01T00:00:00, which allowed the "epoch seconds" to support years from
30+
about 1950 to 2050, which got truncated to about 2000 to 2050 for simplicity.
31+
However, in the year 2022, the upper limit of 2050 seems too close for comfort
32+
to use in embedded devices which could last more than 25 years.
33+
34+
One solution for extending the range of the "epoch seconds" is to use a 64-bit
35+
integer instead of a 32-bit integer. This solution is used by many other time
36+
zone libraries. However, 64-bit operations are extremely resource intensive on
37+
8-bit processors in terms of both flash memory and CPU cycles, and did not seem
38+
appropriate for AceTime which hopes to remain usable on 8-bit processors.
39+
40+
The solution used by AceTime v2 is to keep the "epoch seconds" as a 32-bit
41+
integer, but allow the **epoch year** to be adjustable instead of being
42+
hardcoded to the year 2000. Downstream applications can select an epoch year
43+
that is appropriate for their use case, allowing AceTime to be valid over
44+
roughly a 100-year interval straddling the epoch year. For example, if the epoch
45+
year is set to 2100 (so that the epoch is 2100-01-01T00:00:00), AceTime will
46+
work for the years 2050 to 2150. The assumption is that a 100-year interval is
47+
sufficient for most embedded applications.
48+
49+
With the epoch year being adjustable, it becomes necessary to decouple the
50+
various `year` fields in the TZDB database (implemented by the `zonedb` and
51+
`zonedbx` packages) from the `year` fields in the AceTime library code itself.
52+
Previously in v1, the `year` fields were encoded as 8-bit `int8_t` integers
53+
which were interpreted to be offsets from the hardcoded epoch year of 2000. To
54+
allow the zone databases to be valid until the year 10000, the internal `year`
55+
fields are changed from an `int8_t` to a 16-bit `int16_t` type. The AceTime API
56+
hides most of the impact of this change from client applications, so the biggest
57+
noticeable change may be the increase in flash size of the `zonedb` and
58+
`zonedbx` databases which are now 2.5 kiB to 3.5 kiB larger.
59+
60+
The increase in flash size for `zonedb` and `zonedbx` seems acceptable for the
61+
following reasons: The full impact of the size increase would be felt only if
62+
the application incorporated the entire `zonedb` or `zonedbx` database to
63+
support all 595 time zones in the TZDB database. But most 8-bit processors do
64+
not have enough flash memory to use the full database (e.g. 23 kiB for `zonedb`,
65+
38 kiB for `zonedbx`), so it is likely that these 8-bit applications would use
66+
only a small subset (1-10) of the timezones available in those databases. The
67+
flash size increase would be far smaller when using only small number of time
68+
zones. On 32-bit processors where the full database would likely be used, they
69+
often have far more flash memory (0.5 MiB to 4 MiB on ESP8266 or ESP32), so the
70+
increase of 2.5-3.5 kiB of flash memory would be negligible on those processors.
71+
72+
Some backwards incompatible changes were necessary from v1 to v2. These are
73+
explained in detail in the next section.
74+
75+
<a name="Details200"></a>
76+
### Details
77+
78+
AceTime v2 implements the following major changes and features:
79+
80+
* the internal `year` field in various classes (`LocalDate`, `LocalDateTime`,
81+
`OffsetDateTime`, `ZonedDateTime`) changes from `int8_t` to an `int16_t`
82+
* the range increases from `[1873,2127]` to `[1,9999]`
83+
* the various `year()` methods in these classes were already using `int16_t`
84+
so this internal change should be mostly invisible to client applications
85+
* the `year` fields in the `zonedb` and `zonedbx` databases also change from
86+
`int8_t` to `int16_t`
87+
* the year range increases from `[2000,2049]` to `[2000,9999]`
88+
* decouples the TZ database from the adjustable current epoch year
89+
* removed constants
90+
* `LocalDate::kEpochYear`
91+
* replacement: `Epoch::currentEpochYear()` function
92+
* reason: no longer a constant
93+
* `LocalDate::kSecondsSinceUnixEpoch`
94+
* purpose: number of seconds from 1970 to the AceTime epoch (2000-01-01
95+
in v1, but adjustable in v2)
96+
* replacement: `Epoch::secondsToCurrentEpochFromUnixEpoch64()`
97+
* reasons:
98+
* `int32_t` seconds can overflow, so use `int64_t`
99+
* epoch year is now adjustable, not a constant
100+
* `LocalDate::kDaysSinceUnixEpoch`
101+
* purpose: number of days from 1970-01-01 to AceTime epoch (2000-01-01
102+
in v1, but adjustable in v2)
103+
* replacement: `Epoch::daysToCurrentEpochFromUnixEpoch()`
104+
* reason: epoch is now adjustable, so must become a function
105+
* `LocalDate::kMinYearTiny`
106+
* replacement: `LocalDate::kMinYear`
107+
* reason: 8-bit offset no longer used, replaced by 16-bit integer
108+
* `LocalDate::kMaxYearTiny`
109+
* replacement: `LocalDate::kMaxYear`
110+
* reason: 8-bit offset no longer used, replaced by 16-bit integer
111+
* `LocalDate::kInvalidUnixDays`
112+
* replacement: `kInvalidEpochDays`
113+
* reason: simplification, both had the same value `INT32_MIN`
114+
* `LocalDate::kInvalidUnixSeconds`
115+
* replacement: `LocalDate::kInvalidUnixSeconds64`
116+
* reason: 32-bit versions of `toUnixSeconds()` removed
117+
* removed functions
118+
* `LocalDate::toUnixSeconds()`
119+
* reason: 32-bit Unix seconds will overflow in the year 2038
120+
* replacement: `LocalDate::toUnixSeconds64()`
121+
* `LocalDate::forUnixSeconds()`
122+
* reason: 32-bit Unix seconds will overflow in the year 2038
123+
* replacement: `LocalDate::forUnixSeconds64()`
124+
* `LocalDate::yearTiny()`
125+
* reason: `int8_t` year fields replaced by `int16_t` type
126+
* `LocalDate::forTinyComponents()` (undocumented)
127+
* reason: `int8_t` year fields replaced by `int16_t` type
128+
* `OffsetDateTime::toUnixSeconds()`
129+
* `OffsetDateTime::forUnixSeconds()`
130+
* `OffsetDateTime::yearTiny()`
131+
* `ZonedDateTime::toUnixSeconds()`
132+
* `ZonedDateTime::forUnixSeconds()`
133+
* `ZonedDateTime::yearTiny()`
134+
* new functions
135+
* `Epoch::currentEpochYear(epochYear)`
136+
* purpose: set the current epoch year
137+
* `Epoch::currentEpochYear()`
138+
* purpose: get the current epoch year
139+
* `Epoch::daysToCurrentEpochFromUnixEpoch()`
140+
* purpose: number of days from Unix epoch (1970-01-01) to
141+
the current epoch ({yyyy}-01-01) where `yyyy` is set by
142+
`currentEpochYear(yyyy)`
143+
* `Epoch::daysToCurrentEpochFromConverterEpoch()`
144+
* purpose: number of days from the converter epoch
145+
(2000-01-01T00:00:00) to the current epoch ({yyyy}-01-01T00:00:00)
146+
where `yyyy` is set by `currentEpochYear(yyyy)`
147+
* comment: should not normally be needed by client applications
148+
* `Epoch::secondsToCurrentEpochFromUnixEpoch64()`
149+
* purpose: number of seconds from the Unix epoch (1970-01-01T00:00:00)
150+
to the current epoch ({yyyy}-01-01T00:00:00)
151+
* comment: useful for converting between AceTime epoch and Unix epoch
152+
* `Epoch::epochValidYearLower()`
153+
* purpose: defines lower limit of valid years (`valid_year >= lower`)
154+
for features related to epochSeconds and timezones
155+
* `Epoch::epochValidYearUpper()`
156+
* purpose: defines upper limit of valid years (`valid_year < upper`)
157+
for features related to epochSeconds and timezones
158+
159+
The epochSeconds that was generated by AceTime v1 using the epoch year of 2000
160+
will be incompatible with AceTime v2 using a different epoch year. Client
161+
applications which need read old epochSeconds value using AceTime v2 have a
162+
number of options:
163+
164+
1) Call `Epoch::currentEpochYear(2000)` at the beginning of the application,
165+
so that the v2 epoch year is the same as the v1 epoch year. The disadvantage
166+
is that the 32-bit epochSeconds will stop working with this library sometime
167+
around the year 2065-2066.
168+
2) Perform a conversion of the v1 epochSeconds to the v2 epochSeconds by
169+
setting `Epoch::currentEpochYear(year)` first, then calculating the new
170+
epochSeconds using `newEpochSeconds = oldEpochSeconds -
171+
Epoch::daysToCurrentEpochFromConverterEpoch() * 86400`.
172+
3) Do no conversion. Just reset the date and time using the new epoch year.
173+
The next time the device is rebooted, the date and time will use the
174+
new epoch year instead of the old epoch year.
175+
176+
<a name="Motivation200"></a>
177+
### Background Motivation
178+
179+
Using 32-bit integer field for epochSeconds gives a range of about 136 years.
180+
This is the cause of the famous Unix [Year 2038
181+
problem](https://en.wikipedia.org/wiki/Year_2038_problem) which uses a 32-bit
182+
signed integer starting from the epoch year of 1970 (1970-01-01 00:00:00 UTC).
183+
184+
When the AceTime project started in 2018, using the year 2000 as the epoch year
185+
pushed the theoretical maximum year of epochSeconds to about 2068 which seemed
186+
sufficiently far enough away. The epoch year of 2000 also seemed convenient
187+
because it is the same value used by the [AVR
188+
libc](https://avr-libc.nongnu.org/) in its
189+
[time.h](https://avr-libc.nongnu.org/user-manual/group__avr__time.html)
190+
implementation. The actual upper limit was restricted to 2050 to provide some
191+
headroom before calculations would overflow in the year 2068.
192+
193+
Now in the year 2022, the upper limit of 2050 feels too low, since embedded
194+
devices could be reasonably expected to keep working for the next 25 years.
195+
The updated AceTime v2 is designed to support a 100-year interval from
196+
`[2000,2100)` by default. To prevent the need to change the source code when the
197+
range needs to extended even further in the future, the "current epoch year" is
198+
made adjustable by the client application.
199+
14200
<a name="MigratingToVersion190"></a>
15201
## Migrating to v1.9.0
16202

@@ -34,7 +220,7 @@ design consumed an extra 1100-1300 bytes of flash.
34220
In v1.9, several changes were made to reduce the flash memory size:
35221

36222
1. All virtual methods were removed from the `ZoneManager` and its
37-
subclasses.
223+
subclasses.
38224
2. The `BasicZoneManager` and `ExtendedZoneManager` classes are no longer
39225
template classes, making them easier to use (e.g. in the `ZoneSorterByName`
40226
and `ZoneSorterByOffsetAndName` classes).

0 commit comments

Comments
 (0)