Compare commits

...
8 Commits
Author SHA1 Message Date
Proddy dc2823c1de Merge pull request #3309 from proddy/dev
update time zone strings to IANA tz 2026e
2026-10-20 22:07:04 +02:00
Proddy 304eae34bd Merge branch 'dev' into dev 2026-10-20 22:04:53 +02:00
Proddy 960e7376fe Merge pull request #3301 from csutcliff/pr/mqtt-task-stack
fix(mqtt): 8 KB MQTT task stack when TLS verifies a CA certificate
2026-10-20 21:20:57 +02:00
proddy d91cf08b1d pkg update 2026-10-20 21:18:28 +02:00
proddy 746e5c5ef3 migrate stored TZ formats from IANA labels on boot 2026-10-20 21:14:45 +02:00
CyberNative AI LLC 955a6d8c56 fix(ntp): update time zone strings to IANA tz 2026e
The TIME_ZONES table predates several tz releases, so 32 zones give the
wrong UTC offset today or within the next year when the device applies
the string with setenv("TZ"): Mexico (no DST since 2022), Egypt (DST
again since 2023), Kazakhstan, Morocco, Paraguay, Iran, Jordan, Syria,
Greenland, and from 1 November 2026 British Columbia, Alberta, the
Northwest Territories and Manitoba (permanent UTC-7, -6, -6 and -5).

Each value is now the POSIX TZ footer of the zone's TZif file in IANA
tz 2026e (PyPI tzdata 2026.5). The keys and their order are unchanged.
"UNK" abbreviations become the numeric <+01> form, which newlib accepts.

America/Godthab and America/Scoresbysund still show summer time from
1 January to the last Sunday in March on newlib: their rule time is
negative (M3.5.0/-1) and newlib's tzset reads it as unsigned. That is
a newlib bug, not a table error.
2026-10-20 21:12:14 +02:00
proddy 19187431ce fix 2026-10-20 18:17:59 +02:00
Chris Sutcliff cdf6375c71 fix(mqtt): 8 KB MQTT task stack when TLS verifies a CA certificate
On boards with PSRAM the MQTT client runs in its own task with a 5120
byte stack. Verifying an EC certificate chain against a CA, such as
Let's Encrypt ISRG Root X2, needs about 6 KB, so the task overflowed
and the board boot looped on every connect.

