spacedriveapp/spacedrive · error · anyhow::Error

Invalid time format: {}

Error message

Invalid time format: {}

What it means

Thrown by parse_time_filter() (used for --since on 'sd sync metrics') when the string is not a relative '<N> <unit> ago' duration and also fails both absolute formats tried: RFC 3339 (DateTime::parse_from_rfc3339) and '%Y-%m-%d %H:%M:%S'. Note that a string ending in ' ago' is routed to parse_duration() instead and produces that function's errors, not this one.

Source

Thrown at apps/cli/src/domains/sync/mod.rs:149

	}
}

fn parse_time_filter(time_str: &str) -> Result<DateTime<Utc>> {
	// Try parsing as relative time first
	if time_str.ends_with(" ago") {
		let duration_str = &time_str[..time_str.len() - 4];
		let duration = parse_duration(duration_str)?;
		Ok(Utc::now() - duration)
	} else {
		// Try parsing as absolute time
		DateTime::parse_from_rfc3339(time_str)
			.map(|dt| dt.with_timezone(&Utc))
			.or_else(|_| {
				// Try common formats
				DateTime::parse_from_str(time_str, "%Y-%m-%d %H:%M:%S")
					.map(|dt| dt.with_timezone(&Utc))
			})
			.map_err(|_| anyhow::anyhow!("Invalid time format: {}", time_str))
	}
}

fn parse_duration(duration_str: &str) -> Result<chrono::Duration> {
	let parts: Vec<&str> = duration_str.split_whitespace().collect();
	if parts.len() != 2 {
		return Err(anyhow::anyhow!("Invalid duration format: {}", duration_str));
	}

	let value: i64 = parts[0]
		.parse()
		.map_err(|_| anyhow::anyhow!("Invalid number: {}", parts[0]))?;
	let unit = parts[1].to_lowercase();

	let seconds = match unit.as_str() {
		"second" | "seconds" | "sec" | "s" => value,
		"minute" | "minutes" | "min" | "m" => value * 60,
		"hour" | "hours" | "h" => value * 3600,

View on GitHub (pinned to 6dfeccf211)

Solutions

  1. Use RFC 3339: --since 2024-06-01T12:00:00Z
  2. Or 'YYYY-MM-DD HH:MM:SS': --since '2024-06-01 12:00:00' (quote it)
  3. Or a relative duration: --since '30 minutes ago', '2 hours ago', '7 days ago'
  4. Double-check for stray characters, missing seconds, or a date-only string

Example fix

// accepted inputs
//   RFC 3339:              2024-06-01T12:00:00Z
//   common format:        "2024-06-01 12:00:00"
//   relative:             "30 minutes ago"
// before (fails): sd sync metrics --since 2024-06-01
// after:           sd sync metrics --since '2024-06-01 00:00:00'
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_time_filter(s: &str) -> bool {
    if s.ends_with(" ago") {
        let d = &s[..s.len() - 4];
        let mut it = d.split_whitespace();
        matches!((it.next(), it.next(), it.next()), (Some(n), Some(_), None))
            && n.parse::<i64>().is_ok()
    } else {
        chrono::DateTime::parse_from_rfc3339(s).is_ok()
            || chrono::DateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S").is_ok()
    }
}

if !is_valid_time_filter(&since_str) {
    eprintln!("--since accepts RFC 3339, 'YYYY-MM-DD HH:MM:SS', or '<N> <unit> ago'");
}

Type guard

fn is_valid_time_filter(s: &str) -> bool {
    if s.ends_with(" ago") {
        let d = &s[..s.len() - 4];
        let mut it = d.split_whitespace();
        matches!((it.next(), it.next(), it.next()), (Some(n), Some(_), None))
            && n.parse::<i64>().is_ok()
    } else {
        chrono::DateTime::parse_from_rfc3339(s).is_ok()
            || chrono::DateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S").is_ok()
    }
}

Try / catch

match parse_time_filter(&since_str) {
    Ok(t) => t,
    Err(e) if e.to_string().starts_with("Invalid time format") => {
        eprintln!("Supported: RFC 3339, 'YYYY-MM-DD HH:MM:SS', or e.g. '30 minutes ago'");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing --since 2024-01-01 (date only, no time), --since '01/02/2024', --since 'yesterday', or any timestamp with a format outside the two accepted ones.

Common situations: Users assume natural-language dates or locale formats (MM/DD/YYYY) work; ISO date without time component; timezone-suffix variants like '+02:00' are fine in RFC 3339 but a missing 'T' separator is not.

Related errors


AI-assisted analysis of spacedriveapp/spacedrive@6dfeccf211 (2026-08-16). Data as JSON: /api/errors/834bb3f0c12e1b34. Report an issue: GitHub.