From baecbbfe4c99b234baf11f53cf75f962015b2ac1 Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Thu, 2 Jul 2026 01:15:12 -0500 Subject: [PATCH] cmd/loop: normalize numeric replay time zones Recorded session replay already normalizes timestamps before comparing CLI output, but the time.String timestamp regex only matched alphabetic zone names like UTC and EST. Some systems render the same offset with a numeric zone name, for example -0300 -03, which left the publication deadline unnormalized and caused fixture comparisons to fail. Match numeric zone names as well, and parse the timestamp using the explicit numeric offset instead of the display zone name. Add a regression test covering RFC3339, alphabetic time.String zones, and numeric time.String zones. --- cmd/loop/session_replay_test.go | 46 +++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/cmd/loop/session_replay_test.go b/cmd/loop/session_replay_test.go index 34fe55ed..43d548ab 100644 --- a/cmd/loop/session_replay_test.go +++ b/cmd/loop/session_replay_test.go @@ -1193,7 +1193,7 @@ var rfc3339TimestampRegex = regexp.MustCompile( // timeStringTimestampRegex matches time.String-style timestamps embedded in // CLI output. var timeStringTimestampRegex = regexp.MustCompile( - `\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [+-]\d{4} [A-Z]{2,5}`, + `\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [+-]\d{4} (?:[A-Z]{2,5}|[+-]\d{2}(?:\d{2})?)`, ) // normalizeTimestamps rewrites embedded timestamps to UTC to avoid @@ -1214,7 +1214,14 @@ func normalizeTimestamps(text string) string { // Normalize time.String timestamps next. timeReplacer := func(ts string) string { - parsed, err := time.Parse("2006-01-02 15:04:05 -0700 MST", ts) + zoneNameIndex := strings.LastIndex(ts, " ") + if zoneNameIndex <= 0 { + return ts + } + + parsed, err := time.Parse( + "2006-01-02 15:04:05 -0700", ts[:zoneNameIndex], + ) if err != nil { return ts } @@ -1229,6 +1236,41 @@ func normalizeTimestamps(text string) string { return text } +// TestNormalizeTimestamps verifies that timestamp normalization handles the +// timestamp formats emitted by CLI commands in different local time zones. +func TestNormalizeTimestamps(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + text string + want string + }{ + { + name: "rfc3339", + text: "updated: 2026-01-26T01:28:06-05:00\n", + want: "updated: 2026-01-26T06:28:06Z\n", + }, + { + name: "alphabetic time string zone", + text: "deadline: 2026-01-26 01:28:06 -0500 EST\n", + want: "deadline: 2026-01-26 06:28:06 +0000 UTC\n", + }, + { + name: "numeric time string zone", + text: "deadline: 2026-01-26 03:28:06 -0300 -03\n", + want: "deadline: 2026-01-26 06:28:06 +0000 UTC\n", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + got := normalizeTimestamps(testCase.text) + require.Equal(t, testCase.want, got) + }) + } +} + // TestCloneCommandForReplayResetsFlagState verifies cloned commands reset flag // state. func TestCloneCommandForReplayResetsFlagState(t *testing.T) {