nautechsystems/nautilus_trader · error

days overflow i64 hours

Error message

days overflow i64 hours

What it means

try_duration_days converts a day count into a SignedDuration by first computing days × 24 hours in checked i64 arithmetic. If days is so large that days × 24 exceeds i64 range, this error is thrown rather than silently wrapping. The public duration_days panics on it, so it typically surfaces as a panic in unwrapping paths.

Source

Thrown at crates/model/src/data/bar.rs:362

    let time_difference = base_time.duration_until(now);
    let period_ns = period.as_nanos();
    debug_assert_ne!(period_ns, 0, "bar period must be non-zero");

    // Use div_euclid for floor division (rounds toward -inf, not zero)
    // so negative deltas (now before origin) yield the previous period boundary
    let num_periods = time_difference.as_nanos().div_euclid(period_ns);

    base_time + SignedDuration::from_nanos_i128(num_periods * period_ns)
}

fn duration_days(days: i64) -> SignedDuration {
    try_duration_days(days).unwrap_or_else(|e| panic!("{e}"))
}

fn try_duration_days(days: i64) -> anyhow::Result<SignedDuration> {
    let hours = days
        .checked_mul(24)
        .ok_or_else(|| anyhow::anyhow!("days overflow i64 hours"))?;
    SignedDuration::try_from_hours(hours)
        .ok_or_else(|| anyhow::anyhow!("days exceed signed duration range"))
}

fn try_time_interval(step: usize, aggregation: BarAggregation) -> anyhow::Result<SignedDuration> {
    let step_i64 = i64::try_from(step)
        .map_err(|_| invalid_interval_step(step, aggregation, "step exceeds i64 range"))?;

    let duration = match aggregation {
        BarAggregation::Millisecond => SignedDuration::from_millis(step_i64),
        BarAggregation::Second => SignedDuration::from_secs(step_i64),
        BarAggregation::Minute => SignedDuration::try_from_mins(step_i64).ok_or_else(|| {
            invalid_interval_step(step, aggregation, "step exceeds signed duration range")
        })?,
        BarAggregation::Hour => SignedDuration::try_from_hours(step_i64).ok_or_else(|| {
            invalid_interval_step(step, aggregation, "step exceeds signed duration range")
        })?,
        BarAggregation::Day => try_scaled_days(step, aggregation, step_i64, 1)?,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Bound the days value to a realistic maximum before conversion
  2. Use try_duration_days and handle the error instead of duration_days' unwrap/panic
  3. Validate user/config-supplied intervals up front

Example fix

// before
let dur = duration_days(days); // panics on overflow
// after
let dur = try_duration_days(days).unwrap_or_else(|_| SignedDuration::from_hours(i64::MAX)); // or handle Err
Defensive patterns

Strategy: try-catch

Validate before calling

if days.abs() > i64::MAX / 24 { bail!("days too large"); }

Try / catch

match try_duration_days(days) {
    Ok(d) => d,
    Err(e) => { log::warn!("{e}"); SignedDuration::ZERO }
}

Prevention

When it happens

Trigger: Calling duration_days/try_scaled_days with an i64 day value greater than i64::MAX / 24 (≈ 385 quadrillion days) — effectively only with absurd or corrupted values.

Common situations: Corrupted configuration of bar aggregation intervals; unit tests probing overflow behavior; a usize step converted from user input that was never bounded.

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 nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/f10d5091dec1e820. Report an issue: GitHub.