This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 43e4eb762 fix(core): parse timestamps with any number of
fractional-second digits (#9186)
43e4eb762 is described below
commit 43e4eb76241cf022349abc5cdce9d0fa013c8a15
Author: breken <[email protected]>
AuthorDate: Sun Oct 4 10:06:01 2026 -0500
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 <[email protected]>
---
backend/core/models/common/iso8601time.go | 11 +++++----
backend/core/models/common/iso8601time_test.go | 31 ++++++++++++++++++++++++++
2 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/backend/core/models/common/iso8601time.go
b/backend/core/models/common/iso8601time.go
index a0570d009..00e032b0f 100644
--- a/backend/core/models/common/iso8601time.go
+++ b/backend/core/models/common/iso8601time.go
@@ -57,12 +57,11 @@ func init() {
Format: "2006-01-02T15:04:05-0700",
},
{
- Matcher:
regexp.MustCompile(`[\d]{6}[+-][\d]{2}:[\d]{2}$`),
- Format: "2006-01-02T15:04:05.000000-07:00",
- },
- {
- Matcher:
regexp.MustCompile(`[\d]{3}[+-][\d]{2}:[\d]{2}$`),
- Format: "2006-01-02T15:04:05.000-07:00",
+ // Fractional seconds of any length: Jira/GitLab use 3
digits, Bitbucket
+ // Cloud 6, .NET's round-trip format 7, and Go's
RFC3339Nano up to 9 with
+ // trailing zeros trimmed. The ".999999999" layout
accepts all of them.
+ Matcher:
regexp.MustCompile(`\.[\d]+[+-][\d]{2}:[\d]{2}$`),
+ Format: "2006-01-02T15:04:05.999999999-07:00",
},
{
Matcher: regexp.MustCompile(`[+-][\d]{2}:[\d]{2}$`),
diff --git a/backend/core/models/common/iso8601time_test.go
b/backend/core/models/common/iso8601time_test.go
index 84a33e893..3d3dcc640 100644
--- a/backend/core/models/common/iso8601time_test.go
+++ b/backend/core/models/common/iso8601time_test.go
@@ -157,6 +157,30 @@ func TestConvertStringToTime(t *testing.T) {
output: time.Date(2025, 9, 15, 8, 53, 36, 337932000,
time.UTC),
err: nil,
},
+ {
+ name: "Valid time string with 7 fractional digits
(.NET round-trip format)",
+ input: "2025-09-15T16:53:36.3379321+08:00",
+ output: time.Date(2025, 9, 15, 8, 53, 36, 337932100,
time.UTC),
+ err: nil,
+ },
+ {
+ name: "Valid time string with 9 fractional digits (Go
RFC3339Nano)",
+ input: "2025-09-15T17:53:36.337932123+09:00",
+ output: time.Date(2025, 9, 15, 8, 53, 36, 337932123,
time.UTC),
+ err: nil,
+ },
+ {
+ name: "Valid time string with 4 fractional digits
(trailing zeros trimmed)",
+ input: "2025-09-15T10:53:36.3379+02:00",
+ output: time.Date(2025, 9, 15, 8, 53, 36, 337900000,
time.UTC),
+ err: nil,
+ },
+ {
+ name: "Valid time string with 1 fractional digit",
+ input: "2025-09-15T08:53:36.3+00:00",
+ output: time.Date(2025, 9, 15, 8, 53, 36, 300000000,
time.UTC),
+ err: nil,
+ },
{
name: "Invalid time string",
input: "invalid",
@@ -311,6 +335,13 @@ func TestConvertStringToTimeInLoc(t *testing.T) {
output: time.Date(2023, 3, 1, 0, 0, 0, 0, loc),
err: nil,
},
+ {
+ name: "Valid time string with 7 fractional digits and
location",
+ input: "2023-03-01T12:30:00.1234567+08:00",
+ loc: loc,
+ output: time.Date(2023, 3, 1, 12, 30, 0, 123456700,
loc),
+ err: nil,
+ },
{
name: "Invalid time string with location",
input: "invalid",