From c6c860254ca0881faa92c92b69d9ae8d210fe92b Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Thu, 13 Aug 2026 01:56:36 -0400 Subject: [PATCH] [ADD] tui qemu: pick the time zone from a list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timezone was typed by hand. A misspelt IANA name is not rejected by cloud-init: it is IGNORED. The VM stays on UTC, and you only notice from the timestamps, once deployed. A list of twenty-five zones, Québec first, then the rest of Canada and the places one actually meets. The host's zone goes to the top, without duplication: a machine outside this list must still see its own at a glance. NAMES, not offsets: "UTC-5" says nothing about daylight saving and cloud-init will not take it. A name carries its own switching rules. "free value…" keeps the door open to the other six hundred zones in the database. The choice is copied into the field, which stays the only value the spec reads -- one place holds the answer. --- FR --- Le fuseau se tapait à la main. Un nom IANA mal orthographié n'est pas refusé par cloud-init : il est IGNORÉ. La VM reste en UTC, et on ne s'en aperçoit qu'aux horodatages, une fois déployée. Une liste de vingt-cinq fuseaux, le Québec d'abord, puis le reste du Canada et les places qu'on rencontre en pratique. Le fuseau de l'hôte passe en tête, sans doublon : une machine hors de cette liste doit voir le sien en un coup d'œil. Des NOMS, pas des décalages : « UTC-5 » ne dit rien de l'heure d'été et cloud-init n'en veut pas. Un nom porte ses propres règles de bascule. « libre… » garde la porte ouverte aux six cents autres fuseaux de la base. Le choix est recopié dans le champ, qui reste la seule valeur lue par la spec — un seul endroit porte la réponse. Assisted-by: Claude Opus 5 --- script/todo/qemu_deploy_form.py | 28 +++++++++++++++++++ script/todo/todo.py | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/script/todo/qemu_deploy_form.py b/script/todo/qemu_deploy_form.py index 04a787d..61c5397 100644 --- a/script/todo/qemu_deploy_form.py +++ b/script/todo/qemu_deploy_form.py @@ -370,6 +370,8 @@ def run_deploy_form(ctx, run_app: bool = True): # livrent snapd — la question n'a de sens que pour celles-là, graphiques. app_stores = list(ctx.get("app_stores") or []) snap_distros = set(ctx.get("snap_distros") or ()) + # Fuseaux proposés, celui de l'hôte en tête (voir todo.py). + timezones = list(ctx.get("timezones") or []) defaults = ctx.get("defaults") or {} result = {"spec": None} @@ -683,10 +685,23 @@ def run_deploy_form(ctx, run_app: bool = True): id="f_monitor", ) yield Static(t("Timezone"), classes="grouptitle") + # Une liste plutôt qu'une saisie : un nom IANA mal + # orthographié n'est pas refusé par cloud-init, il est + # IGNORÉ — la VM reste en UTC et on ne s'en aperçoit + # qu'aux horodatages. « libre… » garde la porte ouverte + # aux six cents autres fuseaux de la base. + yield Select( + [(z, z) for z in timezones] + + [(t("free value…"), FREE)], + value=(timezones[0] if timezones else SELECT_NULL), + allow_blank=False, + id="f_tz_sel", + ) yield Input( value=ctx.get("timezone") or "", placeholder=t("Timezone for the VMs"), id="f_tz", + classes="freeval", ) yield Static("SSH", classes="grouptitle") yield Input( @@ -1452,6 +1467,19 @@ def run_deploy_form(ctx, run_app: bool = True): # ci-dessous sans rien recalculer, et les rangées restaient sur # l'ancienne version. Elles n'en gardent pas de copie — « » y # veut dire « celle du formulaire » — il suffit de redessiner. + if event.select.id == "f_tz_sel": + # « libre… » révèle la saisie ; un fuseau choisi la referme et + # y recopie le nom, seule valeur que lisent _form_values et la + # spec — un seul endroit porte la réponse. + free = event.value is FREE + field = self.query_one("#f_tz", Input) + field.display = free + field.disabled = not free + if free: + field.focus() + elif isinstance(event.value, str): + field.value = event.value + return if event.select.id in ("f_branch", "f_profile_install"): self._clear_overrides( ("branch",) diff --git a/script/todo/todo.py b/script/todo/todo.py index 7e6528a..949ab41 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -4413,6 +4413,52 @@ class TODO: ) QEMU_SNAP_DISTROS = ("ubuntu",) + # Fuseaux proposés au déploiement. Des NOMS IANA, pas des décalages : + # cloud-init écrit /etc/timezone et refuse « UTC-5 », qui ne dit d'ailleurs + # rien de l'heure d'été. Un nom porte ses propres règles de bascule. + # + # Liste courte et ordonnée par usage réel plutôt qu'exhaustive : la base + # IANA en compte près de six cents, illisibles dans une liste déroulante. + # Le Québec d'abord, le reste du Canada ensuite, puis les places qu'on + # rencontre en pratique. La saisie libre reste offerte pour le reste. + QEMU_TIMEZONES = ( + "America/Montreal", + "America/Toronto", + "America/Halifax", + "America/Winnipeg", + "America/Edmonton", + "America/Vancouver", + "America/St_Johns", + "UTC", + "America/New_York", + "America/Chicago", + "America/Denver", + "America/Los_Angeles", + "America/Sao_Paulo", + "Europe/London", + "Europe/Paris", + "Europe/Brussels", + "Europe/Zurich", + "Europe/Madrid", + "Europe/Berlin", + "Africa/Casablanca", + "Asia/Dubai", + "Asia/Kolkata", + "Asia/Shanghai", + "Asia/Tokyo", + "Australia/Sydney", + ) + + @classmethod + def _qemu_timezone_choices(cls, current=""): + """Liste à proposer : le fuseau de l'hôte en tête, sans doublon. + + Le mettre en premier plutôt que de le supposer présent : une machine + hors de cette liste doit quand même voir le sien en un coup d'œil.""" + out = [current] if current else [] + out += [z for z in cls.QEMU_TIMEZONES if z != current] + return out + @classmethod def _qemu_desktop_suffixes(cls): """{clé de saveur: suffixe de nom}. La TUI le reçoit par son contexte @@ -5762,6 +5808,9 @@ class TODO: "desktop_disk_gb": self.QEMU_DESKTOP_EXTRA_DISK_GB, "mise_arches": self.QEMU_MISE_ARCHES, "app_stores": [(k, t(lbl)) for k, lbl in self.QEMU_APP_STORES], + "timezones": self._qemu_timezone_choices( + self._qemu_host_timezone() + ), "snap_distros": self.QEMU_SNAP_DISTROS, "desktop_suffixes": self._qemu_desktop_suffixes(), "desktops": [