Skip to content

Commit 43e4eb7

Browse files
authored
fix(core): parse timestamps with any number of fractional-second digits (#9186)
ConvertStringToTime and ConvertStringToTimeInLoc matched timestamps with a `+hh:mm` offset against fixed-width layouts (".000" and ".000000"). Any other precision failed: .NET's round-trip format (7 digits, e.g. PowerShell `Get-Date -Format o`), Go's RFC3339Nano (up to 9 digits, trailing zeros trimmed) and trimmed 4/5-digit values all returned a parse error. This parser backs the mapstructure decode hook, so a webhook deployment or incident posted with such a timestamp was rejected with 400 and never counted, and an Iso8601Time field in an extractor failed the subtask. Replace the two fixed-width entries with one entry that accepts any fraction length through the ".999999999" layout. Generated-by: Claude Code (Claude Opus 5.5) Co-authored-by: breken-ai <312387581+breken-ai@users.noreply.github.com>
1 parent 7e680b3 commit 43e4eb7

2 files changed

Lines changed: 36 additions & 6 deletions

File tree

‎backend/core/models/common/iso8601time.go‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,11 @@ func init() {
5757
Format: "2006-01-02T15:04:05-0700",
5858
},
5959
{
60-
Matcher: regexp.MustCompile(`[\d]{6}[+-][\d]{2}:[\d]{2}$`),
61-
Format: "2006-01-02T15:04:05.000000-07:00",
62-
},
63-
{
64-
Matcher: regexp.MustCompile(`[\d]{3}[+-][\d]{2}:[\d]{2}$`),
65-
Format: "2006-01-02T15:04:05.000-07:00",
60+
// Fractional seconds of any length: Jira/GitLab use 3 digits, Bitbucket
61+
// Cloud 6, .NET's round-trip format 7, and Go's RFC3339Nano up to 9 with
62+
// trailing zeros trimmed. The ".999999999" layout accepts all of them.
63+
Matcher: regexp.MustCompile(`\.[\d]+[+-][\d]{2}:[\d]{2}$`),
64+
Format: "2006-01-02T15:04:05.999999999-07:00",
6665
},
6766
{
6867
Matcher: regexp.MustCompile(`[+-][\d]{2}:[\d]{2}$`),

‎backend/core/models/common/iso8601time_test.go‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,30 @@ func TestConvertStringToTime(t *testing.T) {
157157
output: time.Date(2025, 9, 15, 8, 53, 36, 337932000, time.UTC),
158158
err: nil,
159159
},
160+
{
161+
name: "Valid time string with 7 fractional digits (.NET round-trip format)",
162+
input: "2025-09-15T16:53:36.3379321+08:00",
163+
output: time.Date(2025, 9, 15, 8, 53, 36, 337932100, time.UTC),
164+
err: nil,
165+
},
166+
{
167+
name: "Valid time string with 9 fractional digits (Go RFC3339Nano)",
168+
input: "2025-09-15T17:53:36.337932123+09:00",
169+
output: time.Date(2025, 9, 15, 8, 53, 36, 337932123, time.UTC),
170+
err: nil,
171+
},
172+
{
173+
name: "Valid time string with 4 fractional digits (trailing zeros trimmed)",
174+
input: "2025-09-15T10:53:36.3379+02:00",
175+
output: time.Date(2025, 9, 15, 8, 53, 36, 337900000, time.UTC),
176+
err: nil,
177+
},
178+
{
179+
name: "Valid time string with 1 fractional digit",
180+
input: "2025-09-15T08:53:36.3+00:00",
181+
output: time.Date(2025, 9, 15, 8, 53, 36, 300000000, time.UTC),
182+
err: nil,
183+
},
160184
{
161185
name: "Invalid time string",
162186
input: "invalid",
@@ -311,6 +335,13 @@ func TestConvertStringToTimeInLoc(t *testing.T) {
311335
output: time.Date(2023, 3, 1, 0, 0, 0, 0, loc),
312336
err: nil,
313337
},
338+
{
339+
name: "Valid time string with 7 fractional digits and location",
340+
input: "2023-03-01T12:30:00.1234567+08:00",
341+
loc: loc,
342+
output: time.Date(2023, 3, 1, 12, 30, 0, 123456700, loc),
343+
err: nil,
344+
},
314345
{
315346
name: "Invalid time string with location",
316347
input: "invalid",

0 commit comments

Comments
 (0)