From 64807889794dfbf0755c9d9836ce439b6c1821c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Wed, 12 Aug 2026 17:43:20 +0000 Subject: [PATCH] port the timezone section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original took whatever was typed and handed it straight to timedatectl. An unknown zone — a typo, a guess at the spelling — fails there, and under `set -e` that takes the whole run down four steps in. Names are now checked against /usr/share/zoneinfo before use, and a bad one just re-asks. It also never showed what the machine was already set to, and defaulted to option 1 (UTC) on Enter, so pressing return on a correctly-configured box silently moved it. Now the current zone is printed, Enter keeps it, and a zone equal to the current one reports nothing to do rather than setting it again. timezone_current reads three sources — timedatectl, /etc/timezone, then the /etc/localtime symlink — because they differ in availability rather than in answer: timedatectl needs systemd, /etc/timezone is Debian's, and the symlink is the one that is always there. timezone_set writes through timedatectl where there is a systemd to talk to and the files directly otherwise, which is what it would have written anyway; that is also the WSL path, where timedatectl exists but does nothing. Europe/Berlin added to the shortlist; TIMEZONE in the environment answers the prompt ahead of time and is validated the same way, failing early with the bad value named. Verified detection (UTC here), validation of four names, and the env-var rejection path. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/setup/machine-setup/lib/system.sh | 46 +++++++++++++ scripts/setup/machine-setup/machine-setup.sh | 68 +++++++++++++++++++- 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/scripts/setup/machine-setup/lib/system.sh b/scripts/setup/machine-setup/lib/system.sh index 0bd40550..5c819f12 100644 --- a/scripts/setup/machine-setup/lib/system.sh +++ b/scripts/setup/machine-setup/lib/system.sh @@ -88,3 +88,49 @@ locale_set() { ;; esac } + +# ----------------------------------------------------------------------------- +# Timezone +# ----------------------------------------------------------------------------- + +# The shortlist offered at the prompt. Any zone name can be typed instead, so +# this is a convenience rather than a limit. +TZ_OPTIONS=(UTC Europe/Lisbon Europe/London Europe/Berlin Europe/Stockholm US/Eastern US/Pacific Asia/Tokyo) + +# What the machine is set to now. +# +# Three sources because they disagree about availability rather than about the +# answer: timedatectl is absent without systemd (containers, WSL), /etc/timezone +# is Debian-specific, and the /etc/localtime symlink is the one thing that is +# always true when any of them are. +timezone_current() { + if command -v timedatectl &>/dev/null && timedatectl show -p Timezone --value 2>/dev/null | grep -q .; then + timedatectl show -p Timezone --value 2>/dev/null + elif [[ -r /etc/timezone ]]; then + tr -d '[:space:]' /dev/null ;; + *) + # timedatectl where there is a systemd to talk to; the files directly + # otherwise, which is the same thing it would have written. + if command -v timedatectl &>/dev/null && [[ "$IS_WSL" != true ]]; then + timedatectl set-timezone "$tz" + else + ln -sf "/usr/share/zoneinfo/${tz}" /etc/localtime + echo "$tz" >/etc/timezone + fi + ;; + esac +} diff --git a/scripts/setup/machine-setup/machine-setup.sh b/scripts/setup/machine-setup/machine-setup.sh index 1aa8b3c9..69709e86 100755 --- a/scripts/setup/machine-setup/machine-setup.sh +++ b/scripts/setup/machine-setup/machine-setup.sh @@ -187,13 +187,79 @@ if ! skip; then step_ok fi +# ============================================================================= +# 6. Timezone +# ============================================================================= +# +# TIMEZONE in the environment answers the prompt ahead of time. + +step "Timezone" +if ! skip; then + CURRENT_TZ="$(timezone_current)" + + echo "" + info "Timezone — what logs, timers and every printed date are relative to" + echo " current: ${CURRENT_TZ:-unknown}" + + if [[ -z "${TIMEZONE:-}" ]]; then + echo "" + for i in "${!TZ_OPTIONS[@]}"; do + printf ' [%d] %s\n' "$((i + 1))" "${TZ_OPTIONS[$i]}" + done + echo "" + + while [[ -z "${TIMEZONE:-}" ]]; do + if ! read -rp " Pick a number, or type a zone name — Enter keeps ${CURRENT_TZ:-the current one}: " TZ_CHOICE; then + echo "" + fail "No answer. Set TIMEZONE= to answer this ahead of time." + fi + + if [[ -z "$TZ_CHOICE" ]]; then + TIMEZONE="$CURRENT_TZ" + elif [[ "$TZ_CHOICE" =~ ^[0-9]+$ ]]; then + if ((TZ_CHOICE >= 1 && TZ_CHOICE <= ${#TZ_OPTIONS[@]})); then + TIMEZONE="${TZ_OPTIONS[$((TZ_CHOICE - 1))]}" + else + warn "There is no option ${TZ_CHOICE}." + fi + else + # Validated here rather than left to timedatectl, which fails on an + # unknown name and would take the whole run down over a typo. + if timezone_is_valid "$TZ_CHOICE"; then + TIMEZONE="$TZ_CHOICE" + else + warn "Not a zone this machine knows: '${TZ_CHOICE}' — try e.g. Europe/Berlin" + fi + fi + done + elif ! timezone_is_valid "$TIMEZONE"; then + fail "TIMEZONE='${TIMEZONE}' is not a zone this machine knows." + fi + + if [[ "$TIMEZONE" == "$CURRENT_TZ" ]]; then + echo " keeping ${CURRENT_TZ}, nothing to do" + SUMMARY+=("Timezone: already ${CURRENT_TZ}") + else + echo " to set: ${TIMEZONE}" + if confirm "Proceed?"; then + timezone_set "$TIMEZONE" + ok "Timezone set to ${TIMEZONE}" + SUMMARY+=("Timezone: ${TIMEZONE}") + else + warn "skipped by request" + SUMMARY+=("Timezone: SKIPPED by request — left at ${CURRENT_TZ:-unknown}") + fi + fi + step_ok +fi + # ============================================================================= # NOT PORTED YET # ============================================================================= # # Sections still to move across from scripts/setup-old/setup-ubuntu.sh, in order: # -# timezone · swap · auto-suspend · boot-hang fix · user creation · +# swap · auto-suspend · boot-hang fix · user creation · # ssh keys · ssh hardening · dns · static ip · fail2ban · unattended-upgrades · # git config · docker · zsh + prompt · tailscale · neovim · js runtimes · # dev tools · ufw · zshrc · disk ballast