mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2026-10-28 11:14:58 +00:00
84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
#!/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())
|