{"record":{"id":"11ba92bd6e4b2c22","repo":"kataras/iris","slug":"error-parsing-offset-hours-s-w","errorCode":null,"errorMessage":"error parsing offset hours: %s: %w","messagePattern":"error parsing offset hours: (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"x/jsonx/iso8601.go","lineNumber":237,"sourceCode":"\tparts := strings.Split(offset, \":\")\n\treturn len(parts) == 3\n}\n\nfunc adjustForUnconventionalOffset(t time.Time, offset string) (time.Time, error) {\n\tsign := 1\n\tif offset[0] == '-' {\n\t\tsign = -1\n\t}\n\toffset = offset[1:]\n\n\toffsetParts := strings.Split(offset, \":\")\n\tif len(offsetParts) != 3 {\n\t\treturn time.Time{}, fmt.Errorf(\"invalid offset format: %s\", offset)\n\t}\n\n\thours, err := strconv.Atoi(offsetParts[0])\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"error parsing offset hours: %s: %w\", offset, err)\n\t}\n\n\tif hours > 24 {\n\t\treturn time.Time{}, fmt.Errorf(\"invalid offset hours: %d: %s\", hours, offset)\n\t}\n\n\tminutes, err := strconv.Atoi(offsetParts[1])\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"error parsing offset minutes: %s: %w\", offset, err)\n\t}\n\tif minutes > 60 {\n\t\treturn time.Time{}, fmt.Errorf(\"invalid offset minutes: %d: %s\", minutes, offset)\n\t}\n\n\tseconds, err := strconv.Atoi(offsetParts[2])\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"error parsing offset seconds: %s: %w\", offset, err)\n\t}","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/kataras/iris/blob/7bedaf55a0b64bbb2248a5845a2c60d81a30996a/x/jsonx/iso8601.go#L219-L255","documentation":"When parsing an unconventional '+HH:MM:SS' offset, adjustForUnconventionalOffset converts the hours field with strconv.Atoi. This error wraps the strconv failure, meaning the hours portion of the offset was not a plain integer. The offset text is included and the underlying strconv error is wrapped with %w.","triggerScenarios":"ParseISO8601 on a timestamp whose offset hours field contains non-digits, e.g. '+0x:30:00', '+ 3:00:00', or a truncated/garbled offset.","commonSituations":"Corrupted or hand-edited timestamp strings; offsets copied with stray characters or unicode minus signs; data imported from spreadsheets/CSVs where '+' was stripped or replaced.","solutions":["Fix the offset string so hours are plain ASCII digits (e.g. '+03:30:00').","Check for invisible characters (BOM, unicode minus '−' vs ASCII '-') in the timestamp and strip them.","Regenerate the timestamp at the source using time.Format(time.RFC3339) instead of manual string building."],"exampleFix":"// before\nParseISO8601(\"2024-01-02T15:04:05+0a:30:00\") // error parsing offset hours\n// after\nParseISO8601(\"2024-01-02T15:04:05+03:30:00\")","handlingStrategy":"validation","validationCode":"func offsetHoursNumeric(ts string) bool {\n\ti := strings.LastIndexAny(ts, \"+-\")\n\tif i < 0 { return false }\n\tparts := strings.Split(ts[i+1:], \":\")\n\tif len(parts) != 3 { return false }\n\t_, err := strconv.Atoi(parts[0])\n\treturn err == nil\n}","typeGuard":null,"tryCatchPattern":"tt, err := jsonx.ParseISO8601(s)\nif err != nil {\n\tif strings.Contains(err.Error(), \"error parsing offset hours\") {\n\t\treturn fmt.Errorf(\"malformed timestamp %q: %w\", s, err)\n\t}\n\treturn err\n}","preventionTips":["Reject non-ASCII characters (unicode minus, BOM) in timestamp strings at the boundary.","Never hand-build timestamp strings; use time.Format.","Sanitize inputs copied from CSVs or spreadsheets."],"tags":["time-parsing","iso8601","timezone-offset","strconv"],"backgroundTag":"invalid-timezone-offset-format","analyzedSha":"7bedaf55a0b64bbb2248a5845a2c60d81a30996a","analyzedAt":"2026-08-30T20:38:16.250Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}