Skip to content

added: settings for customizing time zone and DST #191

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

Closed
wants to merge 1 commit into from

Conversation

AlexxBoo
Copy link

@AlexxBoo AlexxBoo commented Oct 6, 2020

I went ahead and added all Time Zones for selection in a dropdown list, as well as a setting for daylight saving.

@jasoncoon
Copy link
Owner

Awesome! I've been debating doing that, with a new field type and just putting the tz list in the web app. But this works as well, thank you!

@AlexxBoo
Copy link
Author

AlexxBoo commented Oct 9, 2020

Yeah, I did not think of this solution. 🤔 Having the list in the web-app would probably relieve communication to the backend, and building and maintaining the list would not use up program memory and processing resources. Hence that would arguably be the proper way I guess. But on the other hand then the web-app would have to deal with validation and value ranges. There would be no way for the backend to figure if a provided value would be valid, right?

Copy link
Collaborator

@henrygab henrygab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timezones are terribly complex.

I strongly recommend instead storing the following:

  1. int32_t corresponding to the UTC offset in seconds
  2. an enumeration value corresponding to a list of immutable DST algorithms (new algorithms == new index)
  3. int32_t indicating the offset to apply at "start" and to remove at "end" ... because we like countries in the southern hemisphere.
Value Enum Start End
0 None N/A N/A
1 US_2021 2AM the second Sunday in March 2AM the first Sunday in November
2 Old_CA_Labrador 12:01AM the second Sunday in March 12:01AM the first Sunday in November
3 MX_2021 2AM on first Sunday in April 2AM last Sunday in October
4 Old_Cuba Midnight (CUST) the second Sunday in March 1AM (CUDT) the first Sunday in November
5 EU_2021 1AM GMT last Sunday in March 1AM GMT last Sunday in October

Critically, note that algorithm 5 uses GMT to define when the shift occurs, regardless of the local time zone applied.

String utcOffset;
String region;
} TimeZone;
typedef TimeZone TimeZoneList[];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend removing this typedef. You call it a "list", but it's actually an array, which is a different data structure....

} TimeZone;
typedef TimeZone TimeZoneList[];

const TimeZoneList timeZones = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend not storing string values in this data structure.
Instead, accept any provided value (in range), and have the HTML page list them explicitly. Below, I recommend storing only the offset on the device (server).

@@ -511,6 +514,18 @@ void setup() {
sendInt(clockBackgroundFade);
});

webServer.on("/timeZone", HTTP_POST, []() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the server side, I would recommend AGAINST storing the setting as an index into the array. Instead, I would recommend allowing the user to indicate any offset from UTC they want. Storing that value == stability across releases, and when geopolitical things change.

Although not required here, if wanting to display multiple options for the same time zone offset (e.g., list London separately from Dakar), can also simply store a stable differentiator (e.g., int) that the client can use to find/display the proper string.

Why do this?

  • Allows the time zone to be set to any value, including time zones not listed.
    • e.g., Newfoundland (Canada) is UTC-4:30
  • Allows updates due to changes in world geopolitics to be done entirely in HTML pages, not in server code

Comment on lines +523 to +527
webServer.on("/dst", HTTP_POST, []() {
String value = webServer.arg("value");
setDst(value.toInt());
sendInt(dst);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Daylight Saving Time is not a boolean True/False. Currently, those timezones that use DST all happen to offset by exactly one hour. However, when DST begins and ends is not always the same.

Recommend to change this to store the following data:

  • DST Start (datetime)
  • DST End (datetime)
  • DST Offset (default == 1 hour)

Want to learn more? See http://ontimezone.com/exceptions.php.

@@ -0,0 +1,49 @@
typedef struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly recommend against storing the strings in the server. Rather, store only in the client (e.g., HTML webpage). In addition, I would not store the index to the array, but rather the utcOffsetInSeconds itself. More reasons given below.

For fun, see video of why timezones are hard.

Comment on lines +112 to +157
String getTimeZone() {
return String(currentTimeZoneIndex);
}

String getTimeZones() {
String json = "";

for (uint8_t i = 0; i < timeZoneCount; i++) {
json += "\"" + timeZones[i].utcOffset + " – " + timeZones[i].region + "\"";
if (i < timeZoneCount - 1)
json += ",";
}

return json;
}

String setTimeZone(uint8_t value)
{
if (value >= timeZoneCount)
value = timeZoneCount - 1;

currentTimeZoneIndex = value;

EEPROM.write(11, currentTimeZoneIndex);
EEPROM.commit();

timeClient.setTimeOffset(timeZones[currentTimeZoneIndex].utcOffsetInSeconds + (dst ? 3600 : 0));

broadcastInt("timeZone", currentTimeZoneIndex);
}

String getDst() {
return String(dst);
}

String setDst(uint8_t value)
{
dst = value == 0 ? 0 : 1;

EEPROM.write(12, dst);
EEPROM.commit();

timeClient.setTimeOffset(timeZones[currentTimeZoneIndex].utcOffsetInSeconds + (dst ? 3600 : 0));

broadcastInt("dst", dst);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These would all change, if accepting my recommendations below.

Comment on lines +517 to +521
webServer.on("/timeZone", HTTP_POST, []() {
String value = webServer.arg("value");
setTimeZone(value.toInt());
sendInt(currentTimeZoneIndex);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be an index into an array, because the first change in the list (adding/removing a timezone) will either make the list not sorted, or will cause any stored values on existing devices to change timezone on upgrade. That's just a bug waiting to happen.

Comment on lines +523 to +527
webServer.on("/dst", HTTP_POST, []() {
String value = webServer.arg("value");
setDst(value.toInt());
sendInt(dst);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Daylight Saving Time is, unfortunately, not a boolean. Different areas of the world start / stop DST at different dates and/or times.

Existing rules that I am aware of include:
0. No DST -- Yay!

  1. US_Canada_2021 -- starts at 2AM second Sunday in March, ends 2AM first Sunday in November
  2. 2010_Newfoundland -- starst at 12:01AM, ends 12:01AM (same dates as US) ... NO LONGER USED AS OF 2011
  3. Mexico_2021 -- starts at 2AM on first Sunday in April, ends 2AM last Sunday in October
  4. Cuba_2013 -- Starts at midnight (standard time) ... same dates as US.
  5. EU_Britain -- Starts 1AM GMT (regardless of timezone) last Sunday in March, Ends last Sunday in October. NOTE THE USE OF GMT FOR CALCULATION OF START/STOP

See, for example, http://ontimezone.com/exceptions.php for some differences, or view the fun video on timezones linked above.

@@ -120,6 +120,8 @@ unsigned long autoPlayTimeout = 0;

uint8_t showClock = 0;
uint8_t clockBackgroundFade = 240;
uint8_t currentTimeZoneIndex = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would now be called currentTimeZoneAlgorithm, for example.

@@ -921,6 +936,14 @@ void loadSettings()

showClock = EEPROM.read(9);
clockBackgroundFade = EEPROM.read(10);

currentTimeZoneIndex = EEPROM.read(11);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would need updates if accepting the above recommendations.

In short:
Store UTC offset directly.
Store DST as an enumeration corresponding to a fixed list of algorithms.

@henrygab
Copy link
Collaborator

@AlexxBoo ... I think we should close this PR, in view of #213. Let us know if you disagree.

@AlexxBoo
Copy link
Author

Hi @henrygab, thank you so much for the feedback (sorry for my late response...)
I'm totally fine with closing this PR, seeing that the initially intended functionality is properly finding its way into the software.

@AlexxBoo AlexxBoo closed this Dec 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants