diff --git a/pkg/cmd/list_test.go b/pkg/cmd/list_test.go index f45616250..a8861828a 100644 --- a/pkg/cmd/list_test.go +++ b/pkg/cmd/list_test.go @@ -17,6 +17,7 @@ limitations under the License. package cmd import ( + "strings" "testing" "time" @@ -28,6 +29,34 @@ import ( release "helm.sh/helm/v4/pkg/release/v1" ) +// Regression test for #31902: the UPDATED column must be rendered in the +// local timezone (derived from TZ) for both the --time-format path and the +// default String() path. +func TestListWriterUsesLocalTime(t *testing.T) { + originalLocal := time.Local + time.Local = time.FixedZone("UTC+2", 2*60*60) + defer func() { time.Local = originalLocal }() + + releases := []*release.Release{{ + Name: "tz-release", + Version: 1, + Namespace: "default", + Info: &release.Info{ + LastDeployed: time.Date(2026, 3, 4, 14, 38, 52, 0, time.UTC), + Status: common.StatusDeployed, + }, + Chart: &chart.Chart{Metadata: &chart.Metadata{Name: "test-chart", Version: "1.0.0", AppVersion: "0.0.1"}}, + }} + + writer := newReleaseListWriter(releases, "2006-01-02 15:04:05", false, false) + assert.Equal(t, "2026-03-04 16:38:52", writer.releases[0].Updated) + + writer = newReleaseListWriter(releases, "", false, false) + if got := writer.releases[0].Updated; !strings.Contains(got, "16:38:52") || !strings.Contains(got, "+0200") { + t.Fatalf("expected default-format timestamp in local zone (UTC+2), got %q", got) + } +} + func TestListCmd(t *testing.T) { defaultNamespace := "default" diff --git a/pkg/cmd/status_test.go b/pkg/cmd/status_test.go index b96a0d19a..8c97e30f0 100644 --- a/pkg/cmd/status_test.go +++ b/pkg/cmd/status_test.go @@ -17,6 +17,8 @@ limitations under the License. package cmd import ( + "bytes" + "strings" "testing" "time" @@ -25,6 +27,32 @@ import ( release "helm.sh/helm/v4/pkg/release/v1" ) +// Regression test for #31902: status table timestamps must be rendered in +// the local timezone (derived from TZ), not the zone they were stored with. +func TestStatusTableUsesLocalTime(t *testing.T) { + originalLocal := time.Local + time.Local = time.FixedZone("UTC+2", 2*60*60) + defer func() { time.Local = originalLocal }() + + rel := &release.Release{ + Name: "tz-release", + Namespace: "default", + Info: &release.Info{ + LastDeployed: time.Date(2026, 3, 4, 14, 38, 52, 0, time.UTC), + Status: common.StatusDeployed, + }, + } + + var buf bytes.Buffer + if err := (statusPrinter{release: rel}).WriteTable(&buf); err != nil { + t.Fatal(err) + } + + if !strings.Contains(buf.String(), "LAST DEPLOYED: Wed Mar 4 16:38:52 2026") { + t.Fatalf("expected LAST DEPLOYED rendered in local zone (UTC+2), got:\n%s", buf.String()) + } +} + func TestStatusCmd(t *testing.T) { releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release { info.LastDeployed = time.Unix(1452902400, 0).UTC()