@@ -372,6 +372,40 @@ impl TimeDelta {
372372 TimeDelta :: new ( secs, nanos as u32 )
373373 }
374374
375+ /// Multiply a `TimeDelta` with a i32, returning `None` if overflow occurred.
376+ #[ must_use]
377+ pub const fn checked_mul ( & self , rhs : i32 ) -> Option < TimeDelta > {
378+ // Multiply nanoseconds as i64, because it cannot overflow that way.
379+ let total_nanos = self . nanos as i64 * rhs as i64 ;
380+ let ( extra_secs, nanos) = div_mod_floor_64 ( total_nanos, NANOS_PER_SEC as i64 ) ;
381+ // Multiply seconds as i128 to prevent overflow
382+ let secs: i128 = self . secs as i128 * rhs as i128 + extra_secs as i128 ;
383+ if secs <= i64:: MIN as i128 || secs >= i64:: MAX as i128 {
384+ return None ;
385+ } ;
386+ Some ( TimeDelta { secs : secs as i64 , nanos : nanos as i32 } )
387+ }
388+
389+ /// Divide a `TimeDelta` with a i32, returning `None` if dividing by 0.
390+ #[ must_use]
391+ pub const fn checked_div ( & self , rhs : i32 ) -> Option < TimeDelta > {
392+ if rhs == 0 {
393+ return None ;
394+ }
395+ let secs = self . secs / rhs as i64 ;
396+ let carry = self . secs % rhs as i64 ;
397+ let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64 ;
398+ let nanos = self . nanos / rhs + extra_nanos as i32 ;
399+
400+ let ( secs, nanos) = match nanos {
401+ i32:: MIN ..=-1 => ( secs - 1 , nanos + NANOS_PER_SEC ) ,
402+ NANOS_PER_SEC ..=i32:: MAX => ( secs + 1 , nanos - NANOS_PER_SEC ) ,
403+ _ => ( secs, nanos) ,
404+ } ;
405+
406+ Some ( TimeDelta { secs, nanos } )
407+ }
408+
375409 /// Returns the `TimeDelta` as an absolute (non-negative) value.
376410 #[ inline]
377411 pub const fn abs ( & self ) -> TimeDelta {
@@ -489,31 +523,15 @@ impl Mul<i32> for TimeDelta {
489523 type Output = TimeDelta ;
490524
491525 fn mul ( self , rhs : i32 ) -> TimeDelta {
492- // Multiply nanoseconds as i64, because it cannot overflow that way.
493- let total_nanos = self . nanos as i64 * rhs as i64 ;
494- let ( extra_secs, nanos) = div_mod_floor_64 ( total_nanos, NANOS_PER_SEC as i64 ) ;
495- let secs = self . secs * rhs as i64 + extra_secs;
496- TimeDelta { secs, nanos : nanos as i32 }
526+ self . checked_mul ( rhs) . expect ( "`TimeDelta * i32` overflowed" )
497527 }
498528}
499529
500530impl Div < i32 > for TimeDelta {
501531 type Output = TimeDelta ;
502532
503533 fn div ( self , rhs : i32 ) -> TimeDelta {
504- let mut secs = self . secs / rhs as i64 ;
505- let carry = self . secs - secs * rhs as i64 ;
506- let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64 ;
507- let mut nanos = self . nanos / rhs + extra_nanos as i32 ;
508- if nanos >= NANOS_PER_SEC {
509- nanos -= NANOS_PER_SEC ;
510- secs += 1 ;
511- }
512- if nanos < 0 {
513- nanos += NANOS_PER_SEC ;
514- secs -= 1 ;
515- }
516- TimeDelta { secs, nanos }
534+ self . checked_div ( rhs) . expect ( "`i32` is zero" )
517535 }
518536}
519537
@@ -1034,6 +1052,7 @@ mod tests {
10341052 #[ test]
10351053 fn test_duration_checked_ops ( ) {
10361054 let milliseconds = |ms| TimeDelta :: try_milliseconds ( ms) . unwrap ( ) ;
1055+ let seconds = |s| TimeDelta :: try_seconds ( s) . unwrap ( ) ;
10371056
10381057 assert_eq ! (
10391058 milliseconds( i64 :: MAX ) . checked_add( & milliseconds( 0 ) ) ,
@@ -1056,6 +1075,10 @@ mod tests {
10561075 ) ;
10571076 assert ! ( milliseconds( -i64 :: MAX ) . checked_sub( & milliseconds( 1 ) ) . is_none( ) ) ;
10581077 assert ! ( milliseconds( -i64 :: MAX ) . checked_sub( & TimeDelta :: nanoseconds( 1 ) ) . is_none( ) ) ;
1078+
1079+ assert ! ( seconds( i64 :: MAX / 1000 ) . checked_mul( 2000 ) . is_none( ) ) ;
1080+ assert ! ( seconds( i64 :: MIN / 1000 ) . checked_mul( 2000 ) . is_none( ) ) ;
1081+ assert ! ( seconds( 1 ) . checked_div( 0 ) . is_none( ) ) ;
10591082 }
10601083
10611084 #[ test]
0 commit comments