Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog

- unreleased
- 4.1.0 (2025-11-17, TZDB version 2025b)
- **Breaking** Replace `uint8_t ZonedExtra.type()` with `Resolved
ZonedExtra.resolved()`
- `ZonedExtra::resolved()` has the same behavior and semantics as
`ZonedDateTime.resolved()`
- 4.0.0 (2025-10-21, TZDB version 2025b)
- See [MIGRATING.md](MIGRATING.md) on breaking API changes, and how to
migrate.
Expand Down
53 changes: 25 additions & 28 deletions MIGRATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,31 @@

## Table of Contents

* [Migrating to v4.0.0](#MigratingToVersion400)
* [Migrating to v3.0.0](#MigratingToVersion300)
* [Migrating to v2.3.0](#MigratingToVersion220)
* [Migrating to v2.2.0](#MigratingToVersion220)
* [Migrating to v2.1.0](#MigratingToVersion210)
* [Migrating to v2.0.0](#MigratingToVersion200)
* [Migrating to v1.9.0](#MigratingToVersion190)
* [Migrating to v1.8.0](#MigratingToVersion180)

<a name="MigratingToVersion400"></a>
* [Migrating to v4.1.0](#migrating-to-v410)
* [Migrating to v4.0.0](#migrating-to-v400)
* [Migrating to v3.0.0](#migrating-to-v300)
* [Migrating to v2.3.0](#migrating-to-v230)
* [Migrating to v2.2.0](#migrating-to-v220)
* [Migrating to v2.1.0](#migrating-to-v210)
* [Migrating to v2.0.0](#migrating-to-v200)
* [Migrating to v1.9.0](#migrating-to-v190)
* [Migrating to v1.8.0](#migrating-to-v180)

## Migrating to v4.1.0

The `uint8_t ZonedExtra::type()` function is replaced with `Resolved
ZonedExtra::resolved()` which returns an enum of type `Resolved`. The new
function has exactly the same behavior and semantics as
`ZonedDateTime::resolved()` which simplifies the implementation and the usage of
the library.

Here is the mapping from the old values to the new enum:

- `kTypeNotFound` -> `Resolved::kError`
- `kTypeExact` -> `Resolved::kUnique`
- `kTypeGap` -> `Resolved::kGapEarlier` or `Resolved::kGapLater`
- `kTypeOverlap` -> `Resolved::kOverlapEarlier` or `Resolved::kOverlapLater`

## Migrating to v4.0.0

These changes were originally intended for 3.0.0, but I ran out of time, so I
Expand Down Expand Up @@ -93,7 +108,6 @@ the `resolved` parameter which is an enum type of `Resolved`. It has 5 options:
If the calling code does not care about how an ambiguity was resolved, then this
parameter can be ignored.

<a name="MigratingToVersion300"></a>
## Migrating to v3.0.0

### Info Container Class
Expand Down Expand Up @@ -126,7 +140,6 @@ which may have leaked out is `ace_time::internal::kAbbrevSize` which is the size
of the string buffer needed to hold the longest TimeZone abbreviation (e.g.
"PST"). This constant is now at `ace_time::kAbbrevSize`.

<a name="MigratingToVersion230"></a>
## Migrating to v2.3.0

The internal implementation details of various classes have changed
Expand Down Expand Up @@ -210,7 +223,6 @@ we can be confident that all algorithms (`BasicZoneProcessor`,
`zonedbx`, `zonedbc`) in the AceTime library are consistent with these third
party libraries.

<a name="MigratingToVersion220"></a>
## Migrating to v2.2

### Immutable TimeZone class
Expand Down Expand Up @@ -250,10 +262,8 @@ TimeZone newTz = TimeZone::forTimeOffset(
ZonedDateTime newDt = zdt.convertToTimeZone(newTz);
```

<a name="MigratingToVersion210"></a>
## Migrating to v2.1

<a name="UnifiedLinks"></a>
### Unified Links

Over the years, I implemented 4 different versions of the Link entries:
Expand Down Expand Up @@ -307,7 +317,6 @@ only 2 methods which apply only to Link time zones:
* Prints the name of the target Zone if the current zone is a link.
* It prints nothing is `isLink()` is false.

<a name="ZonedExtra"></a>
### ZonedExtra

The `ZonedExtra` class was created to replace 3 ad-hoc query methods on the
Expand All @@ -333,10 +342,8 @@ The `ZonedExtra` object provides access to other meta-information about the time
zone at that particular time. See the [ZonedExtra](USER_GUIDE.md#ZonedExtra)
section in the `USER_GUIDE.md` for more detailed information about this class.

<a name="MigratingToVersion200"></a>
## Migrating to v2.0

<a name="HighLevel200"></a>
### High Level

The primary purpose of AceTime v2 is to extend the range of years supported by
Expand Down Expand Up @@ -390,7 +397,6 @@ increase of 2.5-3.5 kiB of flash memory would be negligible on those processors.
Some backwards incompatible changes were necessary from v1 to v2. These are
explained in detail in the next section.

<a name="Details200"></a>
### Details

AceTime v2 implements the following major changes and features:
Expand Down Expand Up @@ -491,7 +497,6 @@ number of options:
The next time the device is rebooted, the date and time will use the
new epoch year instead of the old epoch year.

<a name="Motivation200"></a>
### Background Motivation

Using 32-bit integer field for epochSeconds gives a range of about 136 years.
Expand All @@ -515,13 +520,11 @@ The updated AceTime v2 is designed to support a 100-year interval from
range needs to extended even further in the future, the "current epoch year" is
made adjustable by the client application.

<a name="MigratingToVersion190"></a>
## Migrating to v1.9.0

The `ZoneManager` hierarchy (containing `ManualZoneManager`, `BasicZoneManager`,
and `ExtendedZoneManager`) was refactored from v1.8.0 to v1.9.0.

<a name="ConfiguringZoneManagers"></a>
### Configuring the Zone Managers

In v1.8, the `ZoneManager` was an abstract interface class with 7 pure virtual
Expand Down Expand Up @@ -588,7 +591,6 @@ ExtendedoneManager zoneManager(
zoneProcessorCache);
```

<a name="UsingZoneManagers"></a>
### Using the Zone Managers

In v1.8, the `ZoneManager` was the parent interface class of all polymorphic
Expand Down Expand Up @@ -652,7 +654,6 @@ It is assumed that most applications will hard code either the
`BasicZoneManager` or the `ExtendedZoneManager`, and will not need this level
of configuration.

<a name="LinkManagers"></a>
### Link Managers

In v1.8, the `LinkManager` was an interface class with pure virtual methods:
Expand Down Expand Up @@ -684,7 +685,6 @@ The `BasicLinkManager` and `ExtendedLinkManager` should be used directly,
instead of through the `LinkManager` interface. Since Link Managers were
introduced only in v1.8, I expect almost no one to be affected by this.

<a name="MigratingToVersion180"></a>
## Migrating to v1.8.0

Three breaking changes were made from v1.7.5 to v1.8.0:
Expand All @@ -708,7 +708,6 @@ Three breaking changes were made from v1.7.5 to v1.8.0:
The following subsections show how to migrate client application from
AceTime v1.7.5 to AceTime v1.8.0.

<a name="MigratingToAceTimeClock"></a>
### Migrating to AceTimeClock

For AceTime v1.8.0, the clock classes under the `ace_time::clock` namespace have
Expand Down Expand Up @@ -738,7 +737,6 @@ using namespace ace_time;
using namespace ace_time::clock;
```

<a name="MigratingTheDS3231Clock"></a>
### Migrating the DS3231Clock

For AceTime v1.8.0, the `DS3231Clock` class was converted into a template class
Expand Down Expand Up @@ -834,7 +832,6 @@ consumption by 1500 bytes on an AVR processor. The flash consumption can be
reduced by 2000 bytes if the "fast" version `SimpleWireFastInterface` is used
instead.

<a name="MigratingToLinkManagers"></a>
### Migrating to LinkManagers

In v1.7.5, [thin links](USER_GUIDE.md#ThinLinks) were activated by adding the
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ epoch of 1970-01-01 is provided through conversion functions of the `time_t`
type. Only the 64-bit version of the `time_t` type is supported to avoid the
[Year 2038 Problem](https://en.wikipedia.org/wiki/Year_2038_problem).

The library does *not* support [leap
seconds](https://en.wikipedia.org/wiki/Leap_second) and ignores them. Instead it
uses [UNIX time](https://en.wikipedia.org/wiki/Unix_time) (aka POSIX time) where
the *POSIX second* is variable in duration compared to the [SI
second](https://en.wikipedia.org/wiki/Second). During a leap second, a POSIX
second is conceptually equal to 2 SI seconds, and the POSIX clock changes from
`23:59:58` to `23:59:59`, then is held for 2 seconds before rolling over to
`00:00:00`. Most real-time clock (RTC) chips do not support leap seconds either,
so the final `23:59:59` second will be held for only one second instead of two,
so a clock using AceTime with such an RTC chip will be off by one second after a
leap second compared to the atomic UTC clock.

The companion library [AceTimeClock](https://github.com/bxparks/AceTimeClock)
provides Clock classes to retrieve the time from more accurate sources, such as
an [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol) server, or a
Expand Down Expand Up @@ -81,14 +93,14 @@ This library can be an alternative to the Arduino Time
(https://github.com/JChristensen/Timezone) libraries.

**Major Changes in v4.0**: Rename `LocalDate` to `PlainDate`; `LocalTime` to
`PlainTime`; `LocalDateTime` to `PlainDateTime. Backwards compatible macros are
`PlainTime`; `LocalDateTime` to `PlainDateTime`. Backwards compatible macros are
provided, so most existing programs should still compile. See [Migrating to
v4.0](MIGRATING.md#MigratingToVersion400) for more details. Add `zonedb2025` and
`zonedbx2025` databases which contain DST transitions for year >= 2025, which
reduces flash memory size. Replace `fold` parameter with `disambiguate` (input)
and `resolved` (output) parameters.

**Version**: 4.0.0 (2025-10-21, TZDB 2025b) \
**Version**: 4.1.0 (2025-11-17, TZDB 2025b) \
**Changelog**: [CHANGELOG.md](CHANGELOG.md) \
**Migration**: [MIGRATING.md](MIGRATING.md) \
**User Guide**: [USER_GUIDE.md](USER_GUIDE.md)
Expand Down Expand Up @@ -509,6 +521,7 @@ until 2200.
- [C++11/14/17 Hinnant date](https://github.com/HowardHinnant/date) library
- [GNU libc time](https://www.gnu.org/software/libc/libc.html) library
- [C# Noda Time](https://nodatime.org) library
- [Python whenever](https://pypi.org/project/whenever/)
- [acetimec](https://github.com/bxparks/acetimec) - C version of AceTime
- [acetimego](https://github.com/bxparks/acetimego) - Go or TinyGo version of
AceTime
Expand Down
50 changes: 29 additions & 21 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The IANA TZ database is programmatically generated into 5 predefined databases:
ranges, and are designed to work with different `ZoneProcessor` and
`ZoneManager` classes.

**Version**: 4.0.0 (2025-10-21, TZDB 2025b)
**Version**: 4.1.0 (2025-11-17, TZDB 2025b)

**Related Documents**:

Expand Down Expand Up @@ -1616,7 +1616,7 @@ class ZonedExtra {
const char* abbrev);

bool isError() const;
uint8_t type() const;
Resolved resolved() const;

TimeOffset timeOffset() const; // stdOffset + dstOffset
TimeOffset stdOffset() const;
Expand All @@ -1640,9 +1640,10 @@ methods on the `ZonedExtra` class:
uint8_t hour, uint8_t minute, uint8_t second, const TimeZone& tz,
Disambiguate disambiguate = Disambiguate::kCompatible)`

Often the `ZonedDateTime` will be created first from the epochSeconds, then the
`ZonedExtra` will be created to access additional information about the time
zone at that particular epochSeconds (e.g. abbreviation):
Often the `ZonedDateTime` will be created first using the `forComponents()` or
`forEpochSeconds()` function, then the `ZonedExtra` will be created to access
additional information about the time zone at that particular epochSeconds or
components (e.g. abbreviation):

```C++
ExtendedZoneProcessor zoneProcessor;
Expand All @@ -1654,9 +1655,11 @@ ZonedDateTime zdt = ZonedDateTime::forEpochSeconds(epochSeconds, tz);
ZonedExtra ze = ZonedExtra::forEpochSeconds(epochSeconds, tz);
```

The `ZonedExtra::type()` parameter identifies whether the given time instant
corresponded to a DST gap, or a DST overlap, or an exact match with no
duplicates.
The `ZonedExtra::resolved()` parameter indicates how `forComponents()` function
resolved a gap or an overlap according to the `disambiguate` parameter. For the
`forEpochSeconds()` function, the resolved parameter will always be
`Resolved::kUnique`. If the `ZonedExtra` instance is an error, then `resolved()`
will return `Resolved::kError`.

The `ZonedExtra::stdOffset()` is the standard offset of the timezone at the
given time instant. For example, for `America/Los_Angeles` this will return
Expand All @@ -1683,10 +1686,12 @@ abbreviation is 6 characters long.

The `ZonedExtra::reqStdOffset()` and `ZonedExtra::reqDstOffset()` are relevant
and different from the corresponding `stdOffset()` and `dstOffset()` only if the
`type()` is `kTypeGap`. This occurs only if the `ZonedExtra::forComponents()`
factory method is used. The `reqStdOffset()` and `reqDstOffset()` are
derived from the transition line that is used to select the earlier or later
`PlainDateTime` instance to `epochSeconds`.
requested `PlainDateTime` was in a gap. In other words, if the
`ZonedExtra::resolved()` is `Resolved::kGapBefore` or `Resolved::kGapAfter`.
This occurs only if the `ZonedExtra::forComponents()` factory method is used.
The `reqStdOffset()` and `reqDstOffset()` are derived from the transition line
that is used to select the earlier or later `PlainDateTime` instance to
`epochSeconds`.

The `isError()` method returns true if the given `PlainDateTime` or
`epochSeconds` represents an error condition.
Expand Down Expand Up @@ -2155,10 +2160,11 @@ by JavaScript Temporal and the Python whenever libraries.

#### Factory Methods with Disambiguation

There are 2 main factory methods on `ZonedDateTime`: `forEpochSeconds()` and
`forComponents()`. The `disambiguate` parameter applies to only the
`forComponents()` method. The `forEpochSeconds()` function always corresponds to
a unique `ZonedDateTime` object and does not need a `disambiguate` argument.
The `ZonedDateTime` and `ZonedExtra` classes have 2 factory methods:
`forEpochSeconds()` and `forComponents()`. The `disambiguate` parameter applies
to only the `forComponents()` method. The `forEpochSeconds()` function always
corresponds to a unique `ZonedDateTime` object and does not need a
`disambiguate` argument.

The `disambiguate` parameter is an enum type that takes 4 values:

Expand All @@ -2178,12 +2184,14 @@ then the `disambiguate` parameter has no effect, because it maps to a unique

#### Resolved Disambiguation

When the `forComponents()` method returns a `ZonedDateTime`, it is sometimes
useful to know how the `disambiguate` parameter selected the result. The
`ZonedDateTime` object exposes a `ZonedDateTime::resolved()` variable. It
takes 5 values:
The `ZonedDateTime::forComponents()` method and the
`ZonedExtra::forComponents()` functions accept the `disambiguate` parameter to
control what happens during a gap or an overlap. The resulting `ZonedDateTime`
or `ZonedExtra` exposes a `resolved()` function that returns an enum of type
`Resolved` which takes 6 values:

- `Resolved::kUnique` - the ZonedDateTime is unique
- `Resolved::kError` - the result was not found or an error occurred
- `Resolved::kUnique` - the ZonedDateTime or ZonedExtra is unique
- `Resolved::kOverlapEarlier` - the earlier time in an overlap was selected
- `Resolved::kOverlapLater` - the later time in an overlap was selected
- `Resolved::kGapEarlier` - the earlier time in a gap was selected
Expand Down
27 changes: 22 additions & 5 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
# Documentation

* The `html` directory contains the programmatically generated
[Doxygen docs](https://bxparks.github.io/AceTime/html/) which are viewable on
GitHub Pages.
* The other files are various subsections of the User Guide which are linked
from the main [README.md](../README.md).
The main [README.md](../README.md) and [USER_GUIDE.md](../USER_GUIDE.md)
contain the majority of the documentation.

This directory contains scripts to generate [doxygen](https://www.doxygen.nl/)
documentation from the embedded docstrings in the code.

First, install doxygen and GNU Make if you don't already have them, with
something like the following on an Ubuntu Linux machine:

```
$ sudo apt install doxygen make
```

Second, run the `make` command to generate the HTML files under the `html`
directory:

```
$ cd docs
$ make
```

Third, open the `./docs/html/index.html` file in your web browser.
2 changes: 1 addition & 1 deletion docs/doxygen.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ PROJECT_NAME = AceTime
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = 3.0.0
PROJECT_NUMBER = 4.1.0

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
2 changes: 1 addition & 1 deletion examples/AutoBenchmark/Benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ void disableOptimization(uint32_t value) {
}

void disableOptimization(const ZonedExtra& extra) {
guard ^= extra.type() & 0xff;
guard ^= (uint8_t)extra.resolved() & 0xff;
guard ^= extra.timeOffset().toMinutes() & 0xff;
guard ^= *extra.abbrev();
}
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=AceTime
version=4.0.0
version=4.1.0
author=Brian T. Park <[email protected]>
maintainer=Brian T. Park <[email protected]>
sentence=Date, time, timezone classes for Arduino supporting the full IANA TZ Database to convert epoch seconds to date and time components in different time zones.
Expand Down
4 changes: 2 additions & 2 deletions src/AceTime.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
#include "zonedbc/zone_registry.h"

// Version format: xxyyzz == "xx.yy.zz"
#define ACE_TIME_VERSION 40000
#define ACE_TIME_VERSION_STRING "4.0.0"
#define ACE_TIME_VERSION 40100
#define ACE_TIME_VERSION_STRING "4.1.0"

#endif
Loading