@@ -97,22 +97,154 @@ impl Duration {
9797 #[ stable( feature = "duration" , since = "1.3.0" ) ]
9898 #[ inline]
9999 pub fn subsec_nanos ( & self ) -> u32 { self . nanos }
100+
101+ /// Checked duration addition. Computes `self + other`, returning `None`
102+ /// if overflow occurred.
103+ ///
104+ /// # Examples
105+ ///
106+ /// Basic usage:
107+ ///
108+ /// ```
109+ /// #![feature(duration_checked_ops)]
110+ ///
111+ /// use std::time::Duration;
112+ ///
113+ /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1)));
114+ /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(std::u64::MAX, 0)), None);
115+ /// ```
116+ #[ unstable( feature = "duration_checked_ops" , issue = "35774" ) ]
117+ #[ inline]
118+ pub fn checked_add ( self , rhs : Duration ) -> Option < Duration > {
119+ if let Some ( mut secs) = self . secs . checked_add ( rhs. secs ) {
120+ let mut nanos = self . nanos + rhs. nanos ;
121+ if nanos >= NANOS_PER_SEC {
122+ nanos -= NANOS_PER_SEC ;
123+ if let Some ( new_secs) = secs. checked_add ( 1 ) {
124+ secs = new_secs;
125+ } else {
126+ return None ;
127+ }
128+ }
129+ debug_assert ! ( nanos < NANOS_PER_SEC ) ;
130+ Some ( Duration {
131+ secs : secs,
132+ nanos : nanos,
133+ } )
134+ } else {
135+ None
136+ }
137+ }
138+
139+ /// Checked duration subtraction. Computes `self + other`, returning `None`
140+ /// if the result would be negative or if underflow occurred.
141+ ///
142+ /// # Examples
143+ ///
144+ /// Basic usage:
145+ ///
146+ /// ```
147+ /// #![feature(duration_checked_ops)]
148+ ///
149+ /// use std::time::Duration;
150+ ///
151+ /// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1)));
152+ /// assert_eq!(Duration::new(0, 0).checked_sub(Duration::new(0, 1)), None);
153+ /// ```
154+ #[ unstable( feature = "duration_checked_ops" , issue = "35774" ) ]
155+ #[ inline]
156+ pub fn checked_sub ( self , rhs : Duration ) -> Option < Duration > {
157+ if let Some ( mut secs) = self . secs . checked_sub ( rhs. secs ) {
158+ let nanos = if self . nanos >= rhs. nanos {
159+ self . nanos - rhs. nanos
160+ } else {
161+ if let Some ( sub_secs) = secs. checked_sub ( 1 ) {
162+ secs = sub_secs;
163+ self . nanos + NANOS_PER_SEC - rhs. nanos
164+ } else {
165+ return None ;
166+ }
167+ } ;
168+ debug_assert ! ( nanos < NANOS_PER_SEC ) ;
169+ Some ( Duration { secs : secs, nanos : nanos } )
170+ } else {
171+ None
172+ }
173+ }
174+
175+ /// Checked duration multiplication. Computes `self * other`, returning
176+ /// `None` if underflow or overflow occurred.
177+ ///
178+ /// # Examples
179+ ///
180+ /// Basic usage:
181+ ///
182+ /// ```
183+ /// #![feature(duration_checked_ops)]
184+ ///
185+ /// use std::time::Duration;
186+ ///
187+ /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2)));
188+ /// assert_eq!(Duration::new(std::u64::MAX - 1, 0).checked_mul(2), None);
189+ /// ```
190+ #[ unstable( feature = "duration_checked_ops" , issue = "35774" ) ]
191+ #[ inline]
192+ pub fn checked_mul ( self , rhs : u32 ) -> Option < Duration > {
193+ // Multiply nanoseconds as u64, because it cannot overflow that way.
194+ let total_nanos = self . nanos as u64 * rhs as u64 ;
195+ let extra_secs = total_nanos / ( NANOS_PER_SEC as u64 ) ;
196+ let nanos = ( total_nanos % ( NANOS_PER_SEC as u64 ) ) as u32 ;
197+ if let Some ( secs) = self . secs
198+ . checked_mul ( rhs as u64 )
199+ . and_then ( |s| s. checked_add ( extra_secs) ) {
200+ debug_assert ! ( nanos < NANOS_PER_SEC ) ;
201+ Some ( Duration {
202+ secs : secs,
203+ nanos : nanos,
204+ } )
205+ } else {
206+ None
207+ }
208+ }
209+
210+ /// Checked duration division. Computes `self / other`, returning `None`
211+ /// if `other == 0` or the operation results in underflow or overflow.
212+ ///
213+ /// # Examples
214+ ///
215+ /// Basic usage:
216+ ///
217+ /// ```
218+ /// #![feature(duration_checked_ops)]
219+ ///
220+ /// use std::time::Duration;
221+ ///
222+ /// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0)));
223+ /// assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000)));
224+ /// assert_eq!(Duration::new(2, 0).checked_div(0), None);
225+ /// ```
226+ #[ unstable( feature = "duration_checked_ops" , issue = "35774" ) ]
227+ #[ inline]
228+ pub fn checked_div ( self , rhs : u32 ) -> Option < Duration > {
229+ if rhs != 0 {
230+ let secs = self . secs / ( rhs as u64 ) ;
231+ let carry = self . secs - secs * ( rhs as u64 ) ;
232+ let extra_nanos = carry * ( NANOS_PER_SEC as u64 ) / ( rhs as u64 ) ;
233+ let nanos = self . nanos / rhs + ( extra_nanos as u32 ) ;
234+ debug_assert ! ( nanos < NANOS_PER_SEC ) ;
235+ Some ( Duration { secs : secs, nanos : nanos } )
236+ } else {
237+ None
238+ }
239+ }
100240}
101241
102242#[ stable( feature = "duration" , since = "1.3.0" ) ]
103243impl Add for Duration {
104244 type Output = Duration ;
105245
106246 fn add ( self , rhs : Duration ) -> Duration {
107- let mut secs = self . secs . checked_add ( rhs. secs )
108- . expect ( "overflow when adding durations" ) ;
109- let mut nanos = self . nanos + rhs. nanos ;
110- if nanos >= NANOS_PER_SEC {
111- nanos -= NANOS_PER_SEC ;
112- secs = secs. checked_add ( 1 ) . expect ( "overflow when adding durations" ) ;
113- }
114- debug_assert ! ( nanos < NANOS_PER_SEC ) ;
115- Duration { secs : secs, nanos : nanos }
247+ self . checked_add ( rhs) . expect ( "overflow when adding durations" )
116248 }
117249}
118250
@@ -128,17 +260,7 @@ impl Sub for Duration {
128260 type Output = Duration ;
129261
130262 fn sub ( self , rhs : Duration ) -> Duration {
131- let mut secs = self . secs . checked_sub ( rhs. secs )
132- . expect ( "overflow when subtracting durations" ) ;
133- let nanos = if self . nanos >= rhs. nanos {
134- self . nanos - rhs. nanos
135- } else {
136- secs = secs. checked_sub ( 1 )
137- . expect ( "overflow when subtracting durations" ) ;
138- self . nanos + NANOS_PER_SEC - rhs. nanos
139- } ;
140- debug_assert ! ( nanos < NANOS_PER_SEC ) ;
141- Duration { secs : secs, nanos : nanos }
263+ self . checked_sub ( rhs) . expect ( "overflow when subtracting durations" )
142264 }
143265}
144266
@@ -154,15 +276,7 @@ impl Mul<u32> for Duration {
154276 type Output = Duration ;
155277
156278 fn mul ( self , rhs : u32 ) -> Duration {
157- // Multiply nanoseconds as u64, because it cannot overflow that way.
158- let total_nanos = self . nanos as u64 * rhs as u64 ;
159- let extra_secs = total_nanos / ( NANOS_PER_SEC as u64 ) ;
160- let nanos = ( total_nanos % ( NANOS_PER_SEC as u64 ) ) as u32 ;
161- let secs = self . secs . checked_mul ( rhs as u64 )
162- . and_then ( |s| s. checked_add ( extra_secs) )
163- . expect ( "overflow when multiplying duration" ) ;
164- debug_assert ! ( nanos < NANOS_PER_SEC ) ;
165- Duration { secs : secs, nanos : nanos }
279+ self . checked_mul ( rhs) . expect ( "overflow when multiplying duration by scalar" )
166280 }
167281}
168282
@@ -178,12 +292,7 @@ impl Div<u32> for Duration {
178292 type Output = Duration ;
179293
180294 fn div ( self , rhs : u32 ) -> Duration {
181- let secs = self . secs / ( rhs as u64 ) ;
182- let carry = self . secs - secs * ( rhs as u64 ) ;
183- let extra_nanos = carry * ( NANOS_PER_SEC as u64 ) / ( rhs as u64 ) ;
184- let nanos = self . nanos / rhs + ( extra_nanos as u32 ) ;
185- debug_assert ! ( nanos < NANOS_PER_SEC ) ;
186- Duration { secs : secs, nanos : nanos }
295+ self . checked_div ( rhs) . expect ( "divide by zero error when dividing duration by scalar" )
187296 }
188297}
189298
@@ -234,6 +343,15 @@ mod tests {
234343 Duration :: new( 1 , 1 ) ) ;
235344 }
236345
346+ #[ test]
347+ fn checked_add ( ) {
348+ assert_eq ! ( Duration :: new( 0 , 0 ) . checked_add( Duration :: new( 0 , 1 ) ) ,
349+ Some ( Duration :: new( 0 , 1 ) ) ) ;
350+ assert_eq ! ( Duration :: new( 0 , 500_000_000 ) . checked_add( Duration :: new( 0 , 500_000_001 ) ) ,
351+ Some ( Duration :: new( 1 , 1 ) ) ) ;
352+ assert_eq ! ( Duration :: new( 1 , 0 ) . checked_add( Duration :: new( :: u64 :: MAX , 0 ) ) , None ) ;
353+ }
354+
237355 #[ test]
238356 fn sub ( ) {
239357 assert_eq ! ( Duration :: new( 0 , 1 ) - Duration :: new( 0 , 0 ) ,
@@ -244,6 +362,18 @@ mod tests {
244362 Duration :: new( 0 , 999_999_999 ) ) ;
245363 }
246364
365+ #[ test]
366+ fn checked_sub ( ) {
367+ let zero = Duration :: new ( 0 , 0 ) ;
368+ let one_nano = Duration :: new ( 0 , 1 ) ;
369+ let one_sec = Duration :: new ( 1 , 0 ) ;
370+ assert_eq ! ( one_nano. checked_sub( zero) , Some ( Duration :: new( 0 , 1 ) ) ) ;
371+ assert_eq ! ( one_sec. checked_sub( one_nano) ,
372+ Some ( Duration :: new( 0 , 999_999_999 ) ) ) ;
373+ assert_eq ! ( zero. checked_sub( one_nano) , None ) ;
374+ assert_eq ! ( zero. checked_sub( one_sec) , None ) ;
375+ }
376+
247377 #[ test] #[ should_panic]
248378 fn sub_bad1 ( ) {
249379 Duration :: new ( 0 , 0 ) - Duration :: new ( 0 , 1 ) ;
@@ -263,11 +393,28 @@ mod tests {
263393 Duration :: new( 2000 , 4000 ) ) ;
264394 }
265395
396+ #[ test]
397+ fn checked_mul ( ) {
398+ assert_eq ! ( Duration :: new( 0 , 1 ) . checked_mul( 2 ) , Some ( Duration :: new( 0 , 2 ) ) ) ;
399+ assert_eq ! ( Duration :: new( 1 , 1 ) . checked_mul( 3 ) , Some ( Duration :: new( 3 , 3 ) ) ) ;
400+ assert_eq ! ( Duration :: new( 0 , 500_000_001 ) . checked_mul( 4 ) , Some ( Duration :: new( 2 , 4 ) ) ) ;
401+ assert_eq ! ( Duration :: new( 0 , 500_000_001 ) . checked_mul( 4000 ) ,
402+ Some ( Duration :: new( 2000 , 4000 ) ) ) ;
403+ assert_eq ! ( Duration :: new( :: u64 :: MAX - 1 , 0 ) . checked_mul( 2 ) , None ) ;
404+ }
405+
266406 #[ test]
267407 fn div ( ) {
268408 assert_eq ! ( Duration :: new( 0 , 1 ) / 2 , Duration :: new( 0 , 0 ) ) ;
269409 assert_eq ! ( Duration :: new( 1 , 1 ) / 3 , Duration :: new( 0 , 333_333_333 ) ) ;
270410 assert_eq ! ( Duration :: new( 99 , 999_999_000 ) / 100 ,
271411 Duration :: new( 0 , 999_999_990 ) ) ;
272412 }
413+
414+ #[ test]
415+ fn checked_div ( ) {
416+ assert_eq ! ( Duration :: new( 2 , 0 ) . checked_div( 2 ) , Some ( Duration :: new( 1 , 0 ) ) ) ;
417+ assert_eq ! ( Duration :: new( 1 , 0 ) . checked_div( 2 ) , Some ( Duration :: new( 0 , 500_000_000 ) ) ) ;
418+ assert_eq ! ( Duration :: new( 2 , 0 ) . checked_div( 0 ) , None ) ;
419+ }
273420}
0 commit comments