Files
pad/internal/cli/zone.go
xarmian b13159e4e4 feat(cli): per-user time zone in the CLI, pad auth timezone, and the UTC notice (TASK-4101 PR2b) (#2349)
* feat(cli): show and set the viewer time zone (TASK-4101)

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* test(server): retry timezone checks across midnight (TASK-4101)

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* fix(server): overdue and standup days with no time zone set are now UTC, not server-local; set yours in the web settings or with pad auth timezone <IANA> (TASK-4101)

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* fix(cli): refuse an unsaved time zone, never exit from the zone resolver, and pin the standup day test (TASK-4101)

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* test(cli): the unconfigured-resolver test clears every configuring env var and drops an unwired request count (TASK-4101)

PR2b r2 (Codex Low, hunter question).

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* test(cli): set the machine zone before the stub server starts in the TZ4101 tests (TASK-4101)

standup-user-day wrote time.Local after tzCLI had started its httptest
server, whose goroutines read time.Local: a data race under -race.
tzCLI now delegates to tzCLIWithLocal, which takes the zone and sets it
before the server exists.

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* fix(cli): a stored zone this machine cannot load gets a UTC notice that names it (TASK-4101)

The CLI resolves the zone with its own tzdata. A zone the server accepted
but this machine lacks rendered every time in UTC with no notice, since
the notice keyed on an unset zone only. Both doors, the /auth/me lookup
and the dashboard seed, now record the notice to print.

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v
2026-10-30 12:01:02 -04:00

27 lines
773 B
Go

package cli
import "time"
var displayLocationResolver func() *time.Location
// SetDisplayLocationResolver installs the CLI process's lazy viewer-zone lookup.
// A nil resolver restores the UTC default, including for non-CLI consumers.
func SetDisplayLocationResolver(resolve func() *time.Location) {
displayLocationResolver = resolve
}
// DisplayLocation returns the viewer's zone, never the machine's local zone.
func DisplayLocation() *time.Location {
if displayLocationResolver != nil {
if loc := displayLocationResolver(); loc != nil {
return loc
}
}
return time.UTC
}
// FormatInstant renders an instant for the viewer. JSON timestamps bypass it.
func FormatInstant(t time.Time, layout string) string {
return t.In(DisplayLocation()).Format(layout)
}