Merge pull request #115 from jpirnay/fix-halclock

fix: Add additional time zones
This commit is contained in:
jpirnay
2026-04-20 21:24:50 +02:00
committed by GitHub
8 changed files with 69 additions and 21 deletions
+5 -1
View File
@@ -112,7 +112,8 @@ STR_SHOW_FILE_EXTENSIONS: "Show File Extensions"
STR_USE_CLOCK: "Use Clock" STR_USE_CLOCK: "Use Clock"
STR_USE_WEATHER: "Use Weather" STR_USE_WEATHER: "Use Weather"
STR_CLOCK_SETTINGS: "Clock Settings" STR_CLOCK_SETTINGS: "Clock Settings"
STR_CLOCK_SETTINGS_WARNING: "Uses more battery; clock may drift" STR_CLOCK_SETTINGS_WARNING_BATTERY: "Uses more battery"
STR_CLOCK_SETTINGS_WARNING_DRIFT: "Clock may drift"
STR_CLOCK: "Clock" STR_CLOCK: "Clock"
STR_CLOCK_FORMAT: "Clock Format" STR_CLOCK_FORMAT: "Clock Format"
STR_TIMEZONE: "Timezone" STR_TIMEZONE: "Timezone"
@@ -125,6 +126,9 @@ STR_TZ_EST: "US Eastern (EST/EDT)"
STR_TZ_CST: "US Central (CST/CDT)" STR_TZ_CST: "US Central (CST/CDT)"
STR_TZ_MST: "US Mountain (MST/MDT)" STR_TZ_MST: "US Mountain (MST/MDT)"
STR_TZ_PST: "US Pacific (PST/PDT)" STR_TZ_PST: "US Pacific (PST/PDT)"
STR_TZ_AST_ADT: "Atlantic Canada (AST/ADT)"
STR_TZ_ACST_ACDT: "Australia Central (ACST/ACDT)"
STR_TZ_AKST_AKDT: "Alaska (AKST/AKDT)"
STR_TZ_AEST: "Australia Eastern (AEST/AEDT)" STR_TZ_AEST: "Australia Eastern (AEST/AEDT)"
STR_TZ_NZST: "New Zealand (NZST/NZDT)" STR_TZ_NZST: "New Zealand (NZST/NZDT)"
STR_TZ_MSK: "Russia (MSK)" STR_TZ_MSK: "Russia (MSK)"
+3
View File
@@ -111,6 +111,9 @@ static constexpr TimeZoneEntry TIMEZONES[] = {
{"CST6CDT,M3.2.0/2,M11.1.0/2"}, {"CST6CDT,M3.2.0/2,M11.1.0/2"},
{"MST7MDT,M3.2.0/2,M11.1.0/2"}, {"MST7MDT,M3.2.0/2,M11.1.0/2"},
{"PST8PDT,M3.2.0/2,M11.1.0/2"}, {"PST8PDT,M3.2.0/2,M11.1.0/2"},
{"AST4ADT,M3.2.0/2,M11.1.0/2"},
{"ACST-9:30ACDT,M10.1.0/2,M4.1.0/3"},
{"AKST9AKDT,M3.2.0/2,M11.1.0/2"},
}; };
// ---- NVS helpers ---------------------------------------------------------- // ---- NVS helpers ----------------------------------------------------------
+3
View File
@@ -184,6 +184,9 @@ class CrossPointSettings {
TZ_CST = 13, TZ_CST = 13,
TZ_MST = 14, TZ_MST = 14,
TZ_PST = 15, TZ_PST = 15,
TZ_AST_ADT = 16,
TZ_ACST_ACDT = 17,
TZ_AKST_AKDT = 18,
TIMEZONE_COUNT TIMEZONE_COUNT
}; };
@@ -2,22 +2,17 @@
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <HalClock.h> #include <HalClock.h>
#include <HalGPIO.h>
#include <I18n.h> #include <I18n.h>
#include <string>
#include "CrossPointSettings.h" #include "CrossPointSettings.h"
#include "DetectTimezoneActivity.h" #include "DetectTimezoneActivity.h"
#include "SyncTimeActivity.h" #include "SyncTimeActivity.h"
#include "components/UITheme.h" #include "components/UITheme.h"
#include "fontIds.h" #include "fontIds.h"
namespace {
const StrId timeZoneNames[CrossPointSettings::TIMEZONE_COUNT] = {
StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK,
StrId::STR_TZ_UTC_PLUS4, StrId::STR_TZ_IST, StrId::STR_TZ_UTC_PLUS7, StrId::STR_TZ_UTC_PLUS8,
StrId::STR_TZ_UTC_PLUS9, StrId::STR_TZ_AEST, StrId::STR_TZ_NZST, StrId::STR_TZ_UTC_MINUS3,
StrId::STR_TZ_EST, StrId::STR_TZ_CST, StrId::STR_TZ_MST, StrId::STR_TZ_PST};
} // namespace
void ClockSettingsActivity::buildMenuItems() { void ClockSettingsActivity::buildMenuItems() {
menuItems.reserve(6); menuItems.reserve(6);
menuItems.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_TITLE)); menuItems.push_back(SettingInfo::Separator(StrId::STR_SETTINGS_TITLE));
@@ -25,13 +20,16 @@ void ClockSettingsActivity::buildMenuItems() {
SettingInfo::Toggle(StrId::STR_USE_CLOCK, &CrossPointSettings::useClock, "useClock", StrId::STR_CAT_SYSTEM)); SettingInfo::Toggle(StrId::STR_USE_CLOCK, &CrossPointSettings::useClock, "useClock", StrId::STR_CAT_SYSTEM));
menuItems.push_back(SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat12h, menuItems.push_back(SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat12h,
{StrId::STR_24H, StrId::STR_12H}, "clockFormat12h", StrId::STR_CAT_SYSTEM)); {StrId::STR_24H, StrId::STR_12H}, "clockFormat12h", StrId::STR_CAT_SYSTEM));
menuItems.push_back( {
SettingInfo::Enum(StrId::STR_TIMEZONE, &CrossPointSettings::timeZone, auto tzSetting = SettingInfo::Enum(
{StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, StrId::STR_TIMEZONE, &CrossPointSettings::timeZone,
StrId::STR_TZ_UTC_PLUS4, StrId::STR_TZ_IST, StrId::STR_TZ_UTC_PLUS7, StrId::STR_TZ_UTC_PLUS8, {StrId::STR_TZ_UTC, StrId::STR_TZ_CET, StrId::STR_TZ_EET, StrId::STR_TZ_MSK, StrId::STR_TZ_UTC_PLUS4,
StrId::STR_TZ_UTC_PLUS9, StrId::STR_TZ_AEST, StrId::STR_TZ_NZST, StrId::STR_TZ_UTC_MINUS3, StrId::STR_TZ_IST, StrId::STR_TZ_UTC_PLUS7, StrId::STR_TZ_UTC_PLUS8, StrId::STR_TZ_UTC_PLUS9,
StrId::STR_TZ_EST, StrId::STR_TZ_CST, StrId::STR_TZ_MST, StrId::STR_TZ_PST}, StrId::STR_TZ_AEST, StrId::STR_TZ_NZST, StrId::STR_TZ_UTC_MINUS3, StrId::STR_TZ_EST, StrId::STR_TZ_CST,
"timeZone", StrId::STR_CAT_SYSTEM)); StrId::STR_TZ_MST, StrId::STR_TZ_PST, StrId::STR_TZ_AST_ADT, StrId::STR_TZ_ACST_ACDT, StrId::STR_TZ_AKST_AKDT},
"timeZone", StrId::STR_CAT_SYSTEM);
menuItems.push_back(std::move(tzSetting));
}
menuItems.push_back(SettingInfo::Action(StrId::STR_DETECT_TIMEZONE, SettingAction::DetectTimezone) menuItems.push_back(SettingInfo::Action(StrId::STR_DETECT_TIMEZONE, SettingAction::DetectTimezone)
.withSubcategory(StrId::STR_READER_TOOLS)); .withSubcategory(StrId::STR_READER_TOOLS));
@@ -73,10 +71,19 @@ void ClockSettingsActivity::render(RenderLock&&) {
GUI.drawHeader(renderer, GUI.drawHeader(renderer,
Rect(contentRect.x, contentRect.y + metrics.topPadding, contentRect.width, metrics.headerHeight), Rect(contentRect.x, contentRect.y + metrics.topPadding, contentRect.width, metrics.headerHeight),
tr(STR_CLOCK_SETTINGS)); tr(STR_CLOCK_SETTINGS));
const char* batteryWarning = tr(STR_CLOCK_SETTINGS_WARNING_BATTERY);
const char* driftWarning = tr(STR_CLOCK_SETTINGS_WARNING_DRIFT);
std::string warning = driftWarning;
if (!gpio.deviceIsX3()) {
warning = std::string(batteryWarning) + "; " + driftWarning;
}
GUI.drawSubHeader(renderer, GUI.drawSubHeader(renderer,
Rect(contentRect.x, contentRect.y + metrics.topPadding + metrics.headerHeight, contentRect.width, Rect(contentRect.x, contentRect.y + metrics.topPadding + metrics.headerHeight, contentRect.width,
metrics.tabBarHeight), metrics.tabBarHeight),
tr(STR_CLOCK_SETTINGS_WARNING)); warning.c_str());
const int contentTop = const int contentTop =
contentRect.y + metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing; contentRect.y + metrics.topPadding + metrics.headerHeight + metrics.tabBarHeight + metrics.verticalSpacing;
@@ -56,6 +56,19 @@ bool mapIanaTimezone(const std::string& tz, uint8_t& outSetting) {
outSetting = TZ::TZ_PST; outSetting = TZ::TZ_PST;
return true; return true;
} }
if (tz == "America/Halifax" || tz == "America/Glace_Bay" || tz == "America/Moncton" || tz == "America/Thule") {
outSetting = TZ::TZ_AST_ADT;
return true;
}
if (tz == "America/Anchorage" || tz == "America/Juneau" || tz == "America/Nome" || tz == "America/Sitka" ||
tz == "America/Yakutat" || tz == "America/Metlakatla") {
outSetting = TZ::TZ_AKST_AKDT;
return true;
}
if (tz == "Australia/Adelaide" || tz == "Australia/Broken_Hill") {
outSetting = TZ::TZ_ACST_ACDT;
return true;
}
if (tz == "America/Sao_Paulo" || tz == "America/Argentina/Buenos_Aires" || tz == "America/Montevideo") { if (tz == "America/Sao_Paulo" || tz == "America/Argentina/Buenos_Aires" || tz == "America/Montevideo") {
outSetting = TZ::TZ_UTC_MINUS3; outSetting = TZ::TZ_UTC_MINUS3;
return true; return true;
+5 -1
View File
@@ -32,6 +32,10 @@ std::string SettingInfo::getDisplayValue() const {
value = callValueGetter(); value = callValueGetter();
else else
return {}; return {};
if (!enumLabels.empty()) {
if (value < enumLabels.size()) return enumLabels[value];
return {};
}
if (value < enumValues.size()) return std::string(I18N.get(enumValues[value])); if (value < enumValues.size()) return std::string(I18N.get(enumValues[value]));
return {}; return {};
} }
@@ -61,7 +65,7 @@ void SettingInfo::toggleValue() const {
break; break;
case SettingType::ENUM: { case SettingType::ENUM: {
const auto count = static_cast<uint8_t>(enumValues.size()); const auto count = static_cast<uint8_t>(enumLabels.empty() ? enumValues.size() : enumLabels.size());
if (count == 0) break; if (count == 0) break;
if (valuePtr) { if (valuePtr) {
SETTINGS.*(valuePtr) = (SETTINGS.*(valuePtr) + 1) % count; SETTINGS.*(valuePtr) = (SETTINGS.*(valuePtr) + 1) % count;
+7
View File
@@ -31,7 +31,14 @@ struct SettingInfo {
StrId nameId; StrId nameId;
SettingType type; SettingType type;
uint8_t CrossPointSettings::* valuePtr = nullptr; uint8_t CrossPointSettings::* valuePtr = nullptr;
// Enum values are used as StrId references for localization via I18N.get().
// If enumLabels is populated, it is authoritative for display and index bounds.
// In that case it must have the same length as enumValues, because consumers
// such as getDisplayValue(), toggleValue(), and CrossPointWebServer::handleGetSettings()
// prefer enumLabels when present. Code paths which validate posted enum values
// should also use enumLabels.size() when enumLabels is non-empty.
std::vector<StrId> enumValues; std::vector<StrId> enumValues;
std::vector<std::string> enumLabels;
SettingAction action = SettingAction::None; SettingAction action = SettingAction::None;
struct ValueRange { struct ValueRange {
+10 -3
View File
@@ -1238,8 +1238,14 @@ void CrossPointWebServer::handleGetSettings() const {
doc["value"] = static_cast<int>(s.callValueGetter()); doc["value"] = static_cast<int>(s.callValueGetter());
} }
JsonArray options = doc["options"].to<JsonArray>(); JsonArray options = doc["options"].to<JsonArray>();
for (const auto& opt : s.enumValues) { if (!s.enumLabels.empty()) {
options.add(I18N.get(opt)); for (const auto& opt : s.enumLabels) {
options.add(opt);
}
} else {
for (const auto& opt : s.enumValues) {
options.add(I18N.get(opt));
}
} }
break; break;
} }
@@ -1319,7 +1325,8 @@ void CrossPointWebServer::handlePostSettings() {
} }
case SettingType::ENUM: { case SettingType::ENUM: {
const int val = doc[s.key].as<int>(); const int val = doc[s.key].as<int>();
if (val >= 0 && val < static_cast<int>(s.enumValues.size())) { const auto count = static_cast<int>(s.enumLabels.empty() ? s.enumValues.size() : s.enumLabels.size());
if (val >= 0 && val < count) {
if (s.valuePtr) { if (s.valuePtr) {
SETTINGS.*(s.valuePtr) = static_cast<uint8_t>(val); SETTINGS.*(s.valuePtr) = static_cast<uint8_t>(val);
} else if (s.valueSetter) { } else if (s.valueSetter) {