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.
This commit is contained in:
Boris Nagaev 2026-07-02 01:15:12 -05:00
parent a134cb1f22
commit baecbbfe4c
No known key found for this signature in database

View file

@ -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) {