{"record":{"id":"03b644dd35704455","repo":"larksuite/cli","slug":"date-q-must-be-iso-yyyy-mm-dd-w","errorCode":null,"errorMessage":"date %q must be ISO yyyy-mm-dd: %w","messagePattern":"date %q must be ISO yyyy-mm-dd: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/sheets/lark_sheet_table_io.go","lineNumber":781,"sourceCode":"// `T` separator (`2024-01-15T00:00:00.000`, `2024-01-15T08:30:00+08:00`). The\n// `T...` suffix is dropped before parsing so the pandas `df_to_sheet` helper\n// — which uses `df.to_json(orient=\"split\", date_format=\"iso\")` and therefore\n// always emits the full ISO form — round-trips without an extra string clean\n// step on the agent side. A leading `T` (no date prefix) is left alone so the\n// parser still rejects it cleanly.\nfunc isoDateToSerial(s string) (float64, error) {\n\ts = strings.TrimSpace(s)\n\tif s == \"\" {\n\t\t// Empty cells in a date-typed column are the classic header/total-row\n\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","sourceCodeStart":763,"sourceCodeEnd":799,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/sheets/lark_sheet_table_io.go#L763-L799","documentation":"For values containing a 'T' (ISO datetime), isoDateToSerial first parses the date portion with layout 2006-01-02. If that base date is malformed (e.g. \"2026-13-01T...\" or \"01/31/2026T...\"), the underlying time.Parse error is wrapped in this message. Callers add row/column context in the typed validation error.","triggerScenarios":"Date column value like \"31/01/2026T10:00\", \"2026-1-5T09:00:00\", or \"2026-13-01T00:00\" — the part before 'T' is not a valid yyyy-mm-dd date.","commonSituations":"US-style or locale-formatted dates pasted from other tools; single-digit month/day without zero padding; upstream systems emitting local formats.","solutions":["Reformat the value to strict yyyy-mm-dd before the T, e.g. \"2026-01-31T10:00:00\"","Zero-pad month and day (01-05 not 1-5)","If the column holds non-ISO text, change its dtype to \"object\""],"exampleFix":"// before\n{\"name\":\"Due\",\"type\":\"date\",\"data\":[\"31/01/2026T10:00\"]}\n// after\n{\"name\":\"Due\",\"type\":\"date\",\"data\":[\"2026-01-31T10:00:00\"]}","handlingStrategy":"validation","validationCode":"function assertISOBasedate(v) {\n  const i = v.indexOf('T');\n  if (i > 0 && isNaN(Date.parse(v.slice(0, i) + 'T00:00:00Z')))\n    throw new Error(`base date '${v.slice(0, i)}' is not yyyy-mm-dd`);\n}","typeGuard":"function hasValidISOBaseDate(v) { const m = /^(\\d{4}-\\d{2}-\\d{2})T/.exec(v); return !!m && !isNaN(Date.parse(m[1] + 'T00:00:00Z')); }","tryCatchPattern":"try {\n  await run(['lark','sheet','table-put','--sheets',payload]);\n} catch (e) {\n  if (/must be ISO yyyy-mm-dd/.test(e.message)) {\n    // reformat the pre-T part to yyyy-mm-dd and retry\n  }\n  throw e;\n}","preventionTips":["Always zero-pad month/day in dates (2026-01-05, not 2026-1-5)","Convert locale-formatted dates (MM/DD/YYYY, DD.MM.YYYY) to ISO before payload build","Validate with a strict regex ^\\d{4}-\\d{2}-\\d{2} before sending"],"tags":["cli","validation","date","iso8601"],"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"}