can1357/oh-my-pi · error · io::Error
date is out of range
Error message
date is out of range
What it means
`parse_utc_datetime` computes total seconds since the epoch with `days_from_civil(...)` then `checked_mul(86_400)` and `checked_add(...)`; if any step overflows `i64`, the value cannot be represented and the library throws `date is out of range` (`InvalidInput`). This only happens with extreme years (roughly outside ±250 million years), far beyond normal usage, and indicates an arithmetic-overflow cutoff distinct from the `dates before 1970 are unsupported` rejection that follows.
Source
Thrown at crates/pi-builtins/src/fd.rs:1404
}
let mut time_parts = time.split(':');
let hour = parse_u32_part(time_parts.next(), "hour")?;
let minute = parse_u32_part(time_parts.next(), "minute")?;
let second = parse_u32_part(time_parts.next(), "second")?;
if time_parts.next().is_some()
|| !(1..=12).contains(&month)
|| !(1..=31).contains(&day)
|| hour > 23
|| minute > 59
|| second > 59
{
return Err(io::Error::new(io::ErrorKind::InvalidInput, format!("invalid date: {value}")));
}
let days = days_from_civil(year, month, day);
let seconds = days
.checked_mul(86_400)
.and_then(|base| base.checked_add(i64::from(hour * 3_600 + minute * 60 + second)))
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "date is out of range"))?;
if seconds < 0 {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "dates before 1970 are unsupported"));
}
Ok(UNIX_EPOCH + Duration::from_secs(u64::try_from(seconds).unwrap_or(u64::MAX)))
}
fn parse_i32_part(value: Option<&str>, name: &str) -> io::Result<i32> {
value
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!("missing {name}")))?
.parse::<i32>()
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err.to_string()))
}
fn parse_u32_part(value: Option<&str>, name: &str) -> io::Result<u32> {
value
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!("missing {name}")))?
.parse::<u32>()
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err.to_string()))View on GitHub (pinned to 9690622007)
Solutions
- Correct the year to a realistic 4-digit (or at least sane) value; dates must represent real calendar dates ≥ 1970-01-01 UTC.
- Sanitize upstream input to reject absurd year values before building the filter string.
- Switch machine-generated absolute times to the `@<epoch-seconds>` form, which validates via u64 parsing instead of civil-date arithmetic.
Example fix
// before (year overflow) find --changed 999999999999-01-01 // after (sane date, post-1970 UTC) find --changed 2024-01-15 find --changed @1705276800
Defensive patterns
Strategy: validation
Validate before calling
fn validate_year_range(value: &str) -> Result<(), String> {
let date = value.trim().split(' ').next().unwrap_or("");
let year_str = date.split('-').next().unwrap_or("");
let year: i64 = year_str.parse().map_err(|_| format!("bad year '{year_str}'"))?;
if !(-999..=9999).contains(&year) {
return Err(format!("year '{year}' is implausible; expect a 4-digit year"));
}
Ok(())
} Type guard
fn has_plausible_year(value: &str) -> bool {
let date = value.trim().split(' ').next().unwrap_or("");
date.split('-').next()
.and_then(|y| y.parse::<i64>().ok())
.map(|y| (0..=9999).contains(&y))
.unwrap_or(false)
} Try / catch
match parse_time_filter(input) {
Ok(time) => use_time(time),
Err(e) if e.to_string() == "date is out of range" => {
eprintln!("'{input}' overflows epoch arithmetic; check the year field for corrupted or oversized digits");
}
Err(e) => return Err(e),
} Prevention
- Validate the year is a plausible 4-digit value before constructing the filter string.
- Check for unit-confusion bugs (millisecond timestamps used as year fields).
- Bound or sanitize all user/config input feeding datetime strings.
- Use `@<epoch-seconds>` for machine-generated times; u64 parsing fails fast instead of overflowing.
When it happens
Trigger: Passing an absolute datetime with an astronomically large or small year, e.g. `999999999999-01-01` or `-99999999999-01-01`, such that days*86400 overflows i64.
Common situations: Unvalidated user input or config values fed straight into the filter with missing digit-length checks; a bug where a millisecond/microsecond timestamp is used as the year field; template/variable substitution gone wrong producing a huge year.
Related errors
- invalid date: {value}
- err.to_string() (timestamp parse error)
- duration is too large: {value}
- dates before 1970 are unsupported
- missing {name}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f840659f043d2893.
Report an issue: GitHub.