{"record":{"id":"72e66c3dcd96280d","repo":"larksuite/cli","slug":"datetime-q-must-be-iso-w","errorCode":null,"errorMessage":"datetime %q must be ISO: %w","messagePattern":"datetime %q must be ISO: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/sheets/lark_sheet_table_io.go","lineNumber":791,"sourceCode":"\t\t// clash with the column-wide dtype declaration; name the three ways\n\t\t// out so the caller does not have to guess what \"bad format\" means.\n\t\treturn 0, fmt.Errorf(\"date column has an empty cell — drop the empty rows, fill real yyyy-mm-dd dates, or declare the column dtype as object (text)\") //nolint:forbidigo // intermediate error; callers wrap it into a typed --sheets/--values validation error with row/column context\n\t}\n\tif i := strings.Index(s, \"T\"); i > 0 {\n\t\tbase := s[:i]\n\t\tdate, err := time.Parse(\"2006-01-02\", base)\n\t\tif err != nil {\n\t\t\treturn 0, fmt.Errorf(\"date %q must be ISO yyyy-mm-dd: %w\", base, err) //nolint:forbidigo // intermediate parse error; caller wraps it with typed validation context\n\t\t}\n\t\tvar parsed time.Time\n\t\tclock := s[i:]\n\t\tif strings.ContainsAny(clock, \"Zz+-\") {\n\t\t\tparsed, err = time.Parse(time.RFC3339Nano, s)\n\t\t} else {\n\t\t\tparsed, err = time.Parse(\"2006-01-02T15:04:05.999999999\", s)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn 0, fmt.Errorf(\"datetime %q must be ISO: %w\", s, err) //nolint:forbidigo // intermediate parse error; caller wraps it with typed validation context\n\t\t}\n\t\tif parsed.Location() != time.UTC {\n\t\t\t// Keep the wall-clock time supplied by the caller; Excel serials do\n\t\t\t// not carry a timezone and table-put historically treated the ISO\n\t\t\t// date/time as the sheet-local value.\n\t\t\treturn date.Sub(excelEpoch).Hours()/24 + float64(parsed.Hour()*3600+parsed.Minute()*60+parsed.Second())/86400 + float64(parsed.Nanosecond())/(86400*1e9), nil\n\t\t}\n\t\treturn date.Sub(excelEpoch).Hours()/24 + float64(parsed.Hour()*3600+parsed.Minute()*60+parsed.Second())/86400 + float64(parsed.Nanosecond())/(86400*1e9), nil\n\t}\n\tt, err := time.Parse(\"2006-01-02\", s)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"date %q must be ISO yyyy-mm-dd: %w\", s, err) //nolint:forbidigo // intermediate error; callers wrap it into a typed --sheets/--values validation error with row/column context\n\t}\n\treturn float64(int(math.Round(t.Sub(excelEpoch).Hours() / 24))), nil\n}\n\n// ─── range helpers ────────────────────────────────────────────────────\n","sourceCodeStart":773,"sourceCodeEnd":809,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/sheets/lark_sheet_table_io.go#L773-L809","documentation":"When the value contains a 'T', isoDateToSerial parses the full datetime with RFC3339Nano (if a timezone marker Z/z/+/- is present) or the local-style layout 2006-01-02T15:04:05.999999999. If that full parse fails, this error wraps the cause; the base date already parsed OK, so the clock portion is malformed. Callers add row/column context.","triggerScenarios":"Values like \"2026-01-31T25:00:00\" (invalid hour), \"2026-01-31T10:00 Z\" (space before offset), \"2026-01-31T10:00+0800\" (offset without colon, not RFC3339).","commonSituations":"Offsets written as +0800 instead of +08:00; timezone name abbreviations (CET) instead of Z or ±hh:mm; stray spaces inside the timestamp.","solutions":["Use a valid RFC3339 timestamp with colon in the offset, e.g. \"2026-01-31T10:00:00+08:00\" or \"2026-01-31T10:00:00Z\"","Drop the timezone entirely and use \"2026-01-31T10:00:00\" for sheet-local wall-clock time","Fix the clock fields (hour ≤ 23, minute/second ≤ 59) and remove stray spaces"],"exampleFix":"// before\n{\"name\":\"Due\",\"type\":\"date\",\"data\":[\"2026-01-31T10:00+0800\"]}\n// after\n{\"name\":\"Due\",\"type\":\"date\",\"data\":[\"2026-01-31T10:00:00+08:00\"]}","handlingStrategy":"validation","validationCode":"function assertRFC3339Datetime(v) {\n  if (v.includes('T') && !isNaN(Date.parse(v)) === false)\n    throw new Error(`'${v}' is not a valid ISO datetime; use e.g. 2026-01-31T10:00:00+08:00`);\n  if (/[+-]\\d{4}$/.test(v)) throw new Error(`offset '${v.slice(-5)}' must include a colon (+08:00)`);\n}\n","typeGuard":"function isValidISODatetime(v) { return /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2})?$/.test(v) && !isNaN(Date.parse(v)); }","tryCatchPattern":"try {\n  await run(['lark','sheet','table-put','--sheets',payload]);\n} catch (e) {\n  if (/datetime .* must be ISO/.test(e.message)) {\n    // normalize the timestamp to RFC3339 (colon in offset, no stray spaces) and retry\n  }\n  throw e;\n}","preventionTips":["Use RFC3339 formatting from your language's standard library (e.g. new Date().toISOString()) instead of hand-formatting","Never hand-write timezone offsets; emit Z or ±hh:mm with a colon","Validate timestamps with Date.parse / strict regex before payload build"],"tags":["cli","validation","date","iso8601","timezone"],"backgroundTag":"invalid-date-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"}