larksuite/cli · error

datetime %q must be ISO: %w

Error message

datetime %q must be ISO: %w

What it means

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.

Source

Thrown at shortcuts/sheets/lark_sheet_table_io.go:791

		// clash with the column-wide dtype declaration; name the three ways
		// out so the caller does not have to guess what "bad format" means.
		return 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
	}
	if i := strings.Index(s, "T"); i > 0 {
		base := s[:i]
		date, err := time.Parse("2006-01-02", base)
		if err != nil {
			return 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
		}
		var parsed time.Time
		clock := s[i:]
		if strings.ContainsAny(clock, "Zz+-") {
			parsed, err = time.Parse(time.RFC3339Nano, s)
		} else {
			parsed, err = time.Parse("2006-01-02T15:04:05.999999999", s)
		}
		if err != nil {
			return 0, fmt.Errorf("datetime %q must be ISO: %w", s, err) //nolint:forbidigo // intermediate parse error; caller wraps it with typed validation context
		}
		if parsed.Location() != time.UTC {
			// Keep the wall-clock time supplied by the caller; Excel serials do
			// not carry a timezone and table-put historically treated the ISO
			// date/time as the sheet-local value.
			return date.Sub(excelEpoch).Hours()/24 + float64(parsed.Hour()*3600+parsed.Minute()*60+parsed.Second())/86400 + float64(parsed.Nanosecond())/(86400*1e9), nil
		}
		return date.Sub(excelEpoch).Hours()/24 + float64(parsed.Hour()*3600+parsed.Minute()*60+parsed.Second())/86400 + float64(parsed.Nanosecond())/(86400*1e9), nil
	}
	t, err := time.Parse("2006-01-02", s)
	if err != nil {
		return 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
	}
	return float64(int(math.Round(t.Sub(excelEpoch).Hours() / 24))), nil
}

// ─── range helpers ────────────────────────────────────────────────────

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. 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"
  2. Drop the timezone entirely and use "2026-01-31T10:00:00" for sheet-local wall-clock time
  3. Fix the clock fields (hour ≤ 23, minute/second ≤ 59) and remove stray spaces

Example fix

// before
{"name":"Due","type":"date","data":["2026-01-31T10:00+0800"]}
// after
{"name":"Due","type":"date","data":["2026-01-31T10:00:00+08:00"]}
Defensive patterns

Strategy: validation

Validate before calling

function assertRFC3339Datetime(v) {
  if (v.includes('T') && !isNaN(Date.parse(v)) === false)
    throw new Error(`'${v}' is not a valid ISO datetime; use e.g. 2026-01-31T10:00:00+08:00`);
  if (/[+-]\d{4}$/.test(v)) throw new Error(`offset '${v.slice(-5)}' must include a colon (+08:00)`);
}

Type guard

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)); }

Try / catch

try {
  await run(['lark','sheet','table-put','--sheets',payload]);
} catch (e) {
  if (/datetime .* must be ISO/.test(e.message)) {
    // normalize the timestamp to RFC3339 (colon in offset, no stray spaces) and retry
  }
  throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: Offsets written as +0800 instead of +08:00; timezone name abbreviations (CET) instead of Z or ±hh:mm; stray spaces inside the timestamp.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/72e66c3dcd96280d. Report an issue: GitHub.