Skip to content

fix(core): name the box's time zone instead of "Local" - #1585

Merged
frahlg merged 2 commits into
masterfrom
fix/box-iana-timezone
Oct 12, 2026
Merged

frahlg merged 2 commits into
masterfrom
fix/box-iana-timezone

Conversation

@frahlg

@frahlg frahlg commented Oct 12, 2026

Copy link
Copy Markdown
Member

Problem

Core reports the box's time zone with time.Now().Location().String(), loc.String() or time.Local.String(). When TZ is unset, Go names the host zone Local, not an IANA name. Clients that pass Local to time.LoadLocation or Intl get nothing useful.

This hits the tz field in the app protocol hello. The iOS app and the web client use it to show plan times in the box's zone, so on a box without TZ they cannot. It also hits tz in /api/energy/daily and /api/savings/daily, and timezone in the load research export.

Change

  • New package go/internal/localzone. localzone.Name() returns the IANA name of the box's zone, or UTC. It is the resolver forecastTimezone() already used (TZ, the /etc/localtime link, /etc/timezone), moved so cmd/ftw and api share it. No second resolver.
  • The name is read once per process. time.Local is fixed at start, so a zone changed on the host later must not rename the one the box still runs on.
  • The app hello, both daily endpoints and the research export now use localzone.Name(). The forecast and planner callers of forecastTimezone() call it too, with the same result as before, so forecast learning revisions do not change.

Independent of #1582. That PR keeps the EV ready time on the household clock; this one lets clients learn which clock that is.

Evidence

  • TestTheHelloNamesTheBoxZoneWhenTZIsUnset runs a real hello through appproto in a child process with no TZ in its environment. It first checks that time.Local is Go's unnamed Local, then that hello_ok.box.tz is a name time.LoadLocation accepts. With the old line in newAppBoxInfo it fails with hello tz = "Local".
  • localzone tests cover a TZ value, an unknown TZ, the /etc/localtime link, /etc/timezone and a host that names no zone (UTC), using fake host files.
  • make verify passes locally (macOS).

Not in this PR

Clients may still hold a cached Local from an older box. Treating Local as "zone unknown" is a client change, if wanted.

🤖 Generated with Claude Code

Go names the host zone "Local" when TZ is unset. Core sent that name in
the app hello, /api/energy/daily, /api/savings/daily and the load
research export, and clients cannot load it.

Move the resolver forecastTimezone() already used into
internal/localzone so cmd/ftw and api share it, and read it once per
process to match time.Local. A child-process test runs a real hello
with no TZ and checks hello_ok.box.tz loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T15:14:49.537977Z 3f1217a PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f1217ae2d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +29 to +32
if target, err := filepath.EvalSymlinks(localtime); err == nil {
if _, zone, ok := strings.Cut(target, "/zoneinfo/"); ok {
candidates = append(candidates, zone)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve zones stored in a regular /etc/localtime file

When a host configures its zone by copying a tzfile to /etc/localtime rather than symlinking it, and has no /etc/timezone or TZ setting, Go still uses the copied rules through time.Local, but this resolver cannot extract a name and falls back to UTC. The daily endpoints then calculate buckets in the actual local zone while labeling them UTC, and the app renders plan times in UTC. Compare the regular file against the installed zoneinfo tree (or otherwise avoid returning a confidently incorrect fallback) before reporting UTC.

Useful? React with 👍 / 👎.

@frahlg
frahlg merged commit 51bf2d6 into master Oct 12, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant