@@ -51,10 +51,17 @@ pub struct Duration {
51
51
nanos : i32 , // Always 0 <= nanos < NANOS_PER_SEC
52
52
}
53
53
54
- /// The minimum possible `Duration`.
55
- pub static MIN : Duration = Duration { secs : i64:: MIN , nanos : 0 } ;
56
- /// The maximum possible `Duration`.
57
- pub static MAX : Duration = Duration { secs : i64:: MAX , nanos : NANOS_PER_SEC - 1 } ;
54
+ /// The minimum possible `Duration`: `i64::MIN` milliseconds.
55
+ pub static MIN : Duration = Duration {
56
+ secs : i64:: MIN / MILLIS_PER_SEC - 1 ,
57
+ nanos : NANOS_PER_SEC + ( i64:: MIN % MILLIS_PER_SEC ) as i32 * NANOS_PER_MILLI
58
+ } ;
59
+
60
+ /// The maximum possible `Duration`: `i64::MAX` milliseconds.
61
+ pub static MAX : Duration = Duration {
62
+ secs : i64:: MAX / MILLIS_PER_SEC ,
63
+ nanos : ( i64:: MAX % MILLIS_PER_SEC ) as i32 * NANOS_PER_MILLI
64
+ } ;
58
65
59
66
impl Duration {
60
67
/// Makes a new `Duration` with given number of weeks.
@@ -94,9 +101,15 @@ impl Duration {
94
101
}
95
102
96
103
/// Makes a new `Duration` with given number of seconds.
104
+ /// Fails when the duration is more than `i64::MAX` milliseconds
105
+ /// or less than `i64::MIN` milliseconds.
97
106
#[ inline]
98
107
pub fn seconds ( seconds : i64 ) -> Duration {
99
- Duration { secs : seconds, nanos : 0 }
108
+ let d = Duration { secs : seconds, nanos : 0 } ;
109
+ if d < MIN || d > MAX {
110
+ fail ! ( "Duration::seconds out of bounds" ) ;
111
+ }
112
+ d
100
113
}
101
114
102
115
/// Makes a new `Duration` with given number of milliseconds.
@@ -167,11 +180,12 @@ impl Duration {
167
180
}
168
181
169
182
/// Returns the total number of whole milliseconds in the duration,
170
- /// or `None` on overflow (exceeding 2^63 milliseconds in either direction).
171
- pub fn num_milliseconds ( & self ) -> Option < i64 > {
172
- let secs_part = try_opt ! ( self . num_seconds( ) . checked_mul( & MILLIS_PER_SEC ) ) ;
183
+ pub fn num_milliseconds ( & self ) -> i64 {
184
+ // A proper Duration will not overflow, because MIN and MAX are defined
185
+ // such that the range is exactly i64 milliseconds.
186
+ let secs_part = self . num_seconds ( ) * MILLIS_PER_SEC ;
173
187
let nanos_part = self . nanos_mod_sec ( ) / NANOS_PER_MILLI ;
174
- secs_part. checked_add ( & ( nanos_part as i64 ) )
188
+ secs_part + nanos_part as i64
175
189
}
176
190
177
191
/// Returns the total number of whole microseconds in the duration,
@@ -211,13 +225,7 @@ impl num::Zero for Duration {
211
225
impl Neg < Duration > for Duration {
212
226
#[ inline]
213
227
fn neg ( & self ) -> Duration {
214
- if self . secs == i64:: MIN && self . nanos == 0 {
215
- // The minimum value cannot be negated due to overflow. Use the
216
- // maximum value, which is one nanosecond less than the negated minimum.
217
- MAX
218
- } else if self . secs == i64:: MIN {
219
- Duration { secs : i64:: MAX , nanos : NANOS_PER_SEC - self . nanos }
220
- } else if self . nanos == 0 {
228
+ if self . nanos == 0 {
221
229
Duration { secs : -self . secs , nanos : 0 }
222
230
} else {
223
231
Duration { secs : -self . secs - 1 , nanos : NANOS_PER_SEC - self . nanos }
@@ -245,7 +253,10 @@ impl num::CheckedAdd for Duration {
245
253
nanos -= NANOS_PER_SEC ;
246
254
secs = try_opt ! ( secs. checked_add( & 1 ) ) ;
247
255
}
248
- Some ( Duration { secs : secs, nanos : nanos } )
256
+ let d = Duration { secs : secs, nanos : nanos } ;
257
+ // Even if d is within the bounds of i64 seconds,
258
+ // it might still overflow i64 milliseconds.
259
+ if d < MIN || d > MAX { None } else { Some ( d) }
249
260
}
250
261
}
251
262
@@ -269,7 +280,10 @@ impl num::CheckedSub for Duration {
269
280
nanos += NANOS_PER_SEC ;
270
281
secs = try_opt ! ( secs. checked_sub( & 1 ) ) ;
271
282
}
272
- Some ( Duration { secs : secs, nanos : nanos } )
283
+ let d = Duration { secs : secs, nanos : nanos } ;
284
+ // Even if d is within the bounds of i64 seconds,
285
+ // it might still overflow i64 milliseconds.
286
+ if d < MIN || d > MAX { None } else { Some ( d) }
273
287
}
274
288
}
275
289
@@ -407,26 +421,22 @@ mod tests {
407
421
assert_eq ! ( Duration :: milliseconds( 1001 ) . num_seconds( ) , 1 ) ;
408
422
assert_eq ! ( Duration :: milliseconds( -999 ) . num_seconds( ) , 0 ) ;
409
423
assert_eq ! ( Duration :: milliseconds( -1001 ) . num_seconds( ) , -1 ) ;
410
- assert_eq ! ( Duration :: seconds( i64 :: MAX ) . num_seconds( ) , i64 :: MAX ) ;
411
- assert_eq ! ( Duration :: seconds( i64 :: MIN ) . num_seconds( ) , i64 :: MIN ) ;
412
- assert_eq ! ( MAX . num_seconds( ) , i64 :: MAX ) ;
413
- assert_eq ! ( MIN . num_seconds( ) , i64 :: MIN ) ;
414
424
}
415
425
416
426
#[ test]
417
427
fn test_duration_num_milliseconds ( ) {
418
428
let d: Duration = Zero :: zero ( ) ;
419
- assert_eq ! ( d. num_milliseconds( ) , Some ( 0 ) ) ;
420
- assert_eq ! ( Duration :: milliseconds( 1 ) . num_milliseconds( ) , Some ( 1 ) ) ;
421
- assert_eq ! ( Duration :: milliseconds( -1 ) . num_milliseconds( ) , Some ( - 1 ) ) ;
422
- assert_eq ! ( Duration :: microseconds( 999 ) . num_milliseconds( ) , Some ( 0 ) ) ;
423
- assert_eq ! ( Duration :: microseconds( 1001 ) . num_milliseconds( ) , Some ( 1 ) ) ;
424
- assert_eq ! ( Duration :: microseconds( -999 ) . num_milliseconds( ) , Some ( 0 ) ) ;
425
- assert_eq ! ( Duration :: microseconds( -1001 ) . num_milliseconds( ) , Some ( - 1 ) ) ;
426
- assert_eq ! ( Duration :: milliseconds( i64 :: MAX ) . num_milliseconds( ) , Some ( i64 :: MAX ) ) ;
427
- assert_eq ! ( Duration :: milliseconds( i64 :: MIN ) . num_milliseconds( ) , Some ( i64 :: MIN ) ) ;
428
- assert_eq ! ( MAX . num_milliseconds( ) , None ) ;
429
- assert_eq ! ( MIN . num_milliseconds( ) , None ) ;
429
+ assert_eq ! ( d. num_milliseconds( ) , 0 ) ;
430
+ assert_eq ! ( Duration :: milliseconds( 1 ) . num_milliseconds( ) , 1 ) ;
431
+ assert_eq ! ( Duration :: milliseconds( -1 ) . num_milliseconds( ) , - 1 ) ;
432
+ assert_eq ! ( Duration :: microseconds( 999 ) . num_milliseconds( ) , 0 ) ;
433
+ assert_eq ! ( Duration :: microseconds( 1001 ) . num_milliseconds( ) , 1 ) ;
434
+ assert_eq ! ( Duration :: microseconds( -999 ) . num_milliseconds( ) , 0 ) ;
435
+ assert_eq ! ( Duration :: microseconds( -1001 ) . num_milliseconds( ) , - 1 ) ;
436
+ assert_eq ! ( Duration :: milliseconds( i64 :: MAX ) . num_milliseconds( ) , i64 :: MAX ) ;
437
+ assert_eq ! ( Duration :: milliseconds( i64 :: MIN ) . num_milliseconds( ) , i64 :: MIN ) ;
438
+ assert_eq ! ( MAX . num_milliseconds( ) , i64 :: MAX ) ;
439
+ assert_eq ! ( MIN . num_milliseconds( ) , i64 :: MIN ) ;
430
440
}
431
441
432
442
#[ test]
@@ -477,13 +487,13 @@ mod tests {
477
487
478
488
#[ test]
479
489
fn test_duration_checked_ops ( ) {
480
- assert_eq ! ( Duration :: seconds ( i64 :: MAX ) . checked_add( & Duration :: milliseconds ( 999 ) ) ,
481
- Some ( Duration :: seconds ( i64 :: MAX - 1 ) + Duration :: milliseconds ( 1999 ) ) ) ;
482
- assert ! ( Duration :: seconds ( i64 :: MAX ) . checked_add( & Duration :: milliseconds ( 1000 ) ) . is_none( ) ) ;
490
+ assert_eq ! ( Duration :: milliseconds ( i64 :: MAX - 1 ) . checked_add( & Duration :: microseconds ( 999 ) ) ,
491
+ Some ( Duration :: milliseconds ( i64 :: MAX - 2 ) + Duration :: microseconds ( 1999 ) ) ) ;
492
+ assert ! ( Duration :: milliseconds ( i64 :: MAX ) . checked_add( & Duration :: microseconds ( 1000 ) ) . is_none( ) ) ;
483
493
484
- assert_eq ! ( Duration :: seconds ( i64 :: MIN ) . checked_sub( & Duration :: seconds ( 0 ) ) ,
485
- Some ( Duration :: seconds ( i64 :: MIN ) ) ) ;
486
- assert ! ( Duration :: seconds ( i64 :: MIN ) . checked_sub( & Duration :: seconds ( 1 ) ) . is_none( ) ) ;
494
+ assert_eq ! ( Duration :: milliseconds ( i64 :: MIN ) . checked_sub( & Duration :: milliseconds ( 0 ) ) ,
495
+ Some ( Duration :: milliseconds ( i64 :: MIN ) ) ) ;
496
+ assert ! ( Duration :: milliseconds ( i64 :: MIN ) . checked_sub( & Duration :: milliseconds ( 1 ) ) . is_none( ) ) ;
487
497
}
488
498
489
499
#[ test]
0 commit comments