risingwavelabs/risingwave · error · InvalidParamsError

Can't cast string to time (expected format is HH:MM:SS[.D+{u

Error message

Can't cast string to time (expected format is HH:MM:SS[.D+{up to 6 digits}][Z] or HH:MM)

What it means

ErrorKind::ParseTime is thrown by Time::from_str when speedate::Time::parse_str rejects the input. Accepted shapes are HH:MM:SS[.fraction up to 6 digits][Z] or HH:MM; anything else (e.g. 12-hour clock with AM/PM, missing colon) fails.

Source

Thrown at src/common/src/types/datetime.rs:261

        Time::from_num_seconds_from_midnight_uncheck(secs, nano)
    }
}

#[derive(Copy, Clone, Debug, Error)]
enum ErrorKind {
    #[error("Invalid date: days: {days}")]
    Date { days: i32 },
    #[error("Invalid time: secs: {secs}, nanoseconds: {nsecs}")]
    Time { secs: u32, nsecs: u32 },
    #[error("Invalid time: {value} {unit} is out of range for a time of day")]
    TimeOfDay { value: u64, unit: &'static str },
    #[error("Invalid datetime: seconds: {secs}, nanoseconds: {nsecs}")]
    DateTime { secs: i64, nsecs: u32 },
    #[error("Invalid datetime: {value} {unit} is out of range")]
    Timestamp { value: i64, unit: &'static str },
    #[error("Can't cast string to date (expected format is YYYY-MM-DD)")]
    ParseDate,
    #[error(
        "Can't cast string to time (expected format is HH:MM:SS[.D+{{up to 6 digits}}][Z] or HH:MM)"
    )]
    ParseTime,
    #[error(
        "Can't cast string to timestamp (expected format is YYYY-MM-DD HH:MM:SS[.D+{{up to 9 digits}}] or YYYY-MM-DD HH:MM or YYYY-MM-DD or ISO 8601 format)"
    )]
    ParseTimestamp,
}

#[derive(Debug, Error)]
#[error(transparent)]
pub struct InvalidParamsError(#[from] ErrorKind);

impl InvalidParamsError {
    pub fn date(days: i32) -> Self {
        ErrorKind::Date { days }.into()
    }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Normalize the input to 24-hour HH:MM:SS(.ffffff) format before casting.
  2. Strip timezone suffixes other than 'Z' and convert to the desired clock time beforehand.
  3. Increase fraction precision at the source if more than 6 fractional digits are being sent (only 6 are supported).

Example fix

// before
let t = Time::from_str("04:05:06 PM")?;
// after
let t = Time::from_str("16:05:06")?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_parseable_time(s: &str) -> bool {
    let s = s.trim_end_matches('Z');
    regex::Regex::new(r"^\d{2}:\d{2}(:\d{2}(\.\d{1,6})?)?$").unwrap().is_match(s)
}

Type guard

fn looks_like_time(s: &str) -> bool {
    let parts: Vec<&str> = s.trim_end_matches('Z').split(':').collect();
    (2..=3).contains(&parts.len()) && parts.iter().all(|p| !p.is_empty() && p.chars().all(|c| c.is_ascii_digit() || c == '.'))
}

Try / catch

let t = Time::from_str(s)
    .map_err(|e| anyhow!("'{s}' is not HH:MM:SS[.ffffff][Z] or HH:MM: {e}"))?;

Prevention

When it happens

Trigger: Casting strings like '4:05 PM', '040506', or '25:00' to TIME; a trailing 'Z' is stripped and ignored, but full timezone offsets like '+08:00' are not accepted.

Common situations: Ingesting logs that use 12-hour AM/PM times; timezone-suffixed times from ISO sources; data exports with zero-padded-omitted components.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/cb6c99172a220174. Report an issue: GitHub.