{"record":{"id":"8a42042965942bd8","repo":"larksuite/cli","slug":"cannot-parse-q-as-iso-8601","errorCode":null,"errorMessage":"cannot parse %q as ISO 8601","messagePattern":"cannot parse %q as ISO 8601","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/mail/draft/model.go","lineNumber":453,"sourceCode":"\t\tpanic(fmt.Sprintf(\"MustJSON: %v\", err))\n\t}\n\treturn string(data)\n}\n\n// parseISO8601 tries common ISO 8601 timestamp layouts, accepting both\n// with-seconds (RFC 3339) and without-seconds variants.\nfunc parseISO8601(s string) (time.Time, error) {\n\tfor _, layout := range []string{\n\t\ttime.RFC3339,\n\t\t\"2006-01-02T15:04Z07:00\",\n\t\t\"2006-01-02T15:04:05\",\n\t\t\"2006-01-02T15:04\",\n\t} {\n\t\tif t, err := time.Parse(layout, s); err == nil {\n\t\t\treturn t, nil\n\t\t}\n\t}\n\treturn time.Time{}, fmt.Errorf(\"cannot parse %q as ISO 8601\", s)\n}\n","sourceCodeStart":435,"sourceCodeEnd":455,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/mail/draft/model.go#L435-L455","documentation":"This is the sentinel error returned by parseISO8601 when the input string matches none of its four supported layouts: time.RFC3339 (2006-01-02T15:04:05Z07:00), 2006-01-02T15:04Z07:00, 2006-01-02T15:04:05, and 2006-01-02T15:04. It includes the offending string in %q but is usually wrapped (and its detail dropped) by callers like the set_calendar validation, which replace it with a generic 'must be a valid ISO 8601 timestamp' message.","triggerScenarios":"Any call path that funnels a timestamp through parseISO8601 (currently set_calendar event_start/event_end) with input such as date-only strings, epoch numbers as strings, 'YYYY-MM-DD HH:MM:SS' (space instead of T), offsets without colons ('+0800'), or trailing garbage after a valid timestamp.","commonSituations":"RFC 3339 fractional seconds like 2026-09-04T10:00:00.123Z (the parser layouts do not include fractional seconds); '+0800' offsets missing the colon; lowercase 'z' or 't' separators; copying timestamps from databases (e.g. Postgres '2026-09-04 10:00:00+08').","solutions":["Normalize to RFC 3339 with seconds and offset before submitting: t.Format(time.RFC3339)","If your timestamps have fractional seconds, strip them or format with a matching layout (e.g. 2026-09-04T10:00:00Z without milliseconds)","Replace the space separator with 'T' and ensure offsets use the +08:00 colon form (or Z for UTC)","Accept one of the four exact shapes: with/without seconds, with/without timezone — no date-only or epoch forms"],"exampleFix":"// before\nts := \"2026-09-04 10:00:00+0800\" // rejected\n// after\nts := \"2026-09-04T10:00:00+08:00\" // accepted (RFC 3339)","handlingStrategy":"validation","validationCode":"func normalizeISO8601(s string) (string, error) {\n    t, err := time.Parse(time.RFC3339, s)\n    if err != nil {\n        return \"\", fmt.Errorf(\"timestamp %q must be RFC 3339 (e.g. 2026-09-04T10:00:00Z)\", s)\n    }\n    return t.Format(time.RFC3339), nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Normalize timestamps to RFC 3339 at the boundary of your program, once","Avoid fractional seconds, '+0800' colon-less offsets, and space separators — none match the parser's layouts","Store and transmit timestamps as RFC 3339 strings or time.Time, never as display-formatted strings","Add a test that round-trips every timestamp your system emits through time.Parse(time.RFC3339, ...)"],"tags":["validation","iso8601","parsing","timestamp"],"backgroundTag":"invalid-timestamp-format","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}