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
Closed
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
54 changes: 52 additions & 2 deletions Fields.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,54 @@ void setClockBackgroundFade(uint8_t value)
broadcastInt("clockBackgroundFade", clockBackgroundFade);
}

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);
}
Comment on lines +112 to +157
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.



String getSolidColor() {
return String(solidColor.r) + "," + String(solidColor.g) + "," + String(solidColor.b);
}
Expand Down Expand Up @@ -279,9 +327,11 @@ FieldList fields = {
{"autoplay", "Autoplay", BooleanFieldType, 0, 1, getAutoplay},
{"autoplayDuration", "Autoplay Duration", NumberFieldType, 0, 255, getAutoplayDuration},

{"clock", "Clock", SectionFieldType},
{"showClock", "Show Clock", BooleanFieldType, 0, 1, getShowClock},
{"clock", "Clock", SectionFieldType},
{"showClock", "Show Clock", BooleanFieldType, 0, 1, getShowClock},
{"clockBackgroundFade", "Background Fade", NumberFieldType, 0, 255, getClockBackgroundFade},
{"timeZone", "Time Zone", SelectFieldType, 0, timeZoneCount, getTimeZone, getTimeZones},
{"dst", "Daylight Saving", BooleanFieldType, 0, 1, getDst},

{"solidColorSection", "Solid Color", SectionFieldType},
{"solidColor", "Color", ColorFieldType, 0, 255, getSolidColor},
Expand Down
49 changes: 49 additions & 0 deletions TimeZones.h
Original file line number Diff line number Diff line change
@@ -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.

long utcOffsetInSeconds;
String utcOffset;
String region;
} TimeZone;
Comment on lines +1 to +5
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 NOT storing the strings in the server code. Rather, have the server code store the following:

int32_t utcOffsetInSeconds; // int32_t happens to match javascript native type
int16_t dstType; // index into static list of immutable algorithms to be applied

More info below. For fun, check out this video on why timezones are hard.

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....


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).

{-43200, "UTC−12:00", "Baker Island, Howland Island (both uninhabited)"},
{-39600, "UTC−11:00", "American Samoa, Niue"},
{-36000, "UTC−10:00", "Honolulu"},
{-34200, "UTC−09:30", "Marquesas Islands"},
{-32400, "UTC−09:00", "Anchorage"},
{-28800, "UTC−08:00", "Los Angeles, Vancouver, Tijuana"},
{-25200, "UTC−07:00", "Phoenix, Calgary, Ciudad Juárez"},
{-21600, "UTC−06:00", "Mexico City, Chicago, Guatemala City, Tegucigalpa, Winnipeg, San José, San Salvador"},
{-18000, "UTC−05:00", "New York, Toronto, Havana, Lima, Bogotá, Kingston"},
{-14400, "UTC−04:00", "Santiago, Santo Domingo, Manaus, Caracas, La Paz, Halifax"},
{-12600, "UTC−03:30", "St. John’s"},
{-10800, "UTC−03:00", "São Paulo, Buenos Aires, Montevideo"},
{ -7200, "UTC−02:00", "Brazil (Fernando de Noronha), South Georgia and the South Sandwich Islands"},
{ -3600, "UTC−01:00", "Cape Verde"},
{ 0, "UTC±00:00", "London, Dublin, Lisbon, Abidjan, Accra, Dakar"},
{ 3600, "UTC+01:00", "Berlin, Rome, Paris, Madrid, Warsaw, Lagos, Kinshasa, Algiers, Casablanca"},
{ 7200, "UTC+02:00", "Cairo, Johannesburg, Khartoum, Kiev, Bucharest, Athens, Jerusalem, Sofia"},
{ 10800, "UTC+03:00", "Moscow, Istanbul, Riyadh, Baghdad, Addis Ababa, Doha"},
{ 12600, "UTC+03:30", "Tehran"},
{ 14400, "UTC+04:00", "Dubai, Baku, Tbilisi, Yerevan, Samara"},
{ 16200, "UTC+04:30", "Kabul"},
{ 18000, "UTC+05:00", "Karachi, Tashkent, Yekaterinburg"},
{ 19800, "UTC+05:30", "Mumbai, Colombo"},
{ 20700, "UTC+05:45", "Kathmandu"},
{ 21600, "UTC+06:00", "Dhaka, Almaty, Omsk"},
{ 23400, "UTC+06:30", "Yangon"},
{ 25200, "UTC+07:00", "Jakarta, Ho Chi Minh City, Bangkok, Krasnoyarsk"},
{ 28800, "UTC+08:00", "Shanghai, Taipei, Kuala Lumpur, Singapore, Perth, Manila, Makassar, Irkutsk"},
{ 31500, "UTC+08:45", "Australia (Eucla) unofficial"},
{ 32400, "UTC+09:00", "Tokyo, Seoul, Pyongyang, Ambon, Yakutsk"},
{ 34200, "UTC+09:30", "Adelaide"},
{ 36000, "UTC+10:00", "Sydney, Port Moresby, Vladivostok"},
{ 37800, "UTC+10:30", "Lord Howe Island"},
{ 39600, "UTC+11:00", "Nouméa, Magadan"},
{ 43200, "UTC+12:00", "Auckland, Suva, Petropavlovsk-Kamchatsky"},
{ 45900, "UTC+12:45", "Chatham Islands"},
{ 46800, "UTC+13:00", "Kiribati (Phoenix Islands), Tonga, Tokelau"},
{ 50400, "UTC+14:00", "Kiribati (Line Islands)"}
};

const uint8_t timeZoneCount = ARRAY_SIZE(timeZones);
2 changes: 2 additions & 0 deletions data/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,8 @@ function postValue(name, value) {
"autoplayDuration",
"showClock",
"clockBackgroundFade",
"timeZone",
"dst",
];

if (oldFieldNames.some((f) => f === name)) {
Expand Down
23 changes: 23 additions & 0 deletions esp8266-fastled-webserver.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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.

uint8_t dst = 0; // daylight saving on/off

uint8_t currentPaletteIndex = 0;

Expand Down Expand Up @@ -286,6 +288,7 @@ PatternAndNameList patterns = {

const uint8_t patternCount = ARRAY_SIZE(patterns);

#include "TimeZones.h"
#include "Fields.h"

void setup() {
Expand Down Expand Up @@ -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

String value = webServer.arg("value");
setTimeZone(value.toInt());
sendInt(currentTimeZoneIndex);
});
Comment on lines +517 to +521
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.


webServer.on("/dst", HTTP_POST, []() {
String value = webServer.arg("value");
setDst(value.toInt());
sendInt(dst);
});
Comment on lines +523 to +527
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.

Comment on lines +523 to +527
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.


//list directory
webServer.on("/list", HTTP_GET, handleFileList);
//load editor
Expand Down Expand Up @@ -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.

if (currentTimeZoneIndex < 0)
currentTimeZoneIndex = 0;
else if (currentTimeZoneIndex >= timeZoneCount)
currentTimeZoneIndex = timeZoneCount -1;
dst = EEPROM.read(12);
timeClient.setTimeOffset(timeZones[currentTimeZoneIndex].utcOffsetInSeconds + (dst ? 3600 : 0));
}

void setPower(uint8_t value)
Expand Down