nautechsystems/nautilus_trader · error
Invalid date
Error message
Invalid date
What it means
In last_weekday_nanos the i32 year cannot be converted to the i16 range Date::new requires, so the try_from fails and the function reports a generic "Invalid date". The library throws it because the year is outside the representable date range (roughly -32768..32767) and a real calendar date cannot be constructed. The message is intentionally generic; check which parameter is out of range.
Source
Thrown at crates/core/src/datetime.rs:497
);
out.push('Z');
out
}
/// Floor the given UNIX nanoseconds to the nearest microsecond.
#[must_use]
pub const fn floor_to_nearest_microsecond(unix_nanos: u64) -> u64 {
(unix_nanos / NANOSECONDS_IN_MICROSECOND) * NANOSECONDS_IN_MICROSECOND
}
/// Calculates the last weekday (Mon-Fri) from the given `year`, `month`, and `day`.
///
/// # Errors
///
/// Returns an error if the date is invalid.
pub fn last_weekday_nanos(year: i32, month: u32, day: u32) -> anyhow::Result<UnixNanos> {
let date = Date::new(
i16::try_from(year).map_err(|_| anyhow::anyhow!("Invalid date"))?,
i8::try_from(month).map_err(|_| anyhow::anyhow!("Invalid date"))?,
i8::try_from(day).map_err(|_| anyhow::anyhow!("Invalid date"))?,
)
.map_err(|_| anyhow::anyhow!("Invalid date"))?;
let current_weekday = date.weekday().to_monday_one_offset();
// Calculate the offset in days for closest weekday (Mon-Fri)
let offset = match current_weekday {
1..=5 => 0, // Monday to Friday, no adjustment needed
6 => 1, // Saturday, adjust to previous Friday
_ => 2, // Sunday, adjust to previous Friday
};
// Calculate last closest weekday
let last_closest = date.checked_sub(Span::new().days(offset))?;
// Convert to UNIX nanoseconds
let unix_timestamp_ns = last_closest
.at(0, 0, 0, 0)View on GitHub (pinned to 18893faf8b)
Solutions
- Clamp or validate the year to the i16-representable calendar range before calling (practically 1..=9999).
- Verify the source of the year value; fix upstream arithmetic or parsing that produced an out-of-range year.
- Catch the error and surface the original year/month/day for diagnostics, since the message does not include them.
Example fix
// before let ns = last_weekday_nanos(expiry_year, month, day)?; // expiry_year = 120000 // after assert!((1..=9999).contains(&expiry_year)); let ns = last_weekday_nanos(expiry_year, month, day)?;
Defensive patterns
Strategy: validation
Validate before calling
// Rust: check year range before calling
fn valid_year(y: i32) -> bool { (1..=9999).contains(&y) } Try / catch
let ns = last_weekday_nanos(year, month, day)
.map_err(|e| e.context(format!("last_weekday for y={year} m={month} d={day}")))?; Prevention
- Validate year/month/day triples at config-load time, not at use time.
- Guard against sentinel values like i32::MAX leaking into date fields.
When it happens
Trigger: Calling last_weekday_nanos(year, month, day) with a year that does not fit in i16, e.g. year <= -32769 or year >= 32768, such as year=99999 from a bad config or 0/uninitialized value.
Common situations: Uninitialized/default (0 is fine, but sentinel values like i32::MAX are not); reading a year column from CSV parsed as i32 with junk; computing expiry years with an off-by-thousands arithmetic bug.
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
- DateTime timestamp out of range for UnixNanos: {nanos}
- Lighter client_order_index {client_order_index} is outside t
- seconds must be finite, was {secs}
- seconds {secs} is out of range for `u64` nanoseconds
- seconds {secs} is out of range for `u64` milliseconds
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/d365abc60d79f338.
Report an issue: GitHub.