Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve timezone support #2770

Merged
merged 8 commits into from
Jun 20, 2024
Merged

Conversation

mikee47
Copy link
Contributor

@mikee47 mikee47 commented Apr 23, 2024

This PR fills some gaps in Sming's support of time zones.

Time zones are a complex issue and best left to a separate library.
However, some minimal changes are required to the framework itself.

Fixes

  • Remove DT_DATE_SEPARATOR - no longer used

  • Add MINS_PER_HOUR definition

  • Don't restrict SystemClock timezone offset range. Can vary much more than plus/minus 12 hours.

  • Make core DateTime defines signed. Causes all sorts of hidden issues performing arithmetic otherwise.

  • Ensure DateTime:: fromHttpDate(), fromISO8601() and setTime() set weekday correctly.
    Requires full conversion to/from time_t, so add static versions of these methods which can also be used where broken-down time isn't required.
    Any failure in parsing means the DateTime value is left unchanged, rather than in an indeterminate state.

Add Zoned Time support

This uses DateTime::ZoneInfo and ZonedTime to provide a basic mechanism for dealing with zoned time information.
Handling rules, etc. must be handled with separate library.

ZoneInfo provides information associated with a point-in-time
ZonedTime contains a UTC timestamp (time_t) plus ZoneInfo

This naming is common amongst other libraries, e.g. java.

DateTime formatting now supports "z", ":z" and "%Z" format specifiers with provided (optional) ZoneInfo parameter.

Handle transitions to/from daylight savings

Adds a callback to the SystemClock so that the timezone offset can be maintained efficiently.
Update SystemClock_NTP sample and add to Windows CI builds.

Obtaining the local time involves converting the current system clock time from UTC using an offset based on the currently active timezone rules. The Timezone library is one way to deal with this, but not the only way of course.

There is currently no mechanism for keeping the system clock time offset correct, other than periodically setting it. For example, this is how the SystemClock_NTP sample currently does it. However, that's not ideal since we might only need to update via NTP infrequently; the two tasks aren't actually related.

This is the code added to the SystemClock_NTP sample, in the NtpClientDemo.cpp file:

void checkTimeZoneOffset(time_t systemTime)
{
	static time_t nextChange;
	static const TimeChangeRule* rule;

	if(!rule) {
		tz.toLocal(systemTime, &rule);
	} else if(systemTime < nextChange) {
                // For most calls we just return
		return;
	}

        // This happens on first call, or if switching from DST to STD or vice-versa (twice a year!)
	SystemClock.setTimeZoneOffset(rule->offset * SECS_PER_MIN);
	nextChange = tz.getNextChange(systemTime, &rule);
}

It's important to note that the system clock must be correctly set before this is called for the first time.
Therefore the call to SystemClock.onCheckTimeZoneOffset(checkTimeZoneOffset) is made only after setting the clock in our NTP callback. From then on, checkTimeZoneOffset is called whenever the local time is requested from SystemClock.

It's efficient because we note when the next change to/from DST is and so only need to check it, but will guarantee that the correct local time is returned without having to perform a full TimeChangeRule calculation every time.

NOTE: This isn't a perfect solution since there are ways to easily bypass the update check. Something the user might want to add into their applications is a periodic manual check, perhap?

Timezone library changes

Add POSIX timezone string support

Adds support for parsing POSIX timezone definition strings.

C library support for timezone generally involves code like this:

setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3");
tzset();

This approach is complex and inflexible so the code has been ported from newlib so that rules can be easily defined using these strings.

Provide standard timezone rule definitions

A set of timezone data can be built directly from the IANA database information.
This is limited to the standard POSIX string definitions.

The data itself is deliberately NOT included in the library as it changes frequently, sometimes several times a year.

Note: Whilst currently this data is generated statically for linking with applications,
future improvements to the library will consider mechanisms for keeping this information updated in a running system,
without having to re-flash the firmware.

Testing

Add extensive testing to validate generated rules and compare against OS-provided transition tables.

TODO:

  • Review timezone offset update mechanism
  • Review documentation
  • Review how timezone data is incorporated into applications; consider whether it's the right initial approach

@mikee47 mikee47 force-pushed the feature/timezone-changes branch from 487a0a9 to 1c2cf76 Compare April 23, 2024 20:10
@slaff slaff added this to the 5.2.0 milestone Apr 24, 2024
@mikee47 mikee47 force-pushed the feature/timezone-changes branch 2 times, most recently from d5aa6c4 to 1c2cf76 Compare April 28, 2024 08:58
@mikee47 mikee47 changed the title [WIP] Add timezone check callback to SystemClock [WIP] Improve timezone support Jun 17, 2024
@mikee47 mikee47 force-pushed the feature/timezone-changes branch 2 times, most recently from 7d0c23f to 2e6696e Compare June 17, 2024 07:35
mikee47 added 7 commits June 19, 2024 16:35
Can vary much more than plus/minus 12 hours
Causes all sorts of hidden issues performing arithmetic otherwise
This uses `DateTime::ZoneInfo` and `ZonedTime` to provide a basic mechanism for dealing with zoned time information.
Handling rules, etc. must be handled with separate library.
…kday correctly

Requires full conversion to/from time_t.
So add static versions of these methods which can also be used where broken-down time isn't required.
Any failure in parsing means the DateTime value is left unchanged, rather than in an indeterminate state.
@mikee47 mikee47 force-pushed the feature/timezone-changes branch from 2e6696e to 0569303 Compare June 19, 2024 15:36
@mikee47 mikee47 force-pushed the feature/timezone-changes branch from 0569303 to 6123664 Compare June 19, 2024 15:37
@mikee47 mikee47 changed the title [WIP] Improve timezone support Improve timezone support Jun 19, 2024
@mikee47 mikee47 requested a review from slaff June 19, 2024 18:55
@slaff slaff removed the 3 - Review label Jun 20, 2024
@slaff slaff merged commit 15836e3 into SmingHub:develop Jun 20, 2024
39 checks passed
@mikee47 mikee47 deleted the feature/timezone-changes branch June 20, 2024 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants