pola-rs/polars · error

out-of-range in duration conversion

Error message

out-of-range in duration conversion

What it means

`duration_ms_to_duration` converts an i64 millisecond value into a chrono `Duration` via `Duration::try_milliseconds`. `try_milliseconds` returns `None` for values outside chrono's representable range, and `.expect("out-of-range in duration conversion")` panics.

Solutions

  1. Call `chrono::Duration::try_milliseconds(v)` directly and handle the `None` case instead of the panicking wrapper.
  2. Pre-check |v| against chrono's Duration range before conversion.
  3. Verify the source unit is milliseconds; re-scale if the data is actually us/ns.

Example fix

// before
let d = duration_ms_to_duration(v); // panics on huge values
// after
let d = chrono::Duration::try_milliseconds(v)
    .ok_or_else(|| anyhow!("duration ms {v} out of range"))?;
Defensive patterns

Strategy: validation

Validate before calling

fn duration_ms_in_range(v: i64) -> bool {
    chrono::Duration::try_milliseconds(v).is_some()
}

Try / catch

match chrono::Duration::try_milliseconds(v) {
    Some(d) => /* use d */,
    None => /* handle out-of-range duration */,
}

Prevention

When it happens

Trigger: Calling `duration_ms_to_duration(v)` with |v| greater than chrono Duration's max (roughly i64::MAX/1000 milliseconds is fine; values near i64::MAX milliseconds exceed the ~±292-year span).

Common situations: Treating nanosecond or microsecond epoch values as milliseconds; sentinel values in duration(ms) columns; aggregation results overflowing the duration range.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/839875018d8878b9. Report an issue: GitHub.

Appendix: source

Thrown at crates/polars-arrow/src/temporal_conversions.rs:85

    date64_to_datetime(milliseconds).date()
}

/// converts a `i32` representing a `time32(s)` to [`NaiveTime`]
#[inline]
pub fn time32s_to_time(v: i32) -> NaiveTime {
    NaiveTime::from_num_seconds_from_midnight_opt(v as u32, 0).expect("invalid time")
}

/// converts a `i64` representing a `duration(s)` to [`Duration`]
#[inline]
pub fn duration_s_to_duration(v: i64) -> Duration {
    Duration::try_seconds(v).expect("out-of-range duration")
}

/// converts a `i64` representing a `duration(ms)` to [`Duration`]
#[inline]
pub fn duration_ms_to_duration(v: i64) -> Duration {
    Duration::try_milliseconds(v).expect("out-of-range in duration conversion")
}

/// converts a `i64` representing a `duration(us)` to [`Duration`]
#[inline]
pub fn duration_us_to_duration(v: i64) -> Duration {
    Duration::microseconds(v)
}

/// converts a `i64` representing a `duration(ns)` to [`Duration`]
#[inline]
pub fn duration_ns_to_duration(v: i64) -> Duration {
    Duration::nanoseconds(v)
}

/// converts a `i32` representing a `time32(ms)` to [`NaiveTime`]
#[inline]
pub fn time32ms_to_time(v: i32) -> NaiveTime {
    let v = v as i64;

View on GitHub (pinned to fe841f959e)