Skip to content

Commit 9262c43

Browse files
committed
Fix panic in DateTime::checked_add_days
Incidentally, add TimeDelta::try_* builders so that it's possible to build them without risking a panic.
1 parent 378efb1 commit 9262c43

File tree

2 files changed

+51
-11
lines changed

2 files changed

+51
-11
lines changed

src/naive/date.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,10 @@ impl NaiveDate {
644644
/// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(2)),
645645
/// Some(NaiveDate::from_ymd_opt(2022, 8, 2).unwrap())
646646
/// );
647+
/// assert_eq!(
648+
/// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(1000000000000)),
649+
/// None
650+
/// );
647651
/// ```
648652
pub fn checked_add_days(self, days: Days) -> Option<Self> {
649653
if days.0 == 0 {
@@ -673,7 +677,7 @@ impl NaiveDate {
673677
}
674678

675679
fn diff_days(self, days: i64) -> Option<Self> {
676-
self.checked_add_signed(TimeDelta::days(days))
680+
self.checked_add_signed(TimeDelta::try_days(days)?)
677681
}
678682

679683
/// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`.

src/time_delta.rs

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,47 +75,83 @@ impl TimeDelta {
7575
/// Panics when the duration is out of bounds.
7676
#[inline]
7777
pub fn weeks(weeks: i64) -> TimeDelta {
78-
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
79-
TimeDelta::seconds(secs)
78+
TimeDelta::try_weeks(weeks).expect("Duration::weeks out of bounds")
79+
}
80+
81+
/// Makes a new `Duration` with given number of weeks.
82+
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
83+
/// Returns None when the duration is out of bounds.
84+
#[inline]
85+
pub fn try_weeks(weeks: i64) -> Option<TimeDelta> {
86+
weeks.checked_mul(SECS_PER_WEEK).and_then(TimeDelta::try_seconds)
8087
}
8188

8289
/// Makes a new `Duration` with given number of days.
8390
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
8491
/// Panics when the duration is out of bounds.
8592
#[inline]
8693
pub fn days(days: i64) -> TimeDelta {
87-
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
88-
TimeDelta::seconds(secs)
94+
TimeDelta::try_days(days).expect("Duration::days out of bounds")
95+
}
96+
97+
/// Makes a new `Duration` with given number of days.
98+
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
99+
/// Returns None when the duration is out of bounds.
100+
#[inline]
101+
pub fn try_days(days: i64) -> Option<TimeDelta> {
102+
days.checked_mul(SECS_PER_DAY).and_then(TimeDelta::try_seconds)
89103
}
90104

91105
/// Makes a new `Duration` with given number of hours.
92106
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
93107
/// Panics when the duration is out of bounds.
94108
#[inline]
95109
pub fn hours(hours: i64) -> TimeDelta {
96-
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
97-
TimeDelta::seconds(secs)
110+
TimeDelta::try_hours(hours).expect("Duration::hours ouf of bounds")
111+
}
112+
113+
/// Makes a new `Duration` with given number of hours.
114+
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
115+
/// Returns None when the duration is out of bounds.
116+
#[inline]
117+
pub fn try_hours(hours: i64) -> Option<TimeDelta> {
118+
hours.checked_mul(SECS_PER_HOUR).and_then(TimeDelta::try_seconds)
98119
}
99120

100121
/// Makes a new `Duration` with given number of minutes.
101122
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
102123
/// Panics when the duration is out of bounds.
103124
#[inline]
104125
pub fn minutes(minutes: i64) -> TimeDelta {
105-
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
106-
TimeDelta::seconds(secs)
126+
TimeDelta::try_minutes(minutes).expect("Duration::minutes out of bounds")
127+
}
128+
129+
/// Makes a new `Duration` with given number of minutes.
130+
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
131+
/// Returns None when the duration is out of bounds.
132+
#[inline]
133+
pub fn try_minutes(minutes: i64) -> Option<TimeDelta> {
134+
minutes.checked_mul(SECS_PER_MINUTE).and_then(TimeDelta::try_seconds)
107135
}
108136

109137
/// Makes a new `Duration` with given number of seconds.
110138
/// Panics when the duration is more than `i64::MAX` seconds
111139
/// or less than `i64::MIN` seconds.
112140
#[inline]
113141
pub fn seconds(seconds: i64) -> TimeDelta {
142+
TimeDelta::try_seconds(seconds).expect("Duration::seconds out of bounds")
143+
}
144+
145+
/// Makes a new `Duration` with given number of seconds.
146+
/// Returns None when the duration is more than `i64::MAX` milliseconds
147+
/// or less than `i64::MIN` milliseconds.
148+
#[inline]
149+
pub fn try_seconds(seconds: i64) -> Option<TimeDelta> {
114150
let d = TimeDelta { secs: seconds, nanos: 0 };
115151
if d < MIN || d > MAX {
116-
panic!("Duration::seconds out of bounds");
152+
return None;
117153
}
118-
d
154+
Some(d)
119155
}
120156

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

0 commit comments

Comments
 (0)