#!/usr/bin/env python3 """ Generates configs/eventnotifier_timezones.cfg from the system tzdata. Each zone lists "" " " pairs; the "0" entry is the offset in effect before the first transition in range. Regenerate before END_YEAR runs out (32-bit timestamps end in 2038 anyway). """ import re import zoneinfo from datetime import datetime, timedelta, timezone from pathlib import Path START_YEAR = 2025 END_YEAR = 2037 OUT = Path(__file__).resolve().parent.parent / "configs" / "eventnotifier_timezones.cfg" def utc(ts): return datetime.fromtimestamp(ts, timezone.utc) def state(zone, ts): local = utc(ts).astimezone(zone) return int(local.utcoffset().total_seconds()), local.tzname() def abbreviation(name, offset): # tzdata uses numeric names like "+03" / "-0330" for zones without a real abbreviation. if name and re.fullmatch(r"[A-Za-z]+", name): return name sign = "+" if offset >= 0 else "-" hours, rem = divmod(abs(offset), 3600) minutes = rem // 60 if hours == 0 and minutes == 0: return "UTC" return f"UTC{sign}{hours}:{minutes:02d}" if minutes else f"UTC{sign}{hours}" def transitions(zone): start = int(datetime(START_YEAR, 1, 1, tzinfo=timezone.utc).timestamp()) end = int(datetime(END_YEAR, 12, 31, tzinfo=timezone.utc).timestamp()) step = int(timedelta(days=1).total_seconds()) periods = [(0, *state(zone, start))] prev_ts, prev = start, periods[0][1:] ts = start + step while ts <= end: cur = state(zone, ts) if cur != prev: # Bisect to the first second of the new state. lo, hi = prev_ts, ts while hi - lo > 1: mid = (lo + hi) // 2 if state(zone, mid) == prev: lo = mid else: hi = mid periods.append((hi, *state(zone, hi))) prev = cur prev_ts = ts ts += step return periods def main(): lines = ["// Generated by tools/gen_timezones.py, do not edit by hand.", '"Timezones"', "{"] for name in sorted(zoneinfo.available_timezones()): if name.startswith(("posix/", "right/")) or name in ("Factory", "localtime"): continue zone = zoneinfo.ZoneInfo(name) lines.append(f'\t"{name}"') lines.append("\t{") for ts, offset, tzname in transitions(zone): lines.append(f'\t\t"{ts}"\t"{offset} {abbreviation(tzname, offset)}"') lines.append("\t}") lines.append("}") OUT.parent.mkdir(parents=True, exist_ok=True) OUT.write_text("\n".join(lines) + "\n") print(f"Wrote {OUT}") if __name__ == "__main__": main()