Skip to content

Commit

Permalink
Fix panic in DateTime::checked_add_days
Browse files Browse the repository at this point in the history
Incidentally, add TimeDelta::try_* builders so that it's possible to
build them without risking a panic.
  • Loading branch information
Ekleog committed Jan 22, 2023
1 parent 378efb1 commit 9262c43
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 11 deletions.
6 changes: 5 additions & 1 deletion src/naive/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,10 @@ impl NaiveDate {
/// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(2)),
/// Some(NaiveDate::from_ymd_opt(2022, 8, 2).unwrap())
/// );
/// assert_eq!(
/// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(1000000000000)),
/// None
/// );
/// ```
pub fn checked_add_days(self, days: Days) -> Option<Self> {
if days.0 == 0 {
Expand Down Expand Up @@ -673,7 +677,7 @@ impl NaiveDate {
}

fn diff_days(self, days: i64) -> Option<Self> {
self.checked_add_signed(TimeDelta::days(days))
self.checked_add_signed(TimeDelta::try_days(days)?)
}

/// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`.
Expand Down
56 changes: 46 additions & 10 deletions src/time_delta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,47 +75,83 @@ impl TimeDelta {
/// Panics when the duration is out of bounds.
#[inline]
pub fn weeks(weeks: i64) -> TimeDelta {
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
TimeDelta::seconds(secs)
TimeDelta::try_weeks(weeks).expect("Duration::weeks out of bounds")
}

/// Makes a new `Duration` with given number of weeks.
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
/// Returns None when the duration is out of bounds.
#[inline]
pub fn try_weeks(weeks: i64) -> Option<TimeDelta> {
weeks.checked_mul(SECS_PER_WEEK).and_then(TimeDelta::try_seconds)
}

/// Makes a new `Duration` with given number of days.
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn days(days: i64) -> TimeDelta {
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
TimeDelta::seconds(secs)
TimeDelta::try_days(days).expect("Duration::days out of bounds")
}

/// Makes a new `Duration` with given number of days.
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Returns None when the duration is out of bounds.
#[inline]
pub fn try_days(days: i64) -> Option<TimeDelta> {
days.checked_mul(SECS_PER_DAY).and_then(TimeDelta::try_seconds)
}

/// Makes a new `Duration` with given number of hours.
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn hours(hours: i64) -> TimeDelta {
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
TimeDelta::seconds(secs)
TimeDelta::try_hours(hours).expect("Duration::hours ouf of bounds")
}

/// Makes a new `Duration` with given number of hours.
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Returns None when the duration is out of bounds.
#[inline]
pub fn try_hours(hours: i64) -> Option<TimeDelta> {
hours.checked_mul(SECS_PER_HOUR).and_then(TimeDelta::try_seconds)
}

/// Makes a new `Duration` with given number of minutes.
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn minutes(minutes: i64) -> TimeDelta {
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
TimeDelta::seconds(secs)
TimeDelta::try_minutes(minutes).expect("Duration::minutes out of bounds")
}

/// Makes a new `Duration` with given number of minutes.
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Returns None when the duration is out of bounds.
#[inline]
pub fn try_minutes(minutes: i64) -> Option<TimeDelta> {
minutes.checked_mul(SECS_PER_MINUTE).and_then(TimeDelta::try_seconds)
}

/// Makes a new `Duration` with given number of seconds.
/// Panics when the duration is more than `i64::MAX` seconds
/// or less than `i64::MIN` seconds.
#[inline]
pub fn seconds(seconds: i64) -> TimeDelta {
TimeDelta::try_seconds(seconds).expect("Duration::seconds out of bounds")
}

/// Makes a new `Duration` with given number of seconds.
/// Returns None when the duration is more than `i64::MAX` milliseconds
/// or less than `i64::MIN` milliseconds.
#[inline]
pub fn try_seconds(seconds: i64) -> Option<TimeDelta> {
let d = TimeDelta { secs: seconds, nanos: 0 };
if d < MIN || d > MAX {
panic!("Duration::seconds out of bounds");
return None;
}
d
Some(d)
}

/// Makes a new `Duration` with given number of milliseconds.
Expand Down

0 comments on commit 9262c43

Please sign in to comment.