{"record":{"id":"194d6ce1dea4c5e8","repo":"kataras/iris","slug":"iso8601-w","errorCode":null,"errorMessage":"ISO8601: %w","messagePattern":"ISO8601: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"x/jsonx/iso8601.go","lineNumber":107,"sourceCode":"\t)\n\n\t/*\n\t\t// Check if the string contains a timezone offset after the 'T' character.\n\t\thasOffset := strings.Contains(s, \"Z\") || (strings.Index(s, \"+\") > strings.Index(s, \"T\")) || (strings.Index(s, \"-\") > strings.Index(s, \"T\"))\n\n\t\tswitch {\n\t\tcase strings.HasSuffix(s, \"Z\"):\n\t\t\ttt, err = time.Parse(ISO8601LayoutWithTimezone, s)\n\t\tcase hasOffset && strings.Contains(s, \".\"):\n\t\t\ttt, err = time.Parse(ISO8601ZUTCOffsetLayoutWithMicroseconds, s)\n\t\tcase hasOffset:\n\t\t\ttt, err = parseWithOffset(s)\n\t\tdefault:\n\t\t\ttt, err = time.Parse(ISO8601Layout, s)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn ISO8601{}, fmt.Errorf(\"ISO8601: %w\", err)\n\t\t}\n\n\t\treturn ISO8601(tt), nil\n\t*/\n\n\tif idx := strings.LastIndexFunc(s, startUTCOffsetIndexFunc); idx > 18 { // should have some distance, with and without milliseconds\n\t\tlength := parseSignedOffset(s[idx:])\n\n\t\t// Check if the offset is unconventional, e.g., -04:01:19\n\t\tif offset := s[idx:]; isUnconventionalOffset(offset) {\n\t\t\tmainPart := s[:idx]\n\t\t\ttt, err = time.Parse(\"2006-01-02T15:04:05.000000\", mainPart)\n\t\t\tif err != nil {\n\t\t\t\treturn ISO8601{}, fmt.Errorf(\"ISO8601: %w\", err)\n\t\t\t}\n\n\t\t\tadjustedTime, parseErr := adjustForUnconventionalOffset(tt, offset)\n\t\t\tif parseErr != nil {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/kataras/iris/blob/7bedaf55a0b64bbb2248a5845a2c60d81a30996a/x/jsonx/iso8601.go#L89-L125","documentation":"ParseISO8601 parses strings in several ISO-8601 layouts (with/without milliseconds, Z suffix, or +hh:mm offsets, including unconventional hh:mm:ss offsets). When time.Parse (or offset adjustment) rejects the input, the underlying error is wrapped as \"ISO8601: %w\". It means the string is not in any of the supported ISO-8601 shapes.","triggerScenarios":"Calling jsonx.ParseISO8601, or json.Unmarshal into a jsonx.ISO8601 field (UnmarshalJSON), or ISO8601.Scan on a string DB value, with a string whose format deviates: wrong layout like \"02/01/2006 15:04\", date-only \"2024-01-02\", out-of-range values (month 13), offsets whose parse fails (e.g. malformed +3:00 without zero padding, or a value like +030000), or an offset substring whose hour part is non-numeric.","commonSituations":"A third-party API changed its date format or sends date-only strings; frontend sends a locale-formatted date instead of toISOString(); a column migrated from timestamp to text now delivers values like \"2024-01-02 10:00:00\" (space instead of T); timezone offsets like \"+03\" (no minutes) that the layouts don't cover.","solutions":["Inspect the wrapped %w error (e.g. parsing time ... cannot parse) to see which part of the string failed and fix the producer to emit one of the supported layouts, e.g. time.Time.UTC().Format(time.RFC3339).","Pre-parse with a custom time.Parse layout that matches the actual input, then convert to jsonx.ISO8601(tt).","Normalize common variants before parsing (replace space with 'T', append \":00\" to short offsets, or handle date-only strings explicitly).","For UnmarshalJSON failures, use a *string or json.RawMessage field and decode manually with fallback layouts."],"exampleFix":"// before: value \"2024-01-02 10:30:00\" (space instead of T)\nvar t jsonx.ISO8601\njson.Unmarshal(b, &t) // ISO8601: parsing time ... cannot parse\n\n// after\nvar raw string\njson.Unmarshal(b, &raw)\nnormalized := strings.Replace(raw, \" \", \"T\", 1)\ntt, err := jsonx.ParseISO8601(normalized)","handlingStrategy":"validation","validationCode":"func parseISO8601Safe(s string) (jsonx.ISO8601, error) {\n    s = strings.TrimSpace(s)\n    s = strings.Replace(s, \" \", \"T\", 1)\n    if s == \"\" || s == \"null\" {\n        return jsonx.ISO8601{}, nil\n    }\n    if re := regexp.MustCompile(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})?$`); !re.MatchString(s) {\n        return jsonx.ISO8601{}, fmt.Errorf(\"unsupported timestamp format: %q\", s)\n    }\n    return jsonx.ParseISO8601(s)\n}","typeGuard":"var isoLayout = regexp.MustCompile(`^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2}(:\\d{2})?)?$`)\n\nfunc looksLikeISO8601(s string) bool {\n    return isoLayout.MatchString(strings.TrimSpace(s))\n}","tryCatchPattern":"if err := json.Unmarshal(b, &ts); err != nil {\n    var perr *time.ParseError\n    if errors.As(err, &perr) && strings.HasPrefix(err.Error(), \"ISO8601:\") {\n        // log raw value and fall back to a lenient custom-layout parser\n    }\n}","preventionTips":["Producers should emit RFC3339/ISO-8601 (e.g. JS toISOString(), Go time.RFC3339) — the only shapes ParseISO8601 accepts.","Validate timestamps with a regex before unmarshalling into jsonx.ISO8601 fields.","Normalize separators (space→T) and short offsets (+03→+03:00) before parsing third-party data.","Date-only strings (\"2024-01-02\") are not supported — handle them with a dedicated date type (e.g. jsonx.SimpleDate).","Check the wrapped *time.ParseError to identify exactly which layout component mismatched."],"tags":["go","time-parsing","iso8601","jsonx","validation"],"backgroundTag":"invalid-date-format","analyzedSha":"7bedaf55a0b64bbb2248a5845a2c60d81a30996a","analyzedAt":"2026-08-30T20:38:16.250Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}