The espMqttClient constructors now take the stack size, and
startClient() passes EMSESP_MQTT_STACKSIZE_CA (8192) when TLS is on
with a CA, keeping the 5120 default for insecure TLS and plain MQTT.
The client is recreated when that changes. The stack usage log
reports against the size the task was created with.
2026-10-20 12:40:59 +01:00
19 changed files with 1044 additions and 328 deletions
+1
View File
@@ -327,6 +327,7 @@ jobs:
repo_token: '${{ secrets.GITHUB_TOKEN }}'
title: Development Build v${{steps.build_info.outputs.VERSION}}
body: ${{ steps.notes.outputs.BODY }}
generate_notes: false
automatic_release_tag: 'latest'
prerelease: true
files: |
+2
View File
@@ -71,6 +71,8 @@ This release is based on the latest Espressif/Arduino core version 3. It brings
- a second boiler monitor `0xE4` carrying `0xFFFF` cleared a valid `pc0Flow`. That empty value is now ignored [#3224](https://github.com/emsesp/EMS-ESP32/issues/3224)
- writing a negative custom entity value was always 1 too high because float-to-int rounding always added +0.5. Negative values now round the same way as positive ones [#3297](https://github.com/emsesp/EMS-ESP32/issues/3297)
- after a cold boot, SoftAP fallback no longer abandons STA forever. If a home WiFi SSID is configured, EMS-ESP keeps retrying STA while the captive portal is up so a late-booting router (e.g. after a power outage) can still be joined; SoftAP stops once STA gets an IP
- NTP time zone strings updated to IANA tz 2026e. About 32 zones had the wrong UTC offset now or within the next year (Mexico no longer uses DST, Egypt does again, Morocco is permanent UTC+0, Kazakhstan, and from 1 November 2026 western Canada stays on permanent offsets). On boot EMS-ESP rewrites the stored POSIX string from the saved IANA label so existing installs pick up the new rules without re-selecting the zone [#3308](https://github.com/emsesp/EMS-ESP32/pull/3308)
- MQTT with TLS and a CA certificate crashed on every connect (a boot loop) on boards with PSRAM when the broker's certificate chain is EC, such as Let's Encrypt ISRG Root X2: verifying it needs about 6 KB of stack and the MQTT task only had 5120 bytes. The MQTT task now gets an 8 KB stack when TLS verifies a CA certificate, and keeps 5120 bytes for insecure TLS and plain MQTT
## Changed
+1
View File
@@ -33,6 +33,7 @@
"test/test_api/test_api.h",
"lib_standalone/**",
"pio_local.ini_example",
"tz_zones.h",
"**/i18n/**",
"**/*.js",
"**/*.csv",
+2 -2
View File
@@ -37,7 +37,7 @@
"react": "^19.3.1",
"react-dom": "^19.3.1",
"react-icons": "^5.7.0",
"react-router": "^8.4.0",
"react-router": "^8.5.0",
"typesafe-i18n": "^5.27.1"
},
"devDependencies": {
@@ -56,7 +56,7 @@
"rollup-plugin-visualizer": "^7.1.1",
"terser": "^5.51.2",
"typescript": "^6.0.3",
"typescript-eslint": "^8.71.2",
"typescript-eslint": "^8.72.0",
"vite": "^8.3.4",
"vite-plugin-imagemin": "^0.6.1"
},
+88 -80
View File
@@ -203,8 +203,8 @@ importers:
specifier: ^5.7.0
version: 5.7.0(react@19.3.1)
react-router:
specifier: ^8.4.0
version: 8.4.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
specifier: ^8.5.0
version: 8.5.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
typesafe-i18n:
specifier: ^5.27.1
version: 5.27.1(typescript@6.0.3)
@@ -255,8 +255,8 @@ importers:
specifier: ^6.0.3
version: 6.0.3
typescript-eslint:
specifier: ^8.71.2
version: 8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
specifier: ^8.72.0
version: 8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
vite:
specifier: ^8.3.4
version: 8.3.4(@types/node@26.6.5)(terser@5.51.2)
@@ -873,63 +873,63 @@ packages:
'@types/svgo@2.6.4':
resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
'@typescript-eslint/eslint-plugin@8.71.2':
resolution: {integrity: sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==}
'@typescript-eslint/eslint-plugin@8.72.0':
resolution: {integrity: sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.71.2
'@typescript-eslint/parser': ^8.72.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/parser@8.71.2':
resolution: {integrity: sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==}
'@typescript-eslint/parser@8.72.0':
resolution: {integrity: sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/project-service@8.71.2':
resolution: {integrity: sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==}
'@typescript-eslint/project-service@8.72.0':
resolution: {integrity: sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/scope-manager@8.71.2':
resolution: {integrity: sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==}
'@typescript-eslint/scope-manager@8.72.0':
resolution: {integrity: sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.71.2':
resolution: {integrity: sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==}
'@typescript-eslint/tsconfig-utils@8.72.0':
resolution: {integrity: sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/type-utils@8.71.2':
resolution: {integrity: sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==}
'@typescript-eslint/type-utils@8.72.0':
resolution: {integrity: sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/types@8.71.2':
resolution: {integrity: sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==}
'@typescript-eslint/types@8.72.0':
resolution: {integrity: sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.71.2':
resolution: {integrity: sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==}
'@typescript-eslint/typescript-estree@8.72.0':
resolution: {integrity: sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/utils@8.71.2':
resolution: {integrity: sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==}
'@typescript-eslint/utils@8.72.0':
resolution: {integrity: sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.1.0'
'@typescript-eslint/visitor-keys@8.71.2':
resolution: {integrity: sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==}
'@typescript-eslint/visitor-keys@8.72.0':
resolution: {integrity: sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
acorn-jsx@5.3.2:
@@ -1052,8 +1052,8 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browserslist@4.29.3:
resolution: {integrity: sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==}
browserslist@4.29.4:
resolution: {integrity: sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
@@ -1872,8 +1872,8 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
ignore@7.0.12:
resolution: {integrity: sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==}
ignore@7.0.13:
resolution: {integrity: sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==}
engines: {node: '>= 4'}
imagemin-gifsicle@7.0.0:
@@ -2608,13 +2608,19 @@ packages:
react-is@19.3.1:
resolution: {integrity: sha512-RRieF6IyxQBkcK6pP8xOHprLTPW6QjRkbGdImtqNme+/hYVwCZkLQLdlZGOhUqXwkS9IEynaGvPJtv6XB/cz0Q==}
react-router@8.4.0:
resolution: {integrity: sha512-JtydAkBvU9UTEe5qNC+POhu+ZBNM7rlKY2IegwXc7Idj7xfRG16x5RZrw3mju+XlqBxfvb2ky9P3jUp9WyWC1g==}
react-router@8.5.0:
resolution: {integrity: sha512-9nEd/deifcTJIIcUaNIyQGkyXD0D6QVB9XD2O5GgHpP3xu3U5RXm87YQ8++Wn8JQWgMBM/5+PjWPM/7n7O7WHA==}
engines: {node: '>=22.22.0'}
peerDependencies:
'@types/react': '>=19.2.0'
'@types/react-dom': '>=19.2.0'
react: '>=19.2.7'
react-dom: '>=19.2.7'
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
react-dom:
optional: true
@@ -2996,8 +3002,8 @@ packages:
peerDependencies:
typescript: '>=3.5.1'
typescript-eslint@8.71.2:
resolution: {integrity: sha512-Rz4effbMy2g9wnvOJZG6SbVavX313Lap1vNEbk1RUochzickvzP8P4HB+bkNg5e50eVz1hQGjh9K1omI1xB4Bw==}
typescript-eslint@8.72.0:
resolution: {integrity: sha512-r26Fkc8As+mZvqp7SLsMmNFyYUup/6CjK7/eHBHmhOw6GH6n6YFxHVFrG0jN1c5CMpDzelLPxvgeCOzx7BjN+A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -3224,7 +3230,7 @@ snapshots:
dependencies:
'@babel/compat-data': 7.29.7
'@babel/helper-validator-option': 7.29.7
browserslist: 4.29.3
browserslist: 4.29.4
lru-cache: 5.1.1
semver: 6.3.1
@@ -3796,57 +3802,57 @@ snapshots:
dependencies:
'@types/node': 26.6.5
'@typescript-eslint/eslint-plugin@8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
'@typescript-eslint/eslint-plugin@8.72.0(@typescript-eslint/parser@8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.71.2
'@typescript-eslint/type-utils': 8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/utils': 8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.71.2
'@typescript-eslint/parser': 8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.72.0
'@typescript-eslint/type-utils': 8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/utils': 8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.72.0
eslint: 10.13.0(supports-color@10.2.2)
ignore: 7.0.12
ignore: 7.0.13
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@6.0.3)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
'@typescript-eslint/parser@8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.71.2
'@typescript-eslint/types': 8.71.2
'@typescript-eslint/typescript-estree': 8.71.2(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.71.2
'@typescript-eslint/scope-manager': 8.72.0
'@typescript-eslint/types': 8.72.0
'@typescript-eslint/typescript-estree': 8.72.0(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/visitor-keys': 8.72.0
debug: 4.4.3(supports-color@10.2.2)
eslint: 10.13.0(supports-color@10.2.2)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.71.2(supports-color@10.2.2)(typescript@6.0.3)':
'@typescript-eslint/project-service@8.72.0(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.71.2(typescript@6.0.3)
'@typescript-eslint/types': 8.71.2
'@typescript-eslint/tsconfig-utils': 8.72.0(typescript@6.0.3)
'@typescript-eslint/types': 8.72.0
debug: 4.4.3(supports-color@10.2.2)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/scope-manager@8.71.2':
'@typescript-eslint/scope-manager@8.72.0':
dependencies:
'@typescript-eslint/types': 8.71.2
'@typescript-eslint/visitor-keys': 8.71.2
'@typescript-eslint/types': 8.72.0
'@typescript-eslint/visitor-keys': 8.72.0
'@typescript-eslint/tsconfig-utils@8.71.2(typescript@6.0.3)':
'@typescript-eslint/tsconfig-utils@8.72.0(typescript@6.0.3)':
dependencies:
typescript: 6.0.3
'@typescript-eslint/type-utils@8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
'@typescript-eslint/type-utils@8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@typescript-eslint/types': 8.71.2
'@typescript-eslint/typescript-estree': 8.71.2(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/utils': 8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/types': 8.72.0
'@typescript-eslint/typescript-estree': 8.72.0(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/utils': 8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
debug: 4.4.3(supports-color@10.2.2)
eslint: 10.13.0(supports-color@10.2.2)
ts-api-utils: 2.5.0(typescript@6.0.3)
@@ -3854,14 +3860,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/types@8.71.2': {}
'@typescript-eslint/types@8.72.0': {}
'@typescript-eslint/typescript-estree@8.71.2(supports-color@10.2.2)(typescript@6.0.3)':
'@typescript-eslint/typescript-estree@8.72.0(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@typescript-eslint/project-service': 8.71.2(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/tsconfig-utils': 8.71.2(typescript@6.0.3)
'@typescript-eslint/types': 8.71.2
'@typescript-eslint/visitor-keys': 8.71.2
'@typescript-eslint/project-service': 8.72.0(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/tsconfig-utils': 8.72.0(typescript@6.0.3)
'@typescript-eslint/types': 8.72.0
'@typescript-eslint/visitor-keys': 8.72.0
debug: 4.4.3(supports-color@10.2.2)
minimatch: 10.2.6
semver: 7.8.5
@@ -3871,20 +3877,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
'@typescript-eslint/utils@8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
dependencies:
'@eslint-community/eslint-utils': 4.10.1(eslint@10.13.0(supports-color@10.2.2))
'@typescript-eslint/scope-manager': 8.71.2
'@typescript-eslint/types': 8.71.2
'@typescript-eslint/typescript-estree': 8.71.2(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.72.0
'@typescript-eslint/types': 8.72.0
'@typescript-eslint/typescript-estree': 8.72.0(supports-color@10.2.2)(typescript@6.0.3)
eslint: 10.13.0(supports-color@10.2.2)
typescript: 6.0.3
transitivePeerDependencies:
- supports-color
'@typescript-eslint/visitor-keys@8.71.2':
'@typescript-eslint/visitor-keys@8.72.0':
dependencies:
'@typescript-eslint/types': 8.71.2
'@typescript-eslint/types': 8.72.0
eslint-visitor-keys: 5.0.1
acorn-jsx@5.3.2(acorn@8.19.0):
@@ -4006,13 +4012,13 @@ snapshots:
dependencies:
fill-range: 7.1.1
browserslist@4.29.3:
browserslist@4.29.4:
dependencies:
baseline-browser-mapping: 2.11.29
caniuse-lite: 1.0.30001816
electron-to-chromium: 1.5.459
node-releases: 2.0.59
update-browserslist-db: 1.3.4(browserslist@4.29.3)
update-browserslist-db: 1.3.4(browserslist@4.29.4)
buffer-alloc-unsafe@1.1.0: {}
@@ -4918,7 +4924,7 @@ snapshots:
ignore@5.3.2: {}
ignore@7.0.12: {}
ignore@7.0.13: {}
imagemin-gifsicle@7.0.0:
dependencies:
@@ -5562,12 +5568,14 @@ snapshots:
react-is@19.3.1: {}
react-router@8.4.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1):
react-router@8.5.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1):
dependencies:
'@remix-run/route-pattern': 0.22.1
cookie-es: 3.1.1
react: 19.3.1
optionalDependencies:
'@types/react': 19.3.0
'@types/react-dom': 19.3.0(@types/react@19.3.0)
react-dom: 19.3.1(react@19.3.1)
react-transition-group@4.4.5(react-dom@19.3.1(react@19.3.1))(react@19.3.1):
@@ -5936,12 +5944,12 @@ snapshots:
dependencies:
typescript: 6.0.3
typescript-eslint@8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3):
typescript-eslint@8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/parser': 8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/typescript-estree': 8.71.2(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/utils': 8.71.2(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/eslint-plugin': 8.72.0(@typescript-eslint/parser@8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/parser': 8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/typescript-estree': 8.72.0(supports-color@10.2.2)(typescript@6.0.3)
'@typescript-eslint/utils': 8.72.0(eslint@10.13.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
eslint: 10.13.0(supports-color@10.2.2)
typescript: 6.0.3
transitivePeerDependencies:
@@ -5958,9 +5966,9 @@ snapshots:
universalify@2.0.1: {}
update-browserslist-db@1.3.4(browserslist@4.29.3):
update-browserslist-db@1.3.4(browserslist@4.29.4):
dependencies:
browserslist: 4.29.3
browserslist: 4.29.4
escalade: 3.2.0
picocolors: 1.1.1
+28 -1
View File
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import CancelIcon from '@mui/icons-material/Cancel';
@@ -42,6 +42,7 @@ import type { ValidateFieldsError } from 'validators/schema';
import {
TIME_ZONES,
migrateNtpTimeZones,
selectedTimeZone,
timeZoneSelectItemsT,
useTimeZoneSelectItems
@@ -56,6 +57,7 @@ const NTPSettings = () => {
origData,
dirtyFlags,
setDirtyFlags,
setOrigData,
blocker,
saveData,
errorMessage
@@ -81,6 +83,31 @@ const NTPSettings = () => {
const [settingTime, setSettingTime] = useState<boolean>(false);
const [processing, setProcessing] = useState<boolean>(false);
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
const migratingTz = useRef(false);
// If firmware has not yet rewritten tz_format from the label (or the user
// landed here before reboot), persist the current TIME_ZONES values quietly.
useEffect(() => {
if (!data || migratingTz.current || dirtyFlags.length > 0) {
return;
}
const migrated = migrateNtpTimeZones(data);
if (!migrated) {
return;
}
migratingTz.current = true;
void (async () => {
try {
updateDataValue(migrated);
await NTPApi.updateNTPSettings(migrated);
setOrigData(migrated);
} catch {
// Boot-time firmware migration still applies on the next restart.
} finally {
migratingTz.current = false;
}
})();
}, [data, dirtyFlags.length, setOrigData, updateDataValue]);
const { send: updateTime } = useRequest(
(local_time: Time) => NTPApi.updateTime(local_time),
+247 -216
View File
@@ -13,20 +13,20 @@ export const TIME_ZONES: Record<string, string> = {
'Africa/Blantyre': 'CAT-2',
'Africa/Brazzaville': 'WAT-1',
'Africa/Bujumbura': 'CAT-2',
'Africa/Cairo': 'EET-2',
'Africa/Casablanca': 'UNK-1',
'Africa/Cairo': 'EET-2EEST,M4.5.5/0,M10.5.4/24',
'Africa/Casablanca': '<+00>0',
'Africa/Ceuta': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Africa/Conakry': 'GMT0',
'Africa/Dakar': 'GMT0',
'Africa/Dar_es_Salaam': 'EAT-3',
'Africa/Djibouti': 'EAT-3',
'Africa/Douala': 'WAT-1',
'Africa/El_Aaiun': 'UNK-1',
'Africa/El_Aaiun': '<+00>0',
'Africa/Freetown': 'GMT0',
'Africa/Gaborone': 'CAT-2',
'Africa/Harare': 'CAT-2',
'Africa/Johannesburg': 'SAST-2',
'Africa/Juba': 'EAT-3',
'Africa/Juba': 'CAT-2',
'Africa/Kampala': 'EAT-3',
'Africa/Khartoum': 'CAT-2',
'Africa/Kigali': 'CAT-2',
@@ -57,42 +57,42 @@ export const TIME_ZONES: Record<string, string> = {
'America/Anchorage': 'AKST9AKDT,M3.2.0,M11.1.0',
'America/Anguilla': 'AST4',
'America/Antigua': 'AST4',
'America/Araguaina': 'UNK3',
'America/Argentina/Buenos_Aires': 'UNK3',
'America/Argentina/Catamarca': 'UNK3',
'America/Argentina/Cordoba': 'UNK3',
'America/Argentina/Jujuy': 'UNK3',
'America/Argentina/La_Rioja': 'UNK3',
'America/Argentina/Mendoza': 'UNK3',
'America/Argentina/Rio_Gallegos': 'UNK3',
'America/Argentina/Salta': 'UNK3',
'America/Argentina/San_Juan': 'UNK3',
'America/Argentina/San_Luis': 'UNK3',
'America/Argentina/Tucuman': 'UNK3',
'America/Argentina/Ushuaia': 'UNK3',
'America/Araguaina': '<-03>3',
'America/Argentina/Buenos_Aires': '<-03>3',
'America/Argentina/Catamarca': '<-03>3',
'America/Argentina/Cordoba': '<-03>3',
'America/Argentina/Jujuy': '<-03>3',
'America/Argentina/La_Rioja': '<-03>3',
'America/Argentina/Mendoza': '<-03>3',
'America/Argentina/Rio_Gallegos': '<-03>3',
'America/Argentina/Salta': '<-03>3',
'America/Argentina/San_Juan': '<-03>3',
'America/Argentina/San_Luis': '<-03>3',
'America/Argentina/Tucuman': '<-03>3',
'America/Argentina/Ushuaia': '<-03>3',
'America/Aruba': 'AST4',
'America/Asuncion': 'UNK4UNK,M10.1.0/0,M3.4.0/0',
'America/Asuncion': '<-03>3',
'America/Atikokan': 'EST5',
'America/Bahia': 'UNK3',
'America/Bahia_Banderas': 'CST6CDT,M4.1.0,M10.5.0',
'America/Bahia': '<-03>3',
'America/Bahia_Banderas': 'CST6',
'America/Barbados': 'AST4',
'America/Belem': 'UNK3',
'America/Belem': '<-03>3',
'America/Belize': 'CST6',
'America/Blanc-Sablon': 'AST4',
'America/Boa_Vista': 'UNK4',
'America/Bogota': 'UNK5',
'America/Boa_Vista': '<-04>4',
'America/Bogota': '<-05>5',
'America/Boise': 'MST7MDT,M3.2.0,M11.1.0',
'America/Cambridge_Bay': 'MST7MDT,M3.2.0,M11.1.0',
'America/Campo_Grande': 'UNK4',
'America/Campo_Grande': '<-04>4',
'America/Cancun': 'EST5',
'America/Caracas': 'UNK4',
'America/Cayenne': 'UNK3',
'America/Caracas': '<-04>4',
'America/Cayenne': '<-03>3',
'America/Cayman': 'EST5',
'America/Chicago': 'CST6CDT,M3.2.0,M11.1.0',
'America/Chihuahua': 'MST7MDT,M4.1.0,M10.5.0',
'America/Chihuahua': 'CST6',
'America/Costa_Rica': 'CST6',
'America/Creston': 'MST7',
'America/Cuiaba': 'UNK4',
'America/Cuiaba': '<-04>4',
'America/Curacao': 'AST4',
'America/Danmarkshavn': 'GMT0',
'America/Dawson': 'MST7',
@@ -100,20 +100,20 @@ export const TIME_ZONES: Record<string, string> = {
'America/Denver': 'MST7MDT,M3.2.0,M11.1.0',
'America/Detroit': 'EST5EDT,M3.2.0,M11.1.0',
'America/Dominica': 'AST4',
'America/Edmonton': 'MST7MDT,M3.2.0,M11.1.0',
'America/Eirunepe': 'UNK5',
'America/Edmonton': 'CST6',
'America/Eirunepe': '<-05>5',
'America/El_Salvador': 'CST6',
'America/Fort_Nelson': 'MST7',
'America/Fortaleza': 'UNK3',
'America/Fortaleza': '<-03>3',
'America/Glace_Bay': 'AST4ADT,M3.2.0,M11.1.0',
'America/Godthab': 'UNK3UNK,M3.5.0/-2,M10.5.0/-1',
'America/Godthab': '<-02>2<-01>,M3.5.0/-1,M10.5.0/0',
'America/Goose_Bay': 'AST4ADT,M3.2.0,M11.1.0',
'America/Grand_Turk': 'EST5EDT,M3.2.0,M11.1.0',
'America/Grenada': 'AST4',
'America/Guadeloupe': 'AST4',
'America/Guatemala': 'CST6',
'America/Guayaquil': 'UNK5',
'America/Guyana': 'UNK4',
'America/Guayaquil': '<-05>5',
'America/Guyana': '<-04>4',
'America/Halifax': 'AST4ADT,M3.2.0,M11.1.0',
'America/Havana': 'CST5CDT,M3.2.0/0,M11.1.0/1',
'America/Hermosillo': 'MST7',
@@ -125,63 +125,63 @@ export const TIME_ZONES: Record<string, string> = {
'America/Indiana/Vevay': 'EST5EDT,M3.2.0,M11.1.0',
'America/Indiana/Vincennes': 'EST5EDT,M3.2.0,M11.1.0',
'America/Indiana/Winamac': 'EST5EDT,M3.2.0,M11.1.0',
'America/Inuvik': 'MST7MDT,M3.2.0,M11.1.0',
'America/Inuvik': 'CST6',
'America/Iqaluit': 'EST5EDT,M3.2.0,M11.1.0',
'America/Jamaica': 'EST5',
'America/Juneau': 'AKST9AKDT,M3.2.0,M11.1.0',
'America/Kentucky/Louisville': 'EST5EDT,M3.2.0,M11.1.0',
'America/Kentucky/Monticello': 'EST5EDT,M3.2.0,M11.1.0',
'America/Kralendijk': 'AST4',
'America/La_Paz': 'UNK4',
'America/Lima': 'UNK5',
'America/La_Paz': '<-04>4',
'America/Lima': '<-05>5',
'America/Los_Angeles': 'PST8PDT,M3.2.0,M11.1.0',
'America/Lower_Princes': 'AST4',
'America/Maceio': 'UNK3',
'America/Maceio': '<-03>3',
'America/Managua': 'CST6',
'America/Manaus': 'UNK4',
'America/Manaus': '<-04>4',
'America/Marigot': 'AST4',
'America/Martinique': 'AST4',
'America/Matamoros': 'CST6CDT,M3.2.0,M11.1.0',
'America/Mazatlan': 'MST7MDT,M4.1.0,M10.5.0',
'America/Mazatlan': 'MST7',
'America/Menominee': 'CST6CDT,M3.2.0,M11.1.0',
'America/Merida': 'CST6CDT,M4.1.0,M10.5.0',
'America/Merida': 'CST6',
'America/Metlakatla': 'AKST9AKDT,M3.2.0,M11.1.0',
'America/Mexico_City': 'CST6CDT,M4.1.0,M10.5.0',
'America/Miquelon': 'UNK3UNK,M3.2.0,M11.1.0',
'America/Mexico_City': 'CST6',
'America/Miquelon': '<-03>3<-02>,M3.2.0,M11.1.0',
'America/Moncton': 'AST4ADT,M3.2.0,M11.1.0',
'America/Monterrey': 'CST6CDT,M4.1.0,M10.5.0',
'America/Montevideo': 'UNK3',
'America/Monterrey': 'CST6',
'America/Montevideo': '<-03>3',
'America/Montreal': 'EST5EDT,M3.2.0,M11.1.0',
'America/Montserrat': 'AST4',
'America/Nassau': 'EST5EDT,M3.2.0,M11.1.0',
'America/New_York': 'EST5EDT,M3.2.0,M11.1.0',
'America/Nipigon': 'EST5EDT,M3.2.0,M11.1.0',
'America/Nome': 'AKST9AKDT,M3.2.0,M11.1.0',
'America/Noronha': 'UNK2',
'America/Noronha': '<-02>2',
'America/North_Dakota/Beulah': 'CST6CDT,M3.2.0,M11.1.0',
'America/North_Dakota/Center': 'CST6CDT,M3.2.0,M11.1.0',
'America/North_Dakota/New_Salem': 'CST6CDT,M3.2.0,M11.1.0',
'America/Ojinaga': 'MST7MDT,M3.2.0,M11.1.0',
'America/Ojinaga': 'CST6CDT,M3.2.0,M11.1.0',
'America/Panama': 'EST5',
'America/Pangnirtung': 'EST5EDT,M3.2.0,M11.1.0',
'America/Paramaribo': 'UNK3',
'America/Paramaribo': '<-03>3',
'America/Phoenix': 'MST7',
'America/Port-au-Prince': 'EST5EDT,M3.2.0,M11.1.0',
'America/Port_of_Spain': 'AST4',
'America/Porto_Velho': 'UNK4',
'America/Porto_Velho': '<-04>4',
'America/Puerto_Rico': 'AST4',
'America/Punta_Arenas': 'UNK3',
'America/Rainy_River': 'CST6CDT,M3.2.0,M11.1.0',
'America/Punta_Arenas': '<-03>3',
'America/Rainy_River': 'EST5',
'America/Rankin_Inlet': 'CST6CDT,M3.2.0,M11.1.0',
'America/Recife': 'UNK3',
'America/Recife': '<-03>3',
'America/Regina': 'CST6',
'America/Resolute': 'CST6CDT,M3.2.0,M11.1.0',
'America/Rio_Branco': 'UNK5',
'America/Santarem': 'UNK3',
'America/Santiago': 'UNK4UNK,M9.1.6/24,M4.1.6/24',
'America/Rio_Branco': '<-05>5',
'America/Santarem': '<-03>3',
'America/Santiago': '<-04>4<-03>,M9.1.6/24,M4.1.6/24',
'America/Santo_Domingo': 'AST4',
'America/Sao_Paulo': 'UNK3',
'America/Scoresbysund': 'UNK1UNK,M3.5.0/0,M10.5.0/1',
'America/Sao_Paulo': '<-03>3',
'America/Scoresbysund': '<-02>2<-01>,M3.5.0/-1,M10.5.0/0',
'America/Sitka': 'AKST9AKDT,M3.2.0,M11.1.0',
'America/St_Barthelemy': 'AST4',
'America/St_Johns': 'NST3:30NDT,M3.2.0,M11.1.0',
@@ -196,156 +196,156 @@ export const TIME_ZONES: Record<string, string> = {
'America/Tijuana': 'PST8PDT,M3.2.0,M11.1.0',
'America/Toronto': 'EST5EDT,M3.2.0,M11.1.0',
'America/Tortola': 'AST4',
'America/Vancouver': 'PST8PDT,M3.2.0,M11.1.0',
'America/Vancouver': 'MST7',
'America/Whitehorse': 'MST7',
'America/Winnipeg': 'CST6CDT,M3.2.0,M11.1.0',
'America/Winnipeg': 'EST5',
'America/Yakutat': 'AKST9AKDT,M3.2.0,M11.1.0',
'America/Yellowknife': 'MST7MDT,M3.2.0,M11.1.0',
'Antarctica/Casey': 'UNK-8',
'Antarctica/Davis': 'UNK-7',
'Antarctica/DumontDUrville': 'UNK-10',
'Antarctica/Macquarie': 'UNK-11',
'Antarctica/Mawson': 'UNK-5',
'America/Yellowknife': 'CST6',
'Antarctica/Casey': '<+08>-8',
'Antarctica/Davis': '<+07>-7',
'Antarctica/DumontDUrville': '<+10>-10',
'Antarctica/Macquarie': 'AEST-10AEDT,M10.1.0,M4.1.0/3',
'Antarctica/Mawson': '<+05>-5',
'Antarctica/McMurdo': 'NZST-12NZDT,M9.5.0,M4.1.0/3',
'Antarctica/Palmer': 'UNK3',
'Antarctica/Rothera': 'UNK3',
'Antarctica/Syowa': 'UNK-3',
'Antarctica/Troll': 'UNK0UNK-2,M3.5.0/1,M10.5.0/3',
'Antarctica/Vostok': 'UNK-6',
'Antarctica/Palmer': '<-03>3',
'Antarctica/Rothera': '<-03>3',
'Antarctica/Syowa': '<+03>-3',
'Antarctica/Troll': '<+00>0<+02>-2,M3.5.0/1,M10.5.0/3',
'Antarctica/Vostok': '<+05>-5',
'Arctic/Longyearbyen': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Asia/Aden': 'UNK-3',
'Asia/Almaty': 'UNK-6',
'Asia/Amman': 'EET-2EEST,M3.5.4/24,M10.5.5/1',
'Asia/Anadyr': 'UNK-12',
'Asia/Aqtau': 'UNK-5',
'Asia/Aqtobe': 'UNK-5',
'Asia/Ashgabat': 'UNK-5',
'Asia/Atyrau': 'UNK-5',
'Asia/Baghdad': 'UNK-3',
'Asia/Bahrain': 'UNK-3',
'Asia/Baku': 'UNK-4',
'Asia/Bangkok': 'UNK-7',
'Asia/Barnaul': 'UNK-7',
'Asia/Aden': '<+03>-3',
'Asia/Almaty': '<+05>-5',
'Asia/Amman': '<+03>-3',
'Asia/Anadyr': '<+12>-12',
'Asia/Aqtau': '<+05>-5',
'Asia/Aqtobe': '<+05>-5',
'Asia/Ashgabat': '<+05>-5',
'Asia/Atyrau': '<+05>-5',
'Asia/Baghdad': '<+03>-3',
'Asia/Bahrain': '<+03>-3',
'Asia/Baku': '<+04>-4',
'Asia/Bangkok': '<+07>-7',
'Asia/Barnaul': '<+07>-7',
'Asia/Beirut': 'EET-2EEST,M3.5.0/0,M10.5.0/0',
'Asia/Bishkek': 'UNK-6',
'Asia/Brunei': 'UNK-8',
'Asia/Chita': 'UNK-9',
'Asia/Choibalsan': 'UNK-8',
'Asia/Colombo': 'UNK-5:30',
'Asia/Damascus': 'EET-2EEST,M3.5.5/0,M10.5.5/0',
'Asia/Dhaka': 'UNK-6',
'Asia/Dili': 'UNK-9',
'Asia/Dubai': 'UNK-4',
'Asia/Dushanbe': 'UNK-5',
'Asia/Bishkek': '<+06>-6',
'Asia/Brunei': '<+08>-8',
'Asia/Chita': '<+09>-9',
'Asia/Choibalsan': '<+08>-8',
'Asia/Colombo': '<+0530>-5:30',
'Asia/Damascus': '<+03>-3',
'Asia/Dhaka': '<+06>-6',
'Asia/Dili': '<+09>-9',
'Asia/Dubai': '<+04>-4',
'Asia/Dushanbe': '<+05>-5',
'Asia/Famagusta': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Asia/Gaza': 'EET-2EEST,M3.5.5/0,M10.5.6/1',
'Asia/Hebron': 'EET-2EEST,M3.5.5/0,M10.5.6/1',
'Asia/Ho_Chi_Minh': 'UNK-7',
'Asia/Gaza': 'EET-2EEST,M3.4.4/50,M10.4.4/50',
'Asia/Hebron': 'EET-2EEST,M3.4.4/50,M10.4.4/50',
'Asia/Ho_Chi_Minh': '<+07>-7',
'Asia/Hong_Kong': 'HKT-8',
'Asia/Hovd': 'UNK-7',
'Asia/Irkutsk': 'UNK-8',
'Asia/Hovd': '<+07>-7',
'Asia/Irkutsk': '<+08>-8',
'Asia/Jakarta': 'WIB-7',
'Asia/Jayapura': 'WIT-9',
'Asia/Jerusalem': 'IST-2IDT,M3.4.4/26,M10.5.0',
'Asia/Kabul': 'UNK-4:30',
'Asia/Kamchatka': 'UNK-12',
'Asia/Kabul': '<+0430>-4:30',
'Asia/Kamchatka': '<+12>-12',
'Asia/Karachi': 'PKT-5',
'Asia/Kathmandu': 'UNK-5:45',
'Asia/Khandyga': 'UNK-9',
'Asia/Kathmandu': '<+0545>-5:45',
'Asia/Khandyga': '<+09>-9',
'Asia/Kolkata': 'IST-5:30',
'Asia/Krasnoyarsk': 'UNK-7',
'Asia/Kuala_Lumpur': 'UNK-8',
'Asia/Kuching': 'UNK-8',
'Asia/Kuwait': 'UNK-3',
'Asia/Krasnoyarsk': '<+07>-7',
'Asia/Kuala_Lumpur': '<+08>-8',
'Asia/Kuching': '<+08>-8',
'Asia/Kuwait': '<+03>-3',
'Asia/Macau': 'CST-8',
'Asia/Magadan': 'UNK-11',
'Asia/Magadan': '<+11>-11',
'Asia/Makassar': 'WITA-8',
'Asia/Manila': 'PST-8',
'Asia/Muscat': 'UNK-4',
'Asia/Muscat': '<+04>-4',
'Asia/Nicosia': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Asia/Novokuznetsk': 'UNK-7',
'Asia/Novosibirsk': 'UNK-7',
'Asia/Omsk': 'UNK-6',
'Asia/Oral': 'UNK-5',
'Asia/Phnom_Penh': 'UNK-7',
'Asia/Novokuznetsk': '<+07>-7',
'Asia/Novosibirsk': '<+07>-7',
'Asia/Omsk': '<+06>-6',
'Asia/Oral': '<+05>-5',
'Asia/Phnom_Penh': '<+07>-7',
'Asia/Pontianak': 'WIB-7',
'Asia/Pyongyang': 'KST-9',
'Asia/Qatar': 'UNK-3',
'Asia/Qyzylorda': 'UNK-5',
'Asia/Riyadh': 'UNK-3',
'Asia/Sakhalin': 'UNK-11',
'Asia/Samarkand': 'UNK-5',
'Asia/Qatar': '<+03>-3',
'Asia/Qyzylorda': '<+05>-5',
'Asia/Riyadh': '<+03>-3',
'Asia/Sakhalin': '<+11>-11',
'Asia/Samarkand': '<+05>-5',
'Asia/Seoul': 'KST-9',
'Asia/Shanghai': 'CST-8',
'Asia/Singapore': 'UNK-8',
'Asia/Srednekolymsk': 'UNK-11',
'Asia/Singapore': '<+08>-8',
'Asia/Srednekolymsk': '<+11>-11',
'Asia/Taipei': 'CST-8',
'Asia/Tashkent': 'UNK-5',
'Asia/Tbilisi': 'UNK-4',
'Asia/Tehran': 'UNK-3:30UNK,J79/24,J263/24',
'Asia/Thimphu': 'UNK-6',
'Asia/Tashkent': '<+05>-5',
'Asia/Tbilisi': '<+04>-4',
'Asia/Tehran': '<+0330>-3:30',
'Asia/Thimphu': '<+06>-6',
'Asia/Tokyo': 'JST-9',
'Asia/Tomsk': 'UNK-7',
'Asia/Ulaanbaatar': 'UNK-8',
'Asia/Urumqi': 'UNK-6',
'Asia/Ust-Nera': 'UNK-10',
'Asia/Vientiane': 'UNK-7',
'Asia/Vladivostok': 'UNK-10',
'Asia/Yakutsk': 'UNK-9',
'Asia/Yangon': 'UNK-6:30',
'Asia/Yekaterinburg': 'UNK-5',
'Asia/Yerevan': 'UNK-4',
'Atlantic/Azores': 'UNK1UNK,M3.5.0/0,M10.5.0/1',
'Asia/Tomsk': '<+07>-7',
'Asia/Ulaanbaatar': '<+08>-8',
'Asia/Urumqi': '<+06>-6',
'Asia/Ust-Nera': '<+10>-10',
'Asia/Vientiane': '<+07>-7',
'Asia/Vladivostok': '<+10>-10',
'Asia/Yakutsk': '<+09>-9',
'Asia/Yangon': '<+0630>-6:30',
'Asia/Yekaterinburg': '<+05>-5',
'Asia/Yerevan': '<+04>-4',
'Atlantic/Azores': '<-01>1<+00>,M3.5.0/0,M10.5.0/1',
'Atlantic/Bermuda': 'AST4ADT,M3.2.0,M11.1.0',
'Atlantic/Canary': 'WET0WEST,M3.5.0/1,M10.5.0',
'Atlantic/Cape_Verde': 'UNK1',
'Atlantic/Cape_Verde': '<-01>1',
'Atlantic/Faroe': 'WET0WEST,M3.5.0/1,M10.5.0',
'Atlantic/Madeira': 'WET0WEST,M3.5.0/1,M10.5.0',
'Atlantic/Reykjavik': 'GMT0',
'Atlantic/South_Georgia': 'UNK2',
'Atlantic/South_Georgia': '<-02>2',
'Atlantic/St_Helena': 'GMT0',
'Atlantic/Stanley': 'UNK3',
'Atlantic/Stanley': '<-03>3',
'Australia/Adelaide': 'ACST-9:30ACDT,M10.1.0,M4.1.0/3',
'Australia/Brisbane': 'AEST-10',
'Australia/Broken_Hill': 'ACST-9:30ACDT,M10.1.0,M4.1.0/3',
'Australia/Currie': 'AEST-10AEDT,M10.1.0,M4.1.0/3',
'Australia/Darwin': 'ACST-9:30',
'Australia/Eucla': 'UNK-8:45',
'Australia/Eucla': '<+0845>-8:45',
'Australia/Hobart': 'AEST-10AEDT,M10.1.0,M4.1.0/3',
'Australia/Lindeman': 'AEST-10',
'Australia/Lord_Howe': 'UNK-10:30UNK-11,M10.1.0,M4.1.0',
'Australia/Lord_Howe': '<+1030>-10:30<+11>-11,M10.1.0,M4.1.0',
'Australia/Melbourne': 'AEST-10AEDT,M10.1.0,M4.1.0/3',
'Australia/Perth': 'AWST-8',
'Australia/Sydney': 'AEST-10AEDT,M10.1.0,M4.1.0/3',
'Etc/GMT': 'GMT0',
'Etc/GMT+0': 'GMT0',
'Etc/GMT+1': 'UNK1',
'Etc/GMT+10': 'UNK10',
'Etc/GMT+11': 'UNK11',
'Etc/GMT+12': 'UNK12',
'Etc/GMT+2': 'UNK2',
'Etc/GMT+3': 'UNK3',
'Etc/GMT+4': 'UNK4',
'Etc/GMT+5': 'UNK5',
'Etc/GMT+6': 'UNK6',
'Etc/GMT+7': 'UNK7',
'Etc/GMT+8': 'UNK8',
'Etc/GMT+9': 'UNK9',
'Etc/GMT+1': '<-01>1',
'Etc/GMT+10': '<-10>10',
'Etc/GMT+11': '<-11>11',
'Etc/GMT+12': '<-12>12',
'Etc/GMT+2': '<-02>2',
'Etc/GMT+3': '<-03>3',
'Etc/GMT+4': '<-04>4',
'Etc/GMT+5': '<-05>5',
'Etc/GMT+6': '<-06>6',
'Etc/GMT+7': '<-07>7',
'Etc/GMT+8': '<-08>8',
'Etc/GMT+9': '<-09>9',
'Etc/GMT-0': 'GMT0',
'Etc/GMT-1': 'UNK-1',
'Etc/GMT-10': 'UNK-10',
'Etc/GMT-11': 'UNK-11',
'Etc/GMT-12': 'UNK-12',
'Etc/GMT-13': 'UNK-13',
'Etc/GMT-14': 'UNK-14',
'Etc/GMT-2': 'UNK-2',
'Etc/GMT-3': 'UNK-3',
'Etc/GMT-4': 'UNK-4',
'Etc/GMT-5': 'UNK-5',
'Etc/GMT-6': 'UNK-6',
'Etc/GMT-7': 'UNK-7',
'Etc/GMT-8': 'UNK-8',
'Etc/GMT-9': 'UNK-9',
'Etc/GMT-1': '<+01>-1',
'Etc/GMT-10': '<+10>-10',
'Etc/GMT-11': '<+11>-11',
'Etc/GMT-12': '<+12>-12',
'Etc/GMT-13': '<+13>-13',
'Etc/GMT-14': '<+14>-14',
'Etc/GMT-2': '<+02>-2',
'Etc/GMT-3': '<+03>-3',
'Etc/GMT-4': '<+04>-4',
'Etc/GMT-5': '<+05>-5',
'Etc/GMT-6': '<+06>-6',
'Etc/GMT-7': '<+07>-7',
'Etc/GMT-8': '<+08>-8',
'Etc/GMT-9': '<+09>-9',
'Etc/GMT0': 'GMT0',
'Etc/Greenwich': 'GMT0',
'Etc/UCT': 'UTC0',
@@ -354,7 +354,7 @@ export const TIME_ZONES: Record<string, string> = {
'Etc/Zulu': 'UTC0',
'Europe/Amsterdam': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Andorra': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Astrakhan': 'UNK-4',
'Europe/Astrakhan': '<+04>-4',
'Europe/Athens': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Belgrade': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Berlin': 'CET-1CEST,M3.5.0,M10.5.0/3',
@@ -363,18 +363,18 @@ export const TIME_ZONES: Record<string, string> = {
'Europe/Bucharest': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Budapest': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Busingen': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Chisinau': 'EET-2EEST,M3.5.0,M10.5.0/3',
'Europe/Chisinau': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Copenhagen': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Dublin': 'IST-1GMT0,M10.5.0,M3.5.0/1',
'Europe/Gibraltar': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Guernsey': 'GMT0BST,M3.5.0/1,M10.5.0',
'Europe/Helsinki': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Isle_of_Man': 'GMT0BST,M3.5.0/1,M10.5.0',
'Europe/Istanbul': 'UNK-3',
'Europe/Istanbul': '<+03>-3',
'Europe/Jersey': 'GMT0BST,M3.5.0/1,M10.5.0',
'Europe/Kaliningrad': 'EET-2',
'Europe/Kiev': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Kirov': 'UNK-3',
'Europe/Kirov': 'MSK-3',
'Europe/Lisbon': 'WET0WEST,M3.5.0/1,M10.5.0',
'Europe/Ljubljana': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/London': 'GMT0BST,M3.5.0/1,M10.5.0',
@@ -382,7 +382,7 @@ export const TIME_ZONES: Record<string, string> = {
'Europe/Madrid': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Malta': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Mariehamn': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Minsk': 'UNK-3',
'Europe/Minsk': '<+03>-3',
'Europe/Monaco': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Moscow': 'MSK-3',
'Europe/Oslo': 'CET-1CEST,M3.5.0,M10.5.0/3',
@@ -391,83 +391,114 @@ export const TIME_ZONES: Record<string, string> = {
'Europe/Prague': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Riga': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Rome': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Samara': 'UNK-4',
'Europe/Samara': '<+04>-4',
'Europe/San_Marino': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Sarajevo': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Saratov': 'UNK-4',
'Europe/Saratov': '<+04>-4',
'Europe/Simferopol': 'MSK-3',
'Europe/Skopje': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Sofia': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Stockholm': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Tallinn': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Tirane': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Ulyanovsk': 'UNK-4',
'Europe/Ulyanovsk': '<+04>-4',
'Europe/Uzhgorod': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Vaduz': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Vatican': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Vienna': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Vilnius': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Volgograd': 'UNK-4',
'Europe/Volgograd': 'MSK-3',
'Europe/Warsaw': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Zagreb': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Europe/Zaporozhye': 'EET-2EEST,M3.5.0/3,M10.5.0/4',
'Europe/Zurich': 'CET-1CEST,M3.5.0,M10.5.0/3',
'Indian/Antananarivo': 'EAT-3',
'Indian/Chagos': 'UNK-6',
'Indian/Christmas': 'UNK-7',
'Indian/Cocos': 'UNK-6:30',
'Indian/Chagos': '<+06>-6',
'Indian/Christmas': '<+07>-7',
'Indian/Cocos': '<+0630>-6:30',
'Indian/Comoro': 'EAT-3',
'Indian/Kerguelen': 'UNK-5',
'Indian/Mahe': 'UNK-4',
'Indian/Maldives': 'UNK-5',
'Indian/Mauritius': 'UNK-4',
'Indian/Kerguelen': '<+05>-5',
'Indian/Mahe': '<+04>-4',
'Indian/Maldives': '<+05>-5',
'Indian/Mauritius': '<+04>-4',
'Indian/Mayotte': 'EAT-3',
'Indian/Reunion': 'UNK-4',
'Pacific/Apia': 'UNK-13UNK,M9.5.0/3,M4.1.0/4',
'Indian/Reunion': '<+04>-4',
'Pacific/Apia': '<+13>-13',
'Pacific/Auckland': 'NZST-12NZDT,M9.5.0,M4.1.0/3',
'Pacific/Bougainville': 'UNK-11',
'Pacific/Chatham': 'UNK-12:45UNK,M9.5.0/2:45,M4.1.0/3:45',
'Pacific/Chuuk': 'UNK-10',
'Pacific/Easter': 'UNK6UNK,M9.1.6/22,M4.1.6/22',
'Pacific/Efate': 'UNK-11',
'Pacific/Enderbury': 'UNK-13',
'Pacific/Fakaofo': 'UNK-13',
'Pacific/Fiji': 'UNK-12UNK,M11.2.0,M1.2.3/99',
'Pacific/Funafuti': 'UNK-12',
'Pacific/Galapagos': 'UNK6',
'Pacific/Gambier': 'UNK9',
'Pacific/Guadalcanal': 'UNK-11',
'Pacific/Bougainville': '<+11>-11',
'Pacific/Chatham': '<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45',
'Pacific/Chuuk': '<+10>-10',
'Pacific/Easter': '<-06>6<-05>,M9.1.6/22,M4.1.6/22',
'Pacific/Efate': '<+11>-11',
'Pacific/Enderbury': '<+13>-13',
'Pacific/Fakaofo': '<+13>-13',
'Pacific/Fiji': '<+12>-12',
'Pacific/Funafuti': '<+12>-12',
'Pacific/Galapagos': '<-06>6',
'Pacific/Gambier': '<-09>9',
'Pacific/Guadalcanal': '<+11>-11',
'Pacific/Guam': 'ChST-10',
'Pacific/Honolulu': 'HST10',
'Pacific/Kiritimati': 'UNK-14',
'Pacific/Kosrae': 'UNK-11',
'Pacific/Kwajalein': 'UNK-12',
'Pacific/Majuro': 'UNK-12',
'Pacific/Marquesas': 'UNK9:30',
'Pacific/Kiritimati': '<+14>-14',
'Pacific/Kosrae': '<+11>-11',
'Pacific/Kwajalein': '<+12>-12',
'Pacific/Majuro': '<+12>-12',
'Pacific/Marquesas': '<-0930>9:30',
'Pacific/Midway': 'SST11',
'Pacific/Nauru': 'UNK-12',
'Pacific/Niue': 'UNK11',
'Pacific/Norfolk': 'UNK-11UNK,M10.1.0,M4.1.0/3',
'Pacific/Noumea': 'UNK-11',
'Pacific/Nauru': '<+12>-12',
'Pacific/Niue': '<-11>11',
'Pacific/Norfolk': '<+11>-11<+12>,M10.1.0,M4.1.0/3',
'Pacific/Noumea': '<+11>-11',
'Pacific/Pago_Pago': 'SST11',
'Pacific/Palau': 'UNK-9',
'Pacific/Pitcairn': 'UNK8',
'Pacific/Pohnpei': 'UNK-11',
'Pacific/Port_Moresby': 'UNK-10',
'Pacific/Rarotonga': 'UNK10',
'Pacific/Palau': '<+09>-9',
'Pacific/Pitcairn': '<-08>8',
'Pacific/Pohnpei': '<+11>-11',
'Pacific/Port_Moresby': '<+10>-10',
'Pacific/Rarotonga': '<-10>10',
'Pacific/Saipan': 'ChST-10',
'Pacific/Tahiti': 'UNK10',
'Pacific/Tarawa': 'UNK-12',
'Pacific/Tongatapu': 'UNK-13',
'Pacific/Wake': 'UNK-12',
'Pacific/Wallis': 'UNK-12'
'Pacific/Tahiti': '<-10>10',
'Pacific/Tarawa': '<+12>-12',
'Pacific/Tongatapu': '<+13>-13',
'Pacific/Wake': '<+12>-12',
'Pacific/Wallis': '<+12>-12'
};
// Pre-compute sorted timezone labels for better performance
export const TIME_ZONE_LABELS = Object.keys(TIME_ZONES).sort();
/** Resolve the select value for a saved zone. Prefer the label when it is in
* TIME_ZONES so a stale stored tz_format (after a table update) still shows the
* correct city; firmware migrates the format from the label on boot. */
export function selectedTimeZone(label: string, format: string) {
return TIME_ZONES[label] === format ? label : undefined;
if (label in TIME_ZONES) {
return label;
}
return Object.keys(TIME_ZONES).find((key) => TIME_ZONES[key] === format);
}
/** Return settings with tz_format / tz_format_t refreshed from TIME_ZONES, or
* null if already current (or labels are unknown). */
export function migrateNtpTimeZones<
T extends {
tz_label: string;
tz_format: string;
tz_label_t: string;
tz_format_t: string;
}
>(settings: T): T | null {
const tz_format = TIME_ZONES[settings.tz_label];
const tz_format_t = TIME_ZONES[settings.tz_label_t];
const next = { ...settings };
let changed = false;
if (tz_format && tz_format !== settings.tz_format) {
next.tz_format = tz_format;
changed = true;
}
if (tz_format_t && tz_format_t !== settings.tz_format_t) {
next.tz_format_t = tz_format_t;
changed = true;
}
return changed ? next : null;
}
const precomputedTimeZoneItems = TIME_ZONE_LABELS.map((label) => (
+7 -5
View File
@@ -13,7 +13,7 @@ using espMqttClientInternals::PacketType;
using espMqttClientTypes::DisconnectReason;
using espMqttClientTypes::Error;
MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority, uint8_t core)
MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority, uint8_t core, uint32_t stackSize)
: _useInternalTask(useInternalTask)
, _transport(nullptr)
, _onConnectCallback(nullptr)
@@ -55,6 +55,7 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
, _disconnectReason(DisconnectReason::TCP_DISCONNECTED)
#if defined(ARDUINO_ARCH_ESP32) && ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
, _highWaterMark(4294967295)
, _stackSize(stackSize)
#endif
{
EMC_GENERATE_CLIENTID(_generatedClientId);
@@ -64,12 +65,12 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
if (_useInternalTask == espMqttClientTypes::UseInternalTask::YES) {
#ifdef CONFIG_FREERTOS_UNICORE
// no core to pin to on single-core targets (S2, C3, C6)
xTaskCreate((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle);
xTaskCreate((TaskFunction_t)_loop, "mqttclient", stackSize, this, priority, &_taskHandle);
#else
if (core > 1) {
xTaskCreate((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle);
xTaskCreate((TaskFunction_t)_loop, "mqttclient", stackSize, this, priority, &_taskHandle);
} else {
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", EMC_TASK_STACK_SIZE, this, priority, &_taskHandle, core);
xTaskCreatePinnedToCore((TaskFunction_t)_loop, "mqttclient", stackSize, this, priority, &_taskHandle, core);
}
#endif
}
@@ -77,6 +78,7 @@ MqttClient::MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint
(void) useInternalTask;
(void) priority;
(void) core;
(void) stackSize;
#endif
_clientId = _generatedClientId;
_core = core;
@@ -320,7 +322,7 @@ void MqttClient::loop() {
size_t waterMark = uxTaskGetStackHighWaterMark(NULL);
if (waterMark < _highWaterMark) {
_highWaterMark = waterMark;
emc_log_i("Stack usage: %zu/%i", EMC_TASK_STACK_SIZE - _highWaterMark, EMC_TASK_STACK_SIZE);
emc_log_i("Stack usage: %zu/%zu", _stackSize - _highWaterMark, _stackSize);
}
#endif
}
+2 -1
View File
@@ -81,7 +81,7 @@ class MqttClient {
}
protected:
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1);
explicit MqttClient(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1, uint32_t stackSize = EMC_TASK_STACK_SIZE);
espMqttClientTypes::UseInternalTask _useInternalTask;
espMqttClientInternals::Transport* _transport;
@@ -207,6 +207,7 @@ class MqttClient {
#if defined(ARDUINO_ARCH_ESP32)
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
size_t _highWaterMark;
size_t _stackSize;
#endif
#endif
};
+2 -2
View File
@@ -208,8 +208,8 @@ class MqttClientSetup : public MqttClient {
*/
protected:
explicit MqttClientSetup(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1)
: MqttClient(useInternalTask, priority, core) {
explicit MqttClientSetup(espMqttClientTypes::UseInternalTask useInternalTask, uint8_t priority = 1, uint8_t core = 1, uint32_t stackSize = EMC_TASK_STACK_SIZE)
: MqttClient(useInternalTask, priority, core, stackSize) {
#if EMC_MULTIPLE_CALLBACKS
_onConnectCallback = [this](bool sessionPresent) {
for (auto callback : _onConnectCallbacks) if (callback.first) callback.first(sessionPresent);
+4 -4
View File
@@ -15,8 +15,8 @@ espMqttClient::espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask
_transport = &_client;
}
espMqttClient::espMqttClient(uint8_t priority, uint8_t core)
: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core)
espMqttClient::espMqttClient(uint8_t priority, uint8_t core, uint32_t stackSize)
: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core, stackSize)
, _client() {
_transport = &_client;
}
@@ -27,8 +27,8 @@ espMqttClientSecure::espMqttClientSecure(espMqttClientTypes::UseInternalTask use
_transport = &_client;
}
espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core)
: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core)
espMqttClientSecure::espMqttClientSecure(uint8_t priority, uint8_t core, uint32_t stackSize)
: MqttClientSetup(espMqttClientTypes::UseInternalTask::YES, priority, core, stackSize)
, _client() {
_transport = &_client;
}
+2 -2
View File
@@ -48,7 +48,7 @@ class espMqttClientSecure : public MqttClientSetup<espMqttClientSecure> {
class espMqttClient : public MqttClientSetup<espMqttClient> {
public:
explicit espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask);
explicit espMqttClient(uint8_t priority = 1, uint8_t core = 1);
explicit espMqttClient(uint8_t priority = 1, uint8_t core = 1, uint32_t stackSize = EMC_TASK_STACK_SIZE);
protected:
espMqttClientInternals::ClientSync _client;
@@ -57,7 +57,7 @@ class espMqttClient : public MqttClientSetup<espMqttClient> {
class espMqttClientSecure : public MqttClientSetup<espMqttClientSecure> {
public:
explicit espMqttClientSecure(espMqttClientTypes::UseInternalTask useInternalTask);
explicit espMqttClientSecure(uint8_t priority = 1, uint8_t core = 1);
explicit espMqttClientSecure(uint8_t priority = 1, uint8_t core = 1, uint32_t stackSize = EMC_TASK_STACK_SIZE);
espMqttClientSecure & setInsecure();
espMqttClientSecure & setCACert(const char * rootCA);
espMqttClientSecure & setCertificate(const char * clientCa);
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Generate src/ESP32React/tz_zones.h from interface/src/app/settings/TZ.tsx.
Run from the repo root:
python3 scripts/generate_tz_zones.py
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
TZ_TSX = ROOT / "interface" / "src" / "app" / "settings" / "TZ.tsx"
OUT_H = ROOT / "src" / "ESP32React" / "tz_zones.h"
def parse_time_zones(text: str) -> list[tuple[str, str]]:
match = re.search(
r"export const TIME_ZONES: Record<string, string> = \{([\s\S]*?)\n\};",
text,
)
if not match:
raise SystemExit("TIME_ZONES table not found in TZ.tsx")
entries = re.findall(r"'((?:\\'|[^'])*)': '((?:\\'|[^'])*)'", match.group(1))
if not entries:
raise SystemExit("no TIME_ZONES entries parsed")
return sorted(entries, key=lambda item: item[0])
def c_string(value: str) -> str:
return '"' + value.replace("\\", "\\\\").replace('"', '\\"') + '"'
def main() -> int:
entries = parse_time_zones(TZ_TSX.read_text(encoding="utf-8"))
max_format = max(len(fmt) for _, fmt in entries)
lines = [
"/* Auto-generated by scripts/generate_tz_zones.py — do not edit. */",
"#pragma once",
"",
"#include <stdlib.h>",
"#include <string.h>",
"",
"struct EmsespTzZone {",
" const char * label;",
" const char * format;",
"};",
"",
f"// Longest POSIX TZ value in the table is {max_format} chars.",
"static constexpr size_t EMSESP_TZ_ZONE_COUNT = "
f"{len(entries)};",
"static const EmsespTzZone EMSESP_TZ_ZONES[EMSESP_TZ_ZONE_COUNT] = {",
]
for label, fmt in entries:
lines.append(f" {{{c_string(label)}, {c_string(fmt)}}},")
lines += [
"};",
"",
"inline int emsesp_tz_zone_cmp(const void * key, const void * element) {",
" return strcmp(static_cast<const char *>(key), static_cast<const EmsespTzZone *>(element)->label);",
"}",
"",
"// Returns the POSIX TZ string for an IANA label, or nullptr if unknown.",
"inline const char * emsesp_tz_format_for_label(const char * label) {",
" if (label == nullptr || *label == '\\0') {",
" return nullptr;",
" }",
" const auto * found = static_cast<const EmsespTzZone *>(",
" bsearch(label, EMSESP_TZ_ZONES, EMSESP_TZ_ZONE_COUNT, sizeof(EmsespTzZone), emsesp_tz_zone_cmp));",
" return found != nullptr ? found->format : nullptr;",
"}",
"",
]
OUT_H.write_text("\n".join(lines), encoding="utf-8")
print(f"Wrote {OUT_H.relative_to(ROOT)} ({len(entries)} zones, max format {max_format})")
return 0
if __name__ == "__main__":
sys.exit(main())
+8 -3
View File
@@ -32,10 +32,13 @@ void MqttSettingsService::begin() {
}
void MqttSettingsService::startClient() {
static bool isSecure = false;
static bool isSecure = false;
static uint32_t stackSize = EMC_TASK_STACK_SIZE;
// only verifying a CA chain needs the larger stack, insecure TLS and plain MQTT fit in the default
const uint32_t wantStack = (_state.enableTLS && _state.rootCA != "insecure") ? EMSESP_MQTT_STACKSIZE_CA : EMC_TASK_STACK_SIZE;
if (_mqttClient) {
// do we need to change the client?
if ((isSecure && _state.enableTLS) || (!isSecure && !_state.enableTLS)) {
if (isSecure == _state.enableTLS && stackSize == wantStack) {
return;
}
@@ -43,12 +46,14 @@ void MqttSettingsService::startClient() {
_mqttClient = nullptr;
}
stackSize = wantStack;
if (_state.enableTLS) {
isSecure = true;
if (emsesp::EMSESP::system_.PSram() == 0) {
_mqttClient = new espMqttClientSecure(espMqttClientTypes::UseInternalTask::NO);
} else {
_mqttClient = new espMqttClientSecure(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE);
_mqttClient = new espMqttClientSecure(EMSESP_MQTT_PRIORITY, EMSESP_MQTT_RUNNING_CORE, wantStack);
}
if (!_mqttClient) {
+5
View File
@@ -60,6 +60,11 @@
#define EMC_TASK_STACK_SIZE EMSESP_MQTT_STACKSIZE
#endif
// MQTT task stack when TLS verifies the broker against a CA. BearSSL needs ~6 KB to verify an EC (P-384, P-521) chain
#ifndef EMSESP_MQTT_STACKSIZE_CA
#define EMSESP_MQTT_STACKSIZE_CA 8192
#endif
#ifndef EMSESP_MQTT_PRIORITY
#define EMSESP_MQTT_PRIORITY 1
#endif
+62 -11
View File
@@ -1,7 +1,25 @@
#include "NTPSettingsService.h"
#include "tz_zones.h"
#include <emsesp.h>
namespace {
String trimmedTzFormat(const String & format) {
int space = format.indexOf(' ');
return space < 0 ? format : format.substring(0, static_cast<unsigned int>(space));
}
void padTzFormat(String & format) {
format.reserve(NTP_TZ_FORMAT_LEN);
while (format.length() < NTP_TZ_FORMAT_LEN) {
format += ' ';
}
}
} // namespace
NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
: _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager)
, _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE)
@@ -16,6 +34,13 @@ NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, Securit
void NTPSettingsService::begin() {
_fsPersistence.readFromFS();
// After an OTA that updates tz_zones.h, rewrite stored POSIX strings from the
// saved IANA labels so localtime / thermostat sync use the current rules
// without the user re-selecting the zone in the WebUI.
update([](NTPSettings & settings) {
return NTPSettings::syncFormatsFromLabels(settings) ? StateUpdateResult::CHANGED : StateUpdateResult::UNCHANGED;
});
}
void NTPSettingsService::loop() {
@@ -77,15 +102,40 @@ void NTPSettings::read(NTPSettings & settings, JsonObject root) {
root["enabled"] = settings.enabled;
root["server"] = settings.server;
root["tz_label"] = settings.tzLabel;
root["tz_format"] = settings.tzFormat.substring(0, settings.tzFormat.indexOf(" "));
root["tz_format"] = trimmedTzFormat(settings.tzFormat);
root["thermostat_sync"] = settings.thermostat_sync;
root["tz_label_t"] = settings.tzLabelT;
root["tz_format_t"] = settings.tzFormatT.substring(0, settings.tzFormatT.indexOf(" "));
root["tz_format_t"] = trimmedTzFormat(settings.tzFormatT);
}
bool NTPSettings::syncFormatsFromLabels(NTPSettings & settings) {
bool changed = false;
auto apply = [&](const String & label, String & format) {
const char * mapped = emsesp_tz_format_for_label(label.c_str());
if (mapped == nullptr) {
return;
}
if (trimmedTzFormat(format) == mapped) {
// Still re-pad in case NTP_TZ_FORMAT_LEN grew since the value was stored.
if (format.length() != NTP_TZ_FORMAT_LEN) {
format = mapped;
padTzFormat(format);
changed = true;
}
return;
}
format = mapped;
padTzFormat(format);
changed = true;
};
apply(settings.tzLabel, settings.tzFormat);
apply(settings.tzLabelT, settings.tzFormatT);
return changed;
}
StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
settings.tzFormat.reserve(40);
settings.tzFormatT.reserve(40);
settings.enabled = root["enabled"] | FACTORY_NTP_ENABLED;
settings.server = root["server"] | FACTORY_NTP_SERVER;
settings.tzLabel = root["tz_label"] | FACTORY_NTP_TIME_ZONE_LABEL;
@@ -93,14 +143,15 @@ StateUpdateResult NTPSettings::update(JsonObject root, NTPSettings & settings) {
settings.thermostat_sync = root["thermostat_sync"] | FACTORY_NTP_THERMOSTAT_SYNC;
settings.tzLabelT = root["tz_label_t"] | FACTORY_NTP_TIME_ZONE_LABEL;
settings.tzFormatT = root["tz_format_t"] | FACTORY_NTP_TIME_ZONE_FORMAT;
// The format string must have same length to avoid memory leak, #3184
while (settings.tzFormat.length() < 40) {
settings.tzFormat += " ";
}
while (settings.tzFormatT.length() < 40) {
settings.tzFormatT += " ";
}
// Do not call syncFormatsFromLabels here: readFromFS() uses this updater
// via updateWithoutPropagation, and a sync at that point would not be
// written back. Boot migration runs in NTPSettingsService::begin() instead.
// The WebUI always POSTs tz_format from TIME_ZONES when the user saves.
// The format string must have a fixed length to avoid a setenv leak, #3184.
padTzFormat(settings.tzFormat);
padTzFormat(settings.tzFormatT);
return StateUpdateResult::CHANGED;
}
+10
View File
@@ -32,6 +32,12 @@
#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings"
#define TIME_PATH "/rest/time"
// Fixed length for tz_format passed to setenv to avoid the realloc leak in #3184.
// Must be >= longest POSIX TZ string in tz_zones.h (currently 44 for Pacific/Chatham).
#ifndef NTP_TZ_FORMAT_LEN
#define NTP_TZ_FORMAT_LEN 48
#endif
class NTPSettings {
public:
bool enabled = FACTORY_NTP_ENABLED;
@@ -44,6 +50,10 @@ class NTPSettings {
static void read(NTPSettings & settings, JsonObject root);
static StateUpdateResult update(JsonObject root, NTPSettings & settings);
// Refresh tz_format / tz_format_t from the IANA label using tz_zones.h.
// Returns true if either format string changed.
static bool syncFormatsFromLabels(NTPSettings & settings);
};
class NTPSettingsService : public StatefulService<NTPSettings> {
+489
View File
@@ -0,0 +1,489 @@
/* Auto-generated by scripts/generate_tz_zones.py — do not edit. */
#pragma once
#include <stdlib.h>
#include <string.h>
struct EmsespTzZone {
const char * label;
const char * format;
};
// Longest POSIX TZ value in the table is 44 chars.
static constexpr size_t EMSESP_TZ_ZONE_COUNT = 460;
static const EmsespTzZone EMSESP_TZ_ZONES[EMSESP_TZ_ZONE_COUNT] = {
{"Africa/Abidjan", "GMT0"},
{"Africa/Accra", "GMT0"},
{"Africa/Addis_Ababa", "EAT-3"},
{"Africa/Algiers", "CET-1"},
{"Africa/Asmara", "EAT-3"},
{"Africa/Bamako", "GMT0"},
{"Africa/Bangui", "WAT-1"},
{"Africa/Banjul", "GMT0"},
{"Africa/Bissau", "GMT0"},
{"Africa/Blantyre", "CAT-2"},
{"Africa/Brazzaville", "WAT-1"},
{"Africa/Bujumbura", "CAT-2"},
{"Africa/Cairo", "EET-2EEST,M4.5.5/0,M10.5.4/24"},
{"Africa/Casablanca", "<+00>0"},
{"Africa/Ceuta", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Africa/Conakry", "GMT0"},
{"Africa/Dakar", "GMT0"},
{"Africa/Dar_es_Salaam", "EAT-3"},
{"Africa/Djibouti", "EAT-3"},
{"Africa/Douala", "WAT-1"},
{"Africa/El_Aaiun", "<+00>0"},
{"Africa/Freetown", "GMT0"},
{"Africa/Gaborone", "CAT-2"},
{"Africa/Harare", "CAT-2"},
{"Africa/Johannesburg", "SAST-2"},
{"Africa/Juba", "CAT-2"},
{"Africa/Kampala", "EAT-3"},
{"Africa/Khartoum", "CAT-2"},
{"Africa/Kigali", "CAT-2"},
{"Africa/Kinshasa", "WAT-1"},
{"Africa/Lagos", "WAT-1"},
{"Africa/Libreville", "WAT-1"},
{"Africa/Lome", "GMT0"},
{"Africa/Luanda", "WAT-1"},
{"Africa/Lubumbashi", "CAT-2"},
{"Africa/Lusaka", "CAT-2"},
{"Africa/Malabo", "WAT-1"},
{"Africa/Maputo", "CAT-2"},
{"Africa/Maseru", "SAST-2"},
{"Africa/Mbabane", "SAST-2"},
{"Africa/Mogadishu", "EAT-3"},
{"Africa/Monrovia", "GMT0"},
{"Africa/Nairobi", "EAT-3"},
{"Africa/Ndjamena", "WAT-1"},
{"Africa/Niamey", "WAT-1"},
{"Africa/Nouakchott", "GMT0"},
{"Africa/Ouagadougou", "GMT0"},
{"Africa/Porto-Novo", "WAT-1"},
{"Africa/Sao_Tome", "GMT0"},
{"Africa/Tripoli", "EET-2"},
{"Africa/Tunis", "CET-1"},
{"Africa/Windhoek", "CAT-2"},
{"America/Adak", "HST10HDT,M3.2.0,M11.1.0"},
{"America/Anchorage", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Anguilla", "AST4"},
{"America/Antigua", "AST4"},
{"America/Araguaina", "<-03>3"},
{"America/Argentina/Buenos_Aires", "<-03>3"},
{"America/Argentina/Catamarca", "<-03>3"},
{"America/Argentina/Cordoba", "<-03>3"},
{"America/Argentina/Jujuy", "<-03>3"},
{"America/Argentina/La_Rioja", "<-03>3"},
{"America/Argentina/Mendoza", "<-03>3"},
{"America/Argentina/Rio_Gallegos", "<-03>3"},
{"America/Argentina/Salta", "<-03>3"},
{"America/Argentina/San_Juan", "<-03>3"},
{"America/Argentina/San_Luis", "<-03>3"},
{"America/Argentina/Tucuman", "<-03>3"},
{"America/Argentina/Ushuaia", "<-03>3"},
{"America/Aruba", "AST4"},
{"America/Asuncion", "<-03>3"},
{"America/Atikokan", "EST5"},
{"America/Bahia", "<-03>3"},
{"America/Bahia_Banderas", "CST6"},
{"America/Barbados", "AST4"},
{"America/Belem", "<-03>3"},
{"America/Belize", "CST6"},
{"America/Blanc-Sablon", "AST4"},
{"America/Boa_Vista", "<-04>4"},
{"America/Bogota", "<-05>5"},
{"America/Boise", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Cambridge_Bay", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Campo_Grande", "<-04>4"},
{"America/Cancun", "EST5"},
{"America/Caracas", "<-04>4"},
{"America/Cayenne", "<-03>3"},
{"America/Cayman", "EST5"},
{"America/Chicago", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Chihuahua", "CST6"},
{"America/Costa_Rica", "CST6"},
{"America/Creston", "MST7"},
{"America/Cuiaba", "<-04>4"},
{"America/Curacao", "AST4"},
{"America/Danmarkshavn", "GMT0"},
{"America/Dawson", "MST7"},
{"America/Dawson_Creek", "MST7"},
{"America/Denver", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Detroit", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Dominica", "AST4"},
{"America/Edmonton", "CST6"},
{"America/Eirunepe", "<-05>5"},
{"America/El_Salvador", "CST6"},
{"America/Fort_Nelson", "MST7"},
{"America/Fortaleza", "<-03>3"},
{"America/Glace_Bay", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Godthab", "<-02>2<-01>,M3.5.0/-1,M10.5.0/0"},
{"America/Goose_Bay", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Grand_Turk", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Grenada", "AST4"},
{"America/Guadeloupe", "AST4"},
{"America/Guatemala", "CST6"},
{"America/Guayaquil", "<-05>5"},
{"America/Guyana", "<-04>4"},
{"America/Halifax", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Havana", "CST5CDT,M3.2.0/0,M11.1.0/1"},
{"America/Hermosillo", "MST7"},
{"America/Indiana/Indianapolis", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Knox", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Indiana/Marengo", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Petersburg", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Tell_City", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Indiana/Vevay", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Vincennes", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Winamac", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Inuvik", "CST6"},
{"America/Iqaluit", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Jamaica", "EST5"},
{"America/Juneau", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Kentucky/Louisville", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Kentucky/Monticello", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Kralendijk", "AST4"},
{"America/La_Paz", "<-04>4"},
{"America/Lima", "<-05>5"},
{"America/Los_Angeles", "PST8PDT,M3.2.0,M11.1.0"},
{"America/Lower_Princes", "AST4"},
{"America/Maceio", "<-03>3"},
{"America/Managua", "CST6"},
{"America/Manaus", "<-04>4"},
{"America/Marigot", "AST4"},
{"America/Martinique", "AST4"},
{"America/Matamoros", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Mazatlan", "MST7"},
{"America/Menominee", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Merida", "CST6"},
{"America/Metlakatla", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Mexico_City", "CST6"},
{"America/Miquelon", "<-03>3<-02>,M3.2.0,M11.1.0"},
{"America/Moncton", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Monterrey", "CST6"},
{"America/Montevideo", "<-03>3"},
{"America/Montreal", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Montserrat", "AST4"},
{"America/Nassau", "EST5EDT,M3.2.0,M11.1.0"},
{"America/New_York", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Nipigon", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Nome", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Noronha", "<-02>2"},
{"America/North_Dakota/Beulah", "CST6CDT,M3.2.0,M11.1.0"},
{"America/North_Dakota/Center", "CST6CDT,M3.2.0,M11.1.0"},
{"America/North_Dakota/New_Salem", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Ojinaga", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Panama", "EST5"},
{"America/Pangnirtung", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Paramaribo", "<-03>3"},
{"America/Phoenix", "MST7"},
{"America/Port-au-Prince", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Port_of_Spain", "AST4"},
{"America/Porto_Velho", "<-04>4"},
{"America/Puerto_Rico", "AST4"},
{"America/Punta_Arenas", "<-03>3"},
{"America/Rainy_River", "EST5"},
{"America/Rankin_Inlet", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Recife", "<-03>3"},
{"America/Regina", "CST6"},
{"America/Resolute", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Rio_Branco", "<-05>5"},
{"America/Santarem", "<-03>3"},
{"America/Santiago", "<-04>4<-03>,M9.1.6/24,M4.1.6/24"},
{"America/Santo_Domingo", "AST4"},
{"America/Sao_Paulo", "<-03>3"},
{"America/Scoresbysund", "<-02>2<-01>,M3.5.0/-1,M10.5.0/0"},
{"America/Sitka", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/St_Barthelemy", "AST4"},
{"America/St_Johns", "NST3:30NDT,M3.2.0,M11.1.0"},
{"America/St_Kitts", "AST4"},
{"America/St_Lucia", "AST4"},
{"America/St_Thomas", "AST4"},
{"America/St_Vincent", "AST4"},
{"America/Swift_Current", "CST6"},
{"America/Tegucigalpa", "CST6"},
{"America/Thule", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Thunder_Bay", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Tijuana", "PST8PDT,M3.2.0,M11.1.0"},
{"America/Toronto", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Tortola", "AST4"},
{"America/Vancouver", "MST7"},
{"America/Whitehorse", "MST7"},
{"America/Winnipeg", "EST5"},
{"America/Yakutat", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Yellowknife", "CST6"},
{"Antarctica/Casey", "<+08>-8"},
{"Antarctica/Davis", "<+07>-7"},
{"Antarctica/DumontDUrville", "<+10>-10"},
{"Antarctica/Macquarie", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Antarctica/Mawson", "<+05>-5"},
{"Antarctica/McMurdo", "NZST-12NZDT,M9.5.0,M4.1.0/3"},
{"Antarctica/Palmer", "<-03>3"},
{"Antarctica/Rothera", "<-03>3"},
{"Antarctica/Syowa", "<+03>-3"},
{"Antarctica/Troll", "<+00>0<+02>-2,M3.5.0/1,M10.5.0/3"},
{"Antarctica/Vostok", "<+05>-5"},
{"Arctic/Longyearbyen", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Asia/Aden", "<+03>-3"},
{"Asia/Almaty", "<+05>-5"},
{"Asia/Amman", "<+03>-3"},
{"Asia/Anadyr", "<+12>-12"},
{"Asia/Aqtau", "<+05>-5"},
{"Asia/Aqtobe", "<+05>-5"},
{"Asia/Ashgabat", "<+05>-5"},
{"Asia/Atyrau", "<+05>-5"},
{"Asia/Baghdad", "<+03>-3"},
{"Asia/Bahrain", "<+03>-3"},
{"Asia/Baku", "<+04>-4"},
{"Asia/Bangkok", "<+07>-7"},
{"Asia/Barnaul", "<+07>-7"},
{"Asia/Beirut", "EET-2EEST,M3.5.0/0,M10.5.0/0"},
{"Asia/Bishkek", "<+06>-6"},
{"Asia/Brunei", "<+08>-8"},
{"Asia/Chita", "<+09>-9"},
{"Asia/Choibalsan", "<+08>-8"},
{"Asia/Colombo", "<+0530>-5:30"},
{"Asia/Damascus", "<+03>-3"},
{"Asia/Dhaka", "<+06>-6"},
{"Asia/Dili", "<+09>-9"},
{"Asia/Dubai", "<+04>-4"},
{"Asia/Dushanbe", "<+05>-5"},
{"Asia/Famagusta", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Asia/Gaza", "EET-2EEST,M3.4.4/50,M10.4.4/50"},
{"Asia/Hebron", "EET-2EEST,M3.4.4/50,M10.4.4/50"},
{"Asia/Ho_Chi_Minh", "<+07>-7"},
{"Asia/Hong_Kong", "HKT-8"},
{"Asia/Hovd", "<+07>-7"},
{"Asia/Irkutsk", "<+08>-8"},
{"Asia/Jakarta", "WIB-7"},
{"Asia/Jayapura", "WIT-9"},
{"Asia/Jerusalem", "IST-2IDT,M3.4.4/26,M10.5.0"},
{"Asia/Kabul", "<+0430>-4:30"},
{"Asia/Kamchatka", "<+12>-12"},
{"Asia/Karachi", "PKT-5"},
{"Asia/Kathmandu", "<+0545>-5:45"},
{"Asia/Khandyga", "<+09>-9"},
{"Asia/Kolkata", "IST-5:30"},
{"Asia/Krasnoyarsk", "<+07>-7"},
{"Asia/Kuala_Lumpur", "<+08>-8"},
{"Asia/Kuching", "<+08>-8"},
{"Asia/Kuwait", "<+03>-3"},
{"Asia/Macau", "CST-8"},
{"Asia/Magadan", "<+11>-11"},
{"Asia/Makassar", "WITA-8"},
{"Asia/Manila", "PST-8"},
{"Asia/Muscat", "<+04>-4"},
{"Asia/Nicosia", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Asia/Novokuznetsk", "<+07>-7"},
{"Asia/Novosibirsk", "<+07>-7"},
{"Asia/Omsk", "<+06>-6"},
{"Asia/Oral", "<+05>-5"},
{"Asia/Phnom_Penh", "<+07>-7"},
{"Asia/Pontianak", "WIB-7"},
{"Asia/Pyongyang", "KST-9"},
{"Asia/Qatar", "<+03>-3"},
{"Asia/Qyzylorda", "<+05>-5"},
{"Asia/Riyadh", "<+03>-3"},
{"Asia/Sakhalin", "<+11>-11"},
{"Asia/Samarkand", "<+05>-5"},
{"Asia/Seoul", "KST-9"},
{"Asia/Shanghai", "CST-8"},
{"Asia/Singapore", "<+08>-8"},
{"Asia/Srednekolymsk", "<+11>-11"},
{"Asia/Taipei", "CST-8"},
{"Asia/Tashkent", "<+05>-5"},
{"Asia/Tbilisi", "<+04>-4"},
{"Asia/Tehran", "<+0330>-3:30"},
{"Asia/Thimphu", "<+06>-6"},
{"Asia/Tokyo", "JST-9"},
{"Asia/Tomsk", "<+07>-7"},
{"Asia/Ulaanbaatar", "<+08>-8"},
{"Asia/Urumqi", "<+06>-6"},
{"Asia/Ust-Nera", "<+10>-10"},
{"Asia/Vientiane", "<+07>-7"},
{"Asia/Vladivostok", "<+10>-10"},
{"Asia/Yakutsk", "<+09>-9"},
{"Asia/Yangon", "<+0630>-6:30"},
{"Asia/Yekaterinburg", "<+05>-5"},
{"Asia/Yerevan", "<+04>-4"},
{"Atlantic/Azores", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"},
{"Atlantic/Bermuda", "AST4ADT,M3.2.0,M11.1.0"},
{"Atlantic/Canary", "WET0WEST,M3.5.0/1,M10.5.0"},
{"Atlantic/Cape_Verde", "<-01>1"},
{"Atlantic/Faroe", "WET0WEST,M3.5.0/1,M10.5.0"},
{"Atlantic/Madeira", "WET0WEST,M3.5.0/1,M10.5.0"},
{"Atlantic/Reykjavik", "GMT0"},
{"Atlantic/South_Georgia", "<-02>2"},
{"Atlantic/St_Helena", "GMT0"},
{"Atlantic/Stanley", "<-03>3"},
{"Australia/Adelaide", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"},
{"Australia/Brisbane", "AEST-10"},
{"Australia/Broken_Hill", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"},
{"Australia/Currie", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Australia/Darwin", "ACST-9:30"},
{"Australia/Eucla", "<+0845>-8:45"},
{"Australia/Hobart", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Australia/Lindeman", "AEST-10"},
{"Australia/Lord_Howe", "<+1030>-10:30<+11>-11,M10.1.0,M4.1.0"},
{"Australia/Melbourne", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Australia/Perth", "AWST-8"},
{"Australia/Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Etc/GMT", "GMT0"},
{"Etc/GMT+0", "GMT0"},
{"Etc/GMT+1", "<-01>1"},
{"Etc/GMT+10", "<-10>10"},
{"Etc/GMT+11", "<-11>11"},
{"Etc/GMT+12", "<-12>12"},
{"Etc/GMT+2", "<-02>2"},
{"Etc/GMT+3", "<-03>3"},
{"Etc/GMT+4", "<-04>4"},
{"Etc/GMT+5", "<-05>5"},
{"Etc/GMT+6", "<-06>6"},
{"Etc/GMT+7", "<-07>7"},
{"Etc/GMT+8", "<-08>8"},
{"Etc/GMT+9", "<-09>9"},
{"Etc/GMT-0", "GMT0"},
{"Etc/GMT-1", "<+01>-1"},
{"Etc/GMT-10", "<+10>-10"},
{"Etc/GMT-11", "<+11>-11"},
{"Etc/GMT-12", "<+12>-12"},
{"Etc/GMT-13", "<+13>-13"},
{"Etc/GMT-14", "<+14>-14"},
{"Etc/GMT-2", "<+02>-2"},
{"Etc/GMT-3", "<+03>-3"},
{"Etc/GMT-4", "<+04>-4"},
{"Etc/GMT-5", "<+05>-5"},
{"Etc/GMT-6", "<+06>-6"},
{"Etc/GMT-7", "<+07>-7"},
{"Etc/GMT-8", "<+08>-8"},
{"Etc/GMT-9", "<+09>-9"},
{"Etc/GMT0", "GMT0"},
{"Etc/Greenwich", "GMT0"},
{"Etc/UCT", "UTC0"},
{"Etc/UTC", "UTC0"},
{"Etc/Universal", "UTC0"},
{"Etc/Zulu", "UTC0"},
{"Europe/Amsterdam", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Andorra", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Astrakhan", "<+04>-4"},
{"Europe/Athens", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Belgrade", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Berlin", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Bratislava", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Brussels", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Bucharest", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Budapest", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Busingen", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Chisinau", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Copenhagen", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Dublin", "IST-1GMT0,M10.5.0,M3.5.0/1"},
{"Europe/Gibraltar", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Guernsey", "GMT0BST,M3.5.0/1,M10.5.0"},
{"Europe/Helsinki", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Isle_of_Man", "GMT0BST,M3.5.0/1,M10.5.0"},
{"Europe/Istanbul", "<+03>-3"},
{"Europe/Jersey", "GMT0BST,M3.5.0/1,M10.5.0"},
{"Europe/Kaliningrad", "EET-2"},
{"Europe/Kiev", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Kirov", "MSK-3"},
{"Europe/Lisbon", "WET0WEST,M3.5.0/1,M10.5.0"},
{"Europe/Ljubljana", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/London", "GMT0BST,M3.5.0/1,M10.5.0"},
{"Europe/Luxembourg", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Madrid", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Malta", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Mariehamn", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Minsk", "<+03>-3"},
{"Europe/Monaco", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Moscow", "MSK-3"},
{"Europe/Oslo", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Paris", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Podgorica", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Prague", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Riga", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Rome", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Samara", "<+04>-4"},
{"Europe/San_Marino", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Sarajevo", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Saratov", "<+04>-4"},
{"Europe/Simferopol", "MSK-3"},
{"Europe/Skopje", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Sofia", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Stockholm", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Tallinn", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Tirane", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Ulyanovsk", "<+04>-4"},
{"Europe/Uzhgorod", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Vaduz", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Vatican", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Vienna", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Vilnius", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Volgograd", "MSK-3"},
{"Europe/Warsaw", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Zagreb", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Zaporozhye", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Zurich", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Indian/Antananarivo", "EAT-3"},
{"Indian/Chagos", "<+06>-6"},
{"Indian/Christmas", "<+07>-7"},
{"Indian/Cocos", "<+0630>-6:30"},
{"Indian/Comoro", "EAT-3"},
{"Indian/Kerguelen", "<+05>-5"},
{"Indian/Mahe", "<+04>-4"},
{"Indian/Maldives", "<+05>-5"},
{"Indian/Mauritius", "<+04>-4"},
{"Indian/Mayotte", "EAT-3"},
{"Indian/Reunion", "<+04>-4"},
{"Pacific/Apia", "<+13>-13"},
{"Pacific/Auckland", "NZST-12NZDT,M9.5.0,M4.1.0/3"},
{"Pacific/Bougainville", "<+11>-11"},
{"Pacific/Chatham", "<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45"},
{"Pacific/Chuuk", "<+10>-10"},
{"Pacific/Easter", "<-06>6<-05>,M9.1.6/22,M4.1.6/22"},
{"Pacific/Efate", "<+11>-11"},
{"Pacific/Enderbury", "<+13>-13"},
{"Pacific/Fakaofo", "<+13>-13"},
{"Pacific/Fiji", "<+12>-12"},
{"Pacific/Funafuti", "<+12>-12"},
{"Pacific/Galapagos", "<-06>6"},
{"Pacific/Gambier", "<-09>9"},
{"Pacific/Guadalcanal", "<+11>-11"},
{"Pacific/Guam", "ChST-10"},
{"Pacific/Honolulu", "HST10"},
{"Pacific/Kiritimati", "<+14>-14"},
{"Pacific/Kosrae", "<+11>-11"},
{"Pacific/Kwajalein", "<+12>-12"},
{"Pacific/Majuro", "<+12>-12"},
{"Pacific/Marquesas", "<-0930>9:30"},
{"Pacific/Midway", "SST11"},
{"Pacific/Nauru", "<+12>-12"},
{"Pacific/Niue", "<-11>11"},
{"Pacific/Norfolk", "<+11>-11<+12>,M10.1.0,M4.1.0/3"},
{"Pacific/Noumea", "<+11>-11"},
{"Pacific/Pago_Pago", "SST11"},
{"Pacific/Palau", "<+09>-9"},
{"Pacific/Pitcairn", "<-08>8"},
{"Pacific/Pohnpei", "<+11>-11"},
{"Pacific/Port_Moresby", "<+10>-10"},
{"Pacific/Rarotonga", "<-10>10"},
{"Pacific/Saipan", "ChST-10"},
{"Pacific/Tahiti", "<-10>10"},
{"Pacific/Tarawa", "<+12>-12"},
{"Pacific/Tongatapu", "<+13>-13"},
{"Pacific/Wake", "<+12>-12"},
{"Pacific/Wallis", "<+12>-12"},
};
inline int emsesp_tz_zone_cmp(const void * key, const void * element) {
return strcmp(static_cast<const char *>(key), static_cast<const EmsespTzZone *>(element)->label);
}
// Returns the POSIX TZ string for an IANA label, or nullptr if unknown.
inline const char * emsesp_tz_format_for_label(const char * label) {
if (label == nullptr || *label == '\0') {
return nullptr;
}
const auto * found = static_cast<const EmsespTzZone *>(
bsearch(label, EMSESP_TZ_ZONES, EMSESP_TZ_ZONE_COUNT, sizeof(EmsespTzZone), emsesp_tz_zone_cmp));
return found != nullptr ? found->format : nullptr;
}
+1 -1
View File
@@ -1 +1 @@
#define EMSESP_APP_VERSION "3.9.0-dev.19"
#define EMSESP_APP_VERSION "3.9.0-dev.20"