Coverage Report

Created: 2026-06-28 08:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jiff-0.2.28/src/util/round.rs
Line
Count
Source
1
use crate::{
2
    error::{
3
        unit::{MustDivide, UnitConfigError},
4
        util::RoundingIncrementError,
5
        Error, ErrorContext,
6
    },
7
    util::b,
8
    SignedDuration, Unit,
9
};
10
11
/// A representation of a rounding increment in a particular unit.
12
///
13
/// This implements the core rounding interface of Jiff, shared across all
14
/// types. The constructors on `Increment` know which units *and* increments
15
/// are valid for a particular rounding configuration. For example, only
16
/// `Increment::for_span` supports any `unit` value. All other constructors
17
/// have some kind of limitation (e.g., you can't round a date to the nearest
18
/// month).
19
///
20
/// For most cases, rounding is done via `Increment::round`. This takes a
21
/// quantity in a number of nanoseconds and rounds it to the nearest increment
22
/// (after converting the increment to nanoseconds as well, based on the unit
23
/// provided). This API makes it difficult for callers to get rounding wrong.
24
/// They just need to use the right constructor and provide a quantity in units
25
/// of nanoseconds.
26
///
27
/// For lower level needs, one can use `RoundMode::round_by_duration` directly.
28
/// This doesn't know about any units other than nanoseconds. It's useful in
29
/// contexts when dealing with variable length units and there are no rules
30
/// for what is a valid increment.
31
#[derive(Clone, Copy, Debug)]
32
pub(crate) struct Increment {
33
    unit: Unit,
34
    value: i32,
35
}
36
37
impl Increment {
38
    /// Return a rounding increment suitable for use when rounding a `Span`.
39
0
    pub(crate) fn for_span(
40
0
        unit: Unit,
41
0
        value: i64,
42
0
    ) -> Result<Increment, Error> {
43
        // Indexed by `Unit`.
44
        static LIMITS: &[MustDivide] = &[
45
            MustDivide::NanosPerMicro,
46
            MustDivide::MicrosPerMilli,
47
            MustDivide::MillisPerSec,
48
            MustDivide::SecsPerMin,
49
            MustDivide::MinsPerHour,
50
            MustDivide::HoursPerCivilDay,
51
        ];
52
53
        // We allow any kind of increment for calendar units, but for time
54
        // units, they have to divide evenly into the next highest unit (and
55
        // also be less than that). The reason for this is that calendar
56
        // units vary, where as for time units, given a balanced span, you
57
        // know that time units will always spill over into days so that
58
        // hours/minutes/... will never exceed 24/60/...
59
0
        if unit < Unit::Day {
60
0
            Increment::for_limits(unit, value, LIMITS)
61
0
                .context(RoundingIncrementError::ForSpan)
62
        } else {
63
            // For calendar units, just verify that the increment is in the
64
            // correct range.
65
0
            let value = b::Increment32::check(value)
66
0
                .context(RoundingIncrementError::ForSpan)?;
67
0
            Ok(Increment { unit, value })
68
        }
69
0
    }
70
71
    /// Return a rounding increment suitable for use when rounding a
72
    /// `SignedDuration`.
73
0
    pub(crate) fn for_signed_duration(
74
0
        unit: Unit,
75
0
        value: i64,
76
0
    ) -> Result<Increment, Error> {
77
0
        if unit > Unit::Hour {
78
0
            return Err(Error::from(
79
0
                UnitConfigError::SignedDurationCalendarUnit { smallest: unit },
80
0
            ));
81
0
        }
82
83
        // For signed durations, we allow any increment that is within range.
84
0
        let value = b::Increment32::check(value)
85
0
            .context(RoundingIncrementError::ForSignedDuration)?;
86
0
        Ok(Increment { unit, value })
87
0
    }
88
89
    /// Return a rounding increment suitable for use when rounding a
90
    /// `tz::Offset`.
91
0
    pub(crate) fn for_offset(
92
0
        unit: Unit,
93
0
        value: i64,
94
0
    ) -> Result<Increment, Error> {
95
0
        if !(Unit::Second <= unit && unit <= Unit::Hour) {
96
0
            return Err(Error::from(UnitConfigError::TimeZoneOffset {
97
0
                given: unit,
98
0
            }));
99
0
        }
100
101
        // For time zone offsets, we allow any increment that is within range.
102
0
        let value = b::Increment32::check(value)
103
0
            .context(RoundingIncrementError::ForOffset)?;
104
0
        Ok(Increment { unit, value })
105
0
    }
106
107
    /// Return a rounding increment suitable for use when rounding a
108
    /// `civil::DateTime` or a `Zoned`.
109
0
    pub(crate) fn for_datetime(
110
0
        unit: Unit,
111
0
        value: i64,
112
0
    ) -> Result<Increment, Error> {
113
        // Indexed by `Unit`.
114
        static LIMITS: &[MustDivide] = &[
115
            MustDivide::NanosPerMicro,
116
            MustDivide::MicrosPerMilli,
117
            MustDivide::MillisPerSec,
118
            MustDivide::SecsPerMin,
119
            MustDivide::MinsPerHour,
120
            MustDivide::HoursPerCivilDay,
121
            MustDivide::Days,
122
        ];
123
0
        Increment::for_limits(unit, value, LIMITS)
124
0
            .context(RoundingIncrementError::ForDateTime)
125
0
    }
126
127
    /// Return a rounding increment suitable for use when rounding a
128
    /// `civil::Time`.
129
0
    pub(crate) fn for_time(
130
0
        unit: Unit,
131
0
        value: i64,
132
0
    ) -> Result<Increment, Error> {
133
        // Indexed by `Unit`.
134
        static LIMITS: &[MustDivide] = &[
135
            MustDivide::NanosPerMicro,
136
            MustDivide::MicrosPerMilli,
137
            MustDivide::MillisPerSec,
138
            MustDivide::SecsPerMin,
139
            MustDivide::MinsPerHour,
140
            MustDivide::HoursPerCivilDay,
141
        ];
142
0
        Increment::for_limits(unit, value, LIMITS)
143
0
            .context(RoundingIncrementError::ForTime)
144
0
    }
145
146
    /// Return a rounding increment suitable for use when rounding a
147
    /// `civil::Timestamp`.
148
0
    pub(crate) fn for_timestamp(
149
0
        unit: Unit,
150
0
        value: i64,
151
0
    ) -> Result<Increment, Error> {
152
        // Indexed by `Unit`.
153
        static MAXIMUMS: &[MustDivide] = &[
154
            MustDivide::NanosPerCivilDay,
155
            MustDivide::MicrosPerCivilDay,
156
            MustDivide::MillisPerCivilDay,
157
            MustDivide::SecsPerCivilDay,
158
            MustDivide::MinsPerCivilDay,
159
            MustDivide::HoursPerCivilDay,
160
        ];
161
0
        Increment::for_maximums(unit, value, MAXIMUMS)
162
0
            .context(RoundingIncrementError::ForTimestamp)
163
0
    }
164
165
    /// Rounds the given quantity to the nearest increment value (in terms of
166
    /// the units given to this increment's constructor). Rounding down or up
167
    /// is determined by the mode given.
168
0
    pub(crate) fn round(
169
0
        &self,
170
0
        mode: RoundMode,
171
0
        quantity: SignedDuration,
172
0
    ) -> Result<SignedDuration, Error> {
173
        // OK because the max for the number of nanoseconds in a unit
174
        // is weeks at `604_800_000_000_000` and `increment` cannot be
175
        // any larger than `1_000_000_000`. The multiplication of these two
176
        // values does not exceed `SignedDuration::MAX`.
177
0
        let increment = self.value() * self.unit().duration();
178
0
        mode.round_by_duration(quantity, increment)
179
0
    }
180
181
    /// Returns this increment's unit.
182
    ///
183
    /// The increment value is always in terms of this unit.
184
0
    pub(crate) fn unit(&self) -> Unit {
185
0
        self.unit
186
0
    }
187
188
    /// Returns this increment's value.
189
    ///
190
    /// e.g., "round to the nearest 15th minute" is expressed with an increment
191
    /// value of `15` and a unit of `Unit::Minute`.
192
    ///
193
    /// The value returned is guaranteed to be in the range
194
    /// `1..=1_000_000_000`.
195
0
    pub(crate) fn value(&self) -> i32 {
196
0
        self.value
197
0
    }
198
}
199
200
/// Lower level constructors that require the caller to know the precise
201
/// limits or maximums. These should generally stay unexported.
202
impl Increment {
203
    /// Validate the `increment` value for the given `unit` and limits.
204
    ///
205
    /// Specifically, this ensures that rounding to the given `unit` is
206
    /// supported and that the specific value is *less than* to the
207
    /// corresponding limit.
208
0
    fn for_limits(
209
0
        unit: Unit,
210
0
        value: i64,
211
0
        limits: &[MustDivide],
212
0
    ) -> Result<Increment, Error> {
213
0
        let Some(&must_divide) = limits.get(unit as usize) else {
214
0
            return Err(Error::from(
215
0
                UnitConfigError::RoundToUnitUnsupported { unit },
216
0
            ));
217
        };
218
219
0
        let value32 = b::Increment32::check(value)?;
220
0
        let must_divide_i64 = must_divide.as_i64();
221
0
        if value < must_divide_i64 && must_divide_i64 % value == 0 {
222
0
            return Ok(Increment { unit, value: value32 });
223
0
        }
224
0
        Err(Error::from(UnitConfigError::IncrementDivide {
225
0
            unit,
226
0
            must_divide,
227
0
        }))
228
0
    }
229
230
    /// Validate the `increment` value for the given `unit` and maximums.
231
    ///
232
    /// Specifically, this ensures that rounding to the given `unit` is
233
    /// supported and that the specific value is *less than or equal* to the
234
    /// corresponding maximum.
235
0
    fn for_maximums(
236
0
        unit: Unit,
237
0
        value: i64,
238
0
        maximums: &[MustDivide],
239
0
    ) -> Result<Increment, Error> {
240
0
        let Some(&must_divide) = maximums.get(unit as usize) else {
241
0
            return Err(Error::from(
242
0
                UnitConfigError::RoundToUnitUnsupported { unit },
243
0
            ));
244
        };
245
246
0
        let value32 = b::Increment32::check(value)?;
247
0
        let must_divide_i64 = must_divide.as_i64();
248
0
        if value <= must_divide_i64 && must_divide_i64 % value == 0 {
249
0
            return Ok(Increment { unit, value: value32 });
250
0
        }
251
0
        Err(Error::from(UnitConfigError::IncrementDivide {
252
0
            unit,
253
0
            must_divide,
254
0
        }))
255
0
    }
256
}
257
258
/// The mode for dealing with the remainder when rounding datetimes or spans.
259
///
260
/// This is used in APIs like [`Span::round`](crate::Span::round) for rounding
261
/// spans, and APIs like [`Zoned::round`](crate::Zoned::round) for rounding
262
/// datetimes.
263
///
264
/// In the documentation for each variant, we refer to concepts like the
265
/// "smallest" unit and the "rounding increment." These are best described
266
/// in the documentation for what you're rounding. For example,
267
/// [`SpanRound::smallest`](crate::SpanRound::smallest)
268
/// and [`SpanRound::increment`](crate::SpanRound::increment).
269
///
270
/// # Example
271
///
272
/// This shows how to round a span with a different rounding mode than the
273
/// default:
274
///
275
/// ```
276
/// use jiff::{RoundMode, SpanRound, ToSpan, Unit};
277
///
278
/// // The default rounds like how you were taught in school:
279
/// assert_eq!(
280
///     1.hour().minutes(59).round(Unit::Hour)?,
281
///     2.hours().fieldwise(),
282
/// );
283
/// // But we can change the mode, e.g., truncation:
284
/// let options = SpanRound::new().smallest(Unit::Hour).mode(RoundMode::Trunc);
285
/// assert_eq!(
286
///     1.hour().minutes(59).round(options)?,
287
///     1.hour().fieldwise(),
288
/// );
289
///
290
/// # Ok::<(), Box<dyn std::error::Error>>(())
291
/// ```
292
#[non_exhaustive]
293
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
294
pub enum RoundMode {
295
    /// Rounds toward positive infinity.
296
    ///
297
    /// For negative spans and datetimes, this option will make the absolute
298
    /// value smaller, which could be unexpected. To round away from zero, use
299
    /// `Expand`.
300
    Ceil,
301
    /// Rounds toward negative infinity.
302
    ///
303
    /// This mode acts like `Trunc` for positive spans and datetimes, but
304
    /// for negative values, it will make the absolute value larger, which
305
    /// could be unexpected. To round towards zero, use `Trunc`.
306
    Floor,
307
    /// Rounds away from zero like `Ceil` for positive spans and datetimes,
308
    /// and like `Floor` for negative spans and datetimes.
309
    Expand,
310
    /// Rounds toward zero, chopping off any fractional part of a unit.
311
    ///
312
    /// This is the default when rounding spans returned from
313
    /// datetime arithmetic. (But it is not the default for
314
    /// [`Span::round`](crate::Span::round).)
315
    Trunc,
316
    /// Rounds to the nearest allowed value like `HalfExpand`, but when there
317
    /// is a tie, round towards positive infinity like `Ceil`.
318
    HalfCeil,
319
    /// Rounds to the nearest allowed value like `HalfExpand`, but when there
320
    /// is a tie, round towards negative infinity like `Floor`.
321
    HalfFloor,
322
    /// Rounds to the nearest value allowed by the rounding increment and the
323
    /// smallest unit. When there is a tie, round away from zero like `Ceil`
324
    /// for positive spans and datetimes and like `Floor` for negative spans
325
    /// and datetimes.
326
    ///
327
    /// This corresponds to how rounding is often taught in school.
328
    ///
329
    /// This is the default for rounding spans and datetimes.
330
    HalfExpand,
331
    /// Rounds to the nearest allowed value like `HalfExpand`, but when there
332
    /// is a tie, round towards zero like `Trunc`.
333
    HalfTrunc,
334
    /// Rounds to the nearest allowed value like `HalfExpand`, but when there
335
    /// is a tie, round towards the value that is an even multiple of the
336
    /// rounding increment. For example, with a rounding increment of `3`,
337
    /// the number `10` would round up to `12` instead of down to `9`, because
338
    /// `12` is an even multiple of `3`, where as `9` is is an odd multiple.
339
    HalfEven,
340
}
341
342
impl RoundMode {
343
    /// Round `quantity` to the nearest `increment` using this rounding mode.
344
    ///
345
    /// If this the rounding result would overflow `SignedDuration`, then an
346
    /// error is returned.
347
    ///
348
    /// Callers should generally prefer higher level APIs. But this one is
349
    /// unavoidable when the increment isn't tied to an invariant length and
350
    /// can vary.
351
    ///
352
    /// # Panics
353
    ///
354
    /// Callers must ensure that `increment` has a number of nanoseconds
355
    /// greater than or equal to `1`.
356
0
    pub(crate) fn round_by_duration(
357
0
        self,
358
0
        quantity: SignedDuration,
359
0
        increment: SignedDuration,
360
0
    ) -> Result<SignedDuration, Error> {
361
0
        let quantity = quantity.as_nanos();
362
0
        let increment = increment.as_nanos();
363
0
        assert!(increment >= 1);
364
0
        let rounded = self.round_by_i128(quantity, increment);
365
        // This can fail in the case where `quantity` is somehow rounded to a
366
        // value above/below a signed duration's max/min. This cannot *usually*
367
        // happen in practice because our `quantity` is typically limited by
368
        // the maximum Jiff datetime span. Even spanning from -9999 to 9999, a
369
        // 96-bit integer number of nanoseconds still can't cause an overflow.
370
        // Moreover, `increment` is usually capped to `1_000_000_000`. The only
371
        // case where it isn't is when we're rounding based on variable length
372
        // units (in which case, this lower level rounding routine is called
373
        // directly). For example, when rounding to years with an increment
374
        // (set by the user) to `15_000`. That will get translated to 15,000
375
        // years *in nanoseconds*.
376
        //
377
        // Even still, the complete span of time supported by Jiff can
378
        // comfortably fit into a `SignedDuration`. And because of the limit
379
        // placed on `increment`, I don't believe this error can ever actually
380
        // occur.
381
0
        SignedDuration::try_from_nanos_i128(rounded)
382
0
            .ok_or_else(|| Error::from(b::SignedDurationSeconds::error()))
383
0
    }
384
385
    /// The internal API that does the actual rounding.
386
    ///
387
    /// This does not error on overflow because callers can never use values
388
    /// close to the `i128` limits. If an overflow would otherwise occur due to
389
    /// rounding, then it saturates instead of panicking or returning an error.
390
    ///
391
    /// I wish we could do modulus on `SignedDuration` directly. Then we
392
    /// wouldn't need 128-bit arithmetic at all. But I looked into making
393
    /// `SignedDuration % SignedDuration` work, and it was quite involved? So I
394
    /// guess we should just let the compiler handle it for `i128`. We'd also
395
    /// need division.
396
    ///
397
    /// # Panics
398
    ///
399
    /// When `increment < 1`.
400
0
    fn round_by_i128(self, quantity: i128, increment: i128) -> i128 {
401
0
        assert!(increment >= 1);
402
        // ref: https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement
403
0
        let mode = self;
404
0
        let mut quotient = quantity / increment;
405
0
        let remainder = quantity % increment;
406
0
        if remainder == 0 {
407
0
            return quantity;
408
0
        }
409
0
        let sign = if remainder < 0 { -1 } else { 1 };
410
0
        let tiebreaker = (remainder * 2).abs();
411
0
        let tie = tiebreaker == increment;
412
0
        let expand_is_nearer = tiebreaker > increment;
413
        // ref: https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement
414
0
        match mode {
415
            RoundMode::Ceil => {
416
0
                if sign > 0 {
417
0
                    quotient += sign;
418
0
                }
419
            }
420
            RoundMode::Floor => {
421
0
                if sign < 0 {
422
0
                    quotient += sign;
423
0
                }
424
            }
425
0
            RoundMode::Expand => {
426
0
                quotient += sign;
427
0
            }
428
0
            RoundMode::Trunc => {}
429
            RoundMode::HalfCeil => {
430
0
                if expand_is_nearer || (tie && sign > 0) {
431
0
                    quotient += sign;
432
0
                }
433
            }
434
            RoundMode::HalfFloor => {
435
0
                if expand_is_nearer || (tie && sign < 0) {
436
0
                    quotient += sign;
437
0
                }
438
            }
439
            RoundMode::HalfExpand => {
440
0
                if expand_is_nearer || tie {
441
0
                    quotient += sign;
442
0
                }
443
            }
444
            RoundMode::HalfTrunc => {
445
0
                if expand_is_nearer {
446
0
                    quotient += sign;
447
0
                }
448
            }
449
            RoundMode::HalfEven => {
450
0
                if expand_is_nearer || (tie && quotient.rem_euclid(2) == 1) {
451
0
                    quotient += sign;
452
0
                }
453
            }
454
        }
455
        // We use saturating arithmetic here because this can overflow when
456
        // `quantity` is the max value. Since we're rounding, we just refuse to
457
        // go over the maximum. This can't happen in practice because this is
458
        // a private API and all paths to this API enforce that we are never
459
        // going to round over the `i128` maximum.
460
0
        quotient.saturating_mul(increment)
461
0
    }
462
}
463
464
#[cfg(test)]
465
mod tests {
466
    use super::*;
467
468
    #[test]
469
    fn for_span() {
470
        let get = |unit, value| Increment::for_span(unit, value);
471
472
        assert!(get(Unit::Nanosecond, 1).is_ok());
473
        assert!(get(Unit::Nanosecond, 500).is_ok());
474
        assert!(get(Unit::Nanosecond, -1).is_err());
475
        assert!(get(Unit::Nanosecond, 0).is_err());
476
        assert!(get(Unit::Nanosecond, 7).is_err());
477
        assert!(get(Unit::Nanosecond, 1_000).is_err());
478
        assert!(get(Unit::Nanosecond, i64::MIN).is_err());
479
        assert!(get(Unit::Nanosecond, i64::MAX).is_err());
480
481
        assert!(get(Unit::Microsecond, 1).is_ok());
482
        assert!(get(Unit::Microsecond, 500).is_ok());
483
        assert!(get(Unit::Microsecond, -1).is_err());
484
        assert!(get(Unit::Microsecond, 0).is_err());
485
        assert!(get(Unit::Microsecond, 7).is_err());
486
        assert!(get(Unit::Microsecond, 1_000).is_err());
487
        assert!(get(Unit::Microsecond, i64::MIN).is_err());
488
        assert!(get(Unit::Microsecond, i64::MAX).is_err());
489
490
        assert!(get(Unit::Millisecond, 1).is_ok());
491
        assert!(get(Unit::Millisecond, 500).is_ok());
492
        assert!(get(Unit::Millisecond, -1).is_err());
493
        assert!(get(Unit::Millisecond, 0).is_err());
494
        assert!(get(Unit::Millisecond, 7).is_err());
495
        assert!(get(Unit::Millisecond, 1_000).is_err());
496
        assert!(get(Unit::Millisecond, i64::MIN).is_err());
497
        assert!(get(Unit::Millisecond, i64::MAX).is_err());
498
499
        assert!(get(Unit::Second, 1).is_ok());
500
        assert!(get(Unit::Second, 15).is_ok());
501
        assert!(get(Unit::Second, -1).is_err());
502
        assert!(get(Unit::Second, 0).is_err());
503
        assert!(get(Unit::Second, 7).is_err());
504
        assert!(get(Unit::Second, 1_000).is_err());
505
        assert!(get(Unit::Second, i64::MIN).is_err());
506
        assert!(get(Unit::Second, i64::MAX).is_err());
507
508
        assert!(get(Unit::Minute, 1).is_ok());
509
        assert!(get(Unit::Minute, 15).is_ok());
510
        assert!(get(Unit::Minute, -1).is_err());
511
        assert!(get(Unit::Minute, 0).is_err());
512
        assert!(get(Unit::Minute, 7).is_err());
513
        assert!(get(Unit::Minute, 1_000).is_err());
514
        assert!(get(Unit::Minute, i64::MIN).is_err());
515
        assert!(get(Unit::Minute, i64::MAX).is_err());
516
517
        assert!(get(Unit::Hour, 1).is_ok());
518
        assert!(get(Unit::Hour, 6).is_ok());
519
        assert!(get(Unit::Hour, 12).is_ok());
520
        assert!(get(Unit::Hour, -1).is_err());
521
        assert!(get(Unit::Hour, 0).is_err());
522
        assert!(get(Unit::Hour, 15).is_err());
523
        assert!(get(Unit::Hour, 18).is_err());
524
        assert!(get(Unit::Hour, 23).is_err());
525
        assert!(get(Unit::Hour, 24).is_err());
526
        assert!(get(Unit::Hour, i64::MIN).is_err());
527
        assert!(get(Unit::Hour, i64::MAX).is_err());
528
529
        assert!(get(Unit::Day, 1).is_ok());
530
        assert!(get(Unit::Day, 6).is_ok());
531
        assert!(get(Unit::Day, 7).is_ok());
532
        assert!(get(Unit::Day, 12).is_ok());
533
        assert!(get(Unit::Day, 30).is_ok());
534
        assert!(get(Unit::Day, 31).is_ok());
535
        assert!(get(Unit::Day, 100).is_ok());
536
        assert!(get(Unit::Day, 10_000_000).is_ok());
537
        assert!(get(Unit::Day, 1_000_000_000).is_ok());
538
        assert!(get(Unit::Day, -1).is_err());
539
        assert!(get(Unit::Day, 0).is_err());
540
        assert!(get(Unit::Day, 1_000_000_001).is_err());
541
        assert!(get(Unit::Day, i64::MIN).is_err());
542
        assert!(get(Unit::Day, i64::MAX).is_err());
543
544
        assert!(get(Unit::Week, 1).is_ok());
545
        assert!(get(Unit::Week, 6).is_ok());
546
        assert!(get(Unit::Week, 7).is_ok());
547
        assert!(get(Unit::Week, 12).is_ok());
548
        assert!(get(Unit::Week, 30).is_ok());
549
        assert!(get(Unit::Week, 31).is_ok());
550
        assert!(get(Unit::Week, 100).is_ok());
551
        assert!(get(Unit::Week, 2_000_000).is_ok());
552
        assert!(get(Unit::Week, 1_000_000_000).is_ok());
553
        assert!(get(Unit::Week, -1).is_err());
554
        assert!(get(Unit::Week, 0).is_err());
555
        assert!(get(Unit::Week, 1_000_000_001).is_err());
556
        assert!(get(Unit::Week, i64::MIN).is_err());
557
        assert!(get(Unit::Week, i64::MAX).is_err());
558
559
        assert!(get(Unit::Month, 1).is_ok());
560
        assert!(get(Unit::Month, 6).is_ok());
561
        assert!(get(Unit::Month, 7).is_ok());
562
        assert!(get(Unit::Month, 12).is_ok());
563
        assert!(get(Unit::Month, 30).is_ok());
564
        assert!(get(Unit::Month, 31).is_ok());
565
        assert!(get(Unit::Month, 100).is_ok());
566
        assert!(get(Unit::Month, 300_000).is_ok());
567
        assert!(get(Unit::Month, 1_000_000_000).is_ok());
568
        assert!(get(Unit::Month, -1).is_err());
569
        assert!(get(Unit::Month, 0).is_err());
570
        assert!(get(Unit::Month, 1_000_000_001).is_err());
571
        assert!(get(Unit::Month, i64::MIN).is_err());
572
        assert!(get(Unit::Month, i64::MAX).is_err());
573
574
        assert!(get(Unit::Year, 1).is_ok());
575
        assert!(get(Unit::Year, 6).is_ok());
576
        assert!(get(Unit::Year, 7).is_ok());
577
        assert!(get(Unit::Year, 12).is_ok());
578
        assert!(get(Unit::Year, 30).is_ok());
579
        assert!(get(Unit::Year, 31).is_ok());
580
        assert!(get(Unit::Year, 100).is_ok());
581
        assert!(get(Unit::Year, 50_000).is_ok());
582
        assert!(get(Unit::Year, 1_000_000_000).is_ok());
583
        assert!(get(Unit::Year, -1).is_err());
584
        assert!(get(Unit::Year, 0).is_err());
585
        assert!(get(Unit::Year, 1_000_000_001).is_err());
586
        assert!(get(Unit::Year, i64::MIN).is_err());
587
        assert!(get(Unit::Year, i64::MAX).is_err());
588
    }
589
590
    #[test]
591
    fn for_datetime() {
592
        let get = |unit, value| Increment::for_datetime(unit, value);
593
594
        assert!(get(Unit::Nanosecond, 1).is_ok());
595
        assert!(get(Unit::Nanosecond, 500).is_ok());
596
        assert!(get(Unit::Nanosecond, -1).is_err());
597
        assert!(get(Unit::Nanosecond, 0).is_err());
598
        assert!(get(Unit::Nanosecond, 7).is_err());
599
        assert!(get(Unit::Nanosecond, 1_000).is_err());
600
        assert!(get(Unit::Nanosecond, i64::MIN).is_err());
601
        assert!(get(Unit::Nanosecond, i64::MAX).is_err());
602
603
        assert!(get(Unit::Microsecond, 1).is_ok());
604
        assert!(get(Unit::Microsecond, 500).is_ok());
605
        assert!(get(Unit::Microsecond, -1).is_err());
606
        assert!(get(Unit::Microsecond, 0).is_err());
607
        assert!(get(Unit::Microsecond, 7).is_err());
608
        assert!(get(Unit::Microsecond, 1_000).is_err());
609
        assert!(get(Unit::Microsecond, i64::MIN).is_err());
610
        assert!(get(Unit::Microsecond, i64::MAX).is_err());
611
612
        assert!(get(Unit::Millisecond, 1).is_ok());
613
        assert!(get(Unit::Millisecond, 500).is_ok());
614
        assert!(get(Unit::Millisecond, -1).is_err());
615
        assert!(get(Unit::Millisecond, 0).is_err());
616
        assert!(get(Unit::Millisecond, 7).is_err());
617
        assert!(get(Unit::Millisecond, 1_000).is_err());
618
        assert!(get(Unit::Millisecond, i64::MIN).is_err());
619
        assert!(get(Unit::Millisecond, i64::MAX).is_err());
620
621
        assert!(get(Unit::Second, 1).is_ok());
622
        assert!(get(Unit::Second, 15).is_ok());
623
        assert!(get(Unit::Second, -1).is_err());
624
        assert!(get(Unit::Second, 0).is_err());
625
        assert!(get(Unit::Second, 7).is_err());
626
        assert!(get(Unit::Second, 1_000).is_err());
627
        assert!(get(Unit::Second, i64::MIN).is_err());
628
        assert!(get(Unit::Second, i64::MAX).is_err());
629
630
        assert!(get(Unit::Minute, 1).is_ok());
631
        assert!(get(Unit::Minute, 15).is_ok());
632
        assert!(get(Unit::Minute, -1).is_err());
633
        assert!(get(Unit::Minute, 0).is_err());
634
        assert!(get(Unit::Minute, 7).is_err());
635
        assert!(get(Unit::Minute, 1_000).is_err());
636
        assert!(get(Unit::Minute, i64::MIN).is_err());
637
        assert!(get(Unit::Minute, i64::MAX).is_err());
638
639
        assert!(get(Unit::Hour, 1).is_ok());
640
        assert!(get(Unit::Hour, 6).is_ok());
641
        assert!(get(Unit::Hour, 12).is_ok());
642
        assert!(get(Unit::Hour, -1).is_err());
643
        assert!(get(Unit::Hour, 0).is_err());
644
        assert!(get(Unit::Hour, 15).is_err());
645
        assert!(get(Unit::Hour, 18).is_err());
646
        assert!(get(Unit::Hour, 23).is_err());
647
        assert!(get(Unit::Hour, 24).is_err());
648
        assert!(get(Unit::Hour, i64::MIN).is_err());
649
        assert!(get(Unit::Hour, i64::MAX).is_err());
650
651
        assert!(get(Unit::Day, 1).is_ok());
652
        assert!(get(Unit::Day, -1).is_err());
653
        assert!(get(Unit::Day, 0).is_err());
654
        assert!(get(Unit::Day, 2).is_err());
655
        assert!(get(Unit::Day, 4).is_err());
656
        assert!(get(Unit::Day, 7).is_err());
657
        assert!(get(Unit::Day, i64::MIN).is_err());
658
        assert!(get(Unit::Day, i64::MAX).is_err());
659
    }
660
661
    #[test]
662
    fn for_time() {
663
        let get = |unit, value| Increment::for_time(unit, value);
664
665
        assert!(get(Unit::Nanosecond, 1).is_ok());
666
        assert!(get(Unit::Nanosecond, 500).is_ok());
667
        assert!(get(Unit::Nanosecond, -1).is_err());
668
        assert!(get(Unit::Nanosecond, 0).is_err());
669
        assert!(get(Unit::Nanosecond, 7).is_err());
670
        assert!(get(Unit::Nanosecond, 1_000).is_err());
671
        assert!(get(Unit::Nanosecond, i64::MIN).is_err());
672
        assert!(get(Unit::Nanosecond, i64::MAX).is_err());
673
674
        assert!(get(Unit::Microsecond, 1).is_ok());
675
        assert!(get(Unit::Microsecond, 500).is_ok());
676
        assert!(get(Unit::Microsecond, -1).is_err());
677
        assert!(get(Unit::Microsecond, 0).is_err());
678
        assert!(get(Unit::Microsecond, 7).is_err());
679
        assert!(get(Unit::Microsecond, 1_000).is_err());
680
        assert!(get(Unit::Microsecond, i64::MIN).is_err());
681
        assert!(get(Unit::Microsecond, i64::MAX).is_err());
682
683
        assert!(get(Unit::Millisecond, 1).is_ok());
684
        assert!(get(Unit::Millisecond, 500).is_ok());
685
        assert!(get(Unit::Millisecond, -1).is_err());
686
        assert!(get(Unit::Millisecond, 0).is_err());
687
        assert!(get(Unit::Millisecond, 7).is_err());
688
        assert!(get(Unit::Millisecond, 1_000).is_err());
689
        assert!(get(Unit::Millisecond, i64::MIN).is_err());
690
        assert!(get(Unit::Millisecond, i64::MAX).is_err());
691
692
        assert!(get(Unit::Second, 1).is_ok());
693
        assert!(get(Unit::Second, 15).is_ok());
694
        assert!(get(Unit::Second, -1).is_err());
695
        assert!(get(Unit::Second, 0).is_err());
696
        assert!(get(Unit::Second, 7).is_err());
697
        assert!(get(Unit::Second, 1_000).is_err());
698
        assert!(get(Unit::Second, i64::MIN).is_err());
699
        assert!(get(Unit::Second, i64::MAX).is_err());
700
701
        assert!(get(Unit::Minute, 1).is_ok());
702
        assert!(get(Unit::Minute, 15).is_ok());
703
        assert!(get(Unit::Minute, -1).is_err());
704
        assert!(get(Unit::Minute, 0).is_err());
705
        assert!(get(Unit::Minute, 7).is_err());
706
        assert!(get(Unit::Minute, 1_000).is_err());
707
        assert!(get(Unit::Minute, i64::MIN).is_err());
708
        assert!(get(Unit::Minute, i64::MAX).is_err());
709
710
        assert!(get(Unit::Hour, 1).is_ok());
711
        assert!(get(Unit::Hour, 6).is_ok());
712
        assert!(get(Unit::Hour, 12).is_ok());
713
        assert!(get(Unit::Hour, -1).is_err());
714
        assert!(get(Unit::Hour, 0).is_err());
715
        assert!(get(Unit::Hour, 15).is_err());
716
        assert!(get(Unit::Hour, 18).is_err());
717
        assert!(get(Unit::Hour, 23).is_err());
718
        assert!(get(Unit::Hour, 24).is_err());
719
        assert!(get(Unit::Hour, i64::MIN).is_err());
720
        assert!(get(Unit::Hour, i64::MAX).is_err());
721
722
        assert!(get(Unit::Day, 1).is_err());
723
        assert!(get(Unit::Day, -1).is_err());
724
        assert!(get(Unit::Day, 0).is_err());
725
        assert!(get(Unit::Day, 2).is_err());
726
        assert!(get(Unit::Day, 4).is_err());
727
        assert!(get(Unit::Day, 7).is_err());
728
        assert!(get(Unit::Day, i64::MIN).is_err());
729
        assert!(get(Unit::Day, i64::MAX).is_err());
730
    }
731
732
    #[test]
733
    fn for_timestamp() {
734
        let get = |unit, value| Increment::for_timestamp(unit, value);
735
736
        assert!(get(Unit::Nanosecond, 1).is_ok());
737
        assert!(get(Unit::Nanosecond, 500).is_ok());
738
        assert!(get(Unit::Nanosecond, 1_000).is_ok());
739
        assert!(get(Unit::Nanosecond, 1_000_000_000).is_ok());
740
        assert!(get(Unit::Nanosecond, -1).is_err());
741
        assert!(get(Unit::Nanosecond, 0).is_err());
742
        assert!(get(Unit::Nanosecond, 7).is_err());
743
        assert!(get(Unit::Nanosecond, 1_000_000_001).is_err());
744
        assert!(get(Unit::Nanosecond, i64::MIN).is_err());
745
        assert!(get(Unit::Nanosecond, i64::MAX).is_err());
746
747
        assert!(get(Unit::Microsecond, 1).is_ok());
748
        assert!(get(Unit::Microsecond, 500).is_ok());
749
        assert!(get(Unit::Microsecond, 1_000).is_ok());
750
        assert!(get(Unit::Microsecond, 2_000).is_ok());
751
        assert!(get(Unit::Microsecond, -1).is_err());
752
        assert!(get(Unit::Microsecond, 0).is_err());
753
        assert!(get(Unit::Microsecond, 7).is_err());
754
        assert!(get(Unit::Microsecond, 1_000_000_000).is_err());
755
        assert!(get(Unit::Microsecond, 1_000_000_001).is_err());
756
        assert!(get(Unit::Microsecond, i64::MIN).is_err());
757
        assert!(get(Unit::Microsecond, i64::MAX).is_err());
758
759
        assert!(get(Unit::Millisecond, 1).is_ok());
760
        assert!(get(Unit::Millisecond, 500).is_ok());
761
        assert!(get(Unit::Millisecond, 1_000).is_ok());
762
        assert!(get(Unit::Millisecond, 2_000).is_ok());
763
        assert!(get(Unit::Millisecond, 86_400_000).is_ok());
764
        assert!(get(Unit::Millisecond, -1).is_err());
765
        assert!(get(Unit::Millisecond, 0).is_err());
766
        assert!(get(Unit::Millisecond, 7).is_err());
767
        assert!(get(Unit::Millisecond, 1_000_000_000).is_err());
768
        assert!(get(Unit::Millisecond, 1_000_000_001).is_err());
769
        assert!(get(Unit::Millisecond, i64::MIN).is_err());
770
        assert!(get(Unit::Millisecond, i64::MAX).is_err());
771
772
        assert!(get(Unit::Second, 1).is_ok());
773
        assert!(get(Unit::Second, 15).is_ok());
774
        assert!(get(Unit::Second, 3_600).is_ok());
775
        assert!(get(Unit::Second, 86_400).is_ok());
776
        assert!(get(Unit::Second, -1).is_err());
777
        assert!(get(Unit::Second, 0).is_err());
778
        assert!(get(Unit::Second, 7).is_err());
779
        assert!(get(Unit::Second, 1_000).is_err());
780
        assert!(get(Unit::Second, 86_401).is_err());
781
        assert!(get(Unit::Second, 172_800).is_err());
782
        assert!(get(Unit::Second, 1_000_000_000).is_err());
783
        assert!(get(Unit::Second, 1_000_000_001).is_err());
784
        assert!(get(Unit::Second, i64::MIN).is_err());
785
        assert!(get(Unit::Second, i64::MAX).is_err());
786
787
        assert!(get(Unit::Minute, 1).is_ok());
788
        assert!(get(Unit::Minute, 15).is_ok());
789
        assert!(get(Unit::Minute, 1_440).is_ok());
790
        assert!(get(Unit::Minute, -1).is_err());
791
        assert!(get(Unit::Minute, 0).is_err());
792
        assert!(get(Unit::Minute, 7).is_err());
793
        assert!(get(Unit::Minute, 1_000).is_err());
794
        assert!(get(Unit::Minute, 1_441).is_err());
795
        assert!(get(Unit::Minute, 2_880).is_err());
796
        assert!(get(Unit::Minute, 1_000_000_000).is_err());
797
        assert!(get(Unit::Minute, 1_000_000_001).is_err());
798
        assert!(get(Unit::Minute, i64::MIN).is_err());
799
        assert!(get(Unit::Minute, i64::MAX).is_err());
800
801
        assert!(get(Unit::Hour, 1).is_ok());
802
        assert!(get(Unit::Hour, 6).is_ok());
803
        assert!(get(Unit::Hour, 12).is_ok());
804
        assert!(get(Unit::Hour, 24).is_ok());
805
        assert!(get(Unit::Hour, -1).is_err());
806
        assert!(get(Unit::Hour, 0).is_err());
807
        assert!(get(Unit::Hour, 15).is_err());
808
        assert!(get(Unit::Hour, 18).is_err());
809
        assert!(get(Unit::Hour, 23).is_err());
810
        assert!(get(Unit::Hour, 25).is_err());
811
        assert!(get(Unit::Hour, 1_000_000_000).is_err());
812
        assert!(get(Unit::Hour, 1_000_000_001).is_err());
813
        assert!(get(Unit::Hour, i64::MIN).is_err());
814
        assert!(get(Unit::Hour, i64::MAX).is_err());
815
816
        assert!(get(Unit::Day, 1).is_err());
817
        assert!(get(Unit::Day, -1).is_err());
818
        assert!(get(Unit::Day, 0).is_err());
819
        assert!(get(Unit::Day, 2).is_err());
820
        assert!(get(Unit::Day, 4).is_err());
821
        assert!(get(Unit::Day, 7).is_err());
822
        assert!(get(Unit::Day, i64::MIN).is_err());
823
        assert!(get(Unit::Day, i64::MAX).is_err());
824
825
        assert!(get(Unit::Week, 1).is_err());
826
        assert!(get(Unit::Week, -1).is_err());
827
        assert!(get(Unit::Week, 0).is_err());
828
        assert!(get(Unit::Week, 2).is_err());
829
        assert!(get(Unit::Week, 4).is_err());
830
        assert!(get(Unit::Week, 7).is_err());
831
        assert!(get(Unit::Week, i64::MIN).is_err());
832
        assert!(get(Unit::Week, i64::MAX).is_err());
833
834
        assert!(get(Unit::Month, 1).is_err());
835
        assert!(get(Unit::Month, -1).is_err());
836
        assert!(get(Unit::Month, 0).is_err());
837
        assert!(get(Unit::Month, 2).is_err());
838
        assert!(get(Unit::Month, 4).is_err());
839
        assert!(get(Unit::Month, 7).is_err());
840
        assert!(get(Unit::Month, i64::MIN).is_err());
841
        assert!(get(Unit::Month, i64::MAX).is_err());
842
843
        assert!(get(Unit::Year, 1).is_err());
844
        assert!(get(Unit::Year, -1).is_err());
845
        assert!(get(Unit::Year, 0).is_err());
846
        assert!(get(Unit::Year, 2).is_err());
847
        assert!(get(Unit::Year, 4).is_err());
848
        assert!(get(Unit::Year, 7).is_err());
849
        assert!(get(Unit::Year, i64::MIN).is_err());
850
        assert!(get(Unit::Year, i64::MAX).is_err());
851
    }
852
853
    // Some ad hoc tests I wrote while writing the rounding increment code.
854
    #[test]
855
    fn round_to_increment_half_expand_ad_hoc() {
856
        let round = |quantity: i128, increment: i128| -> i128 {
857
            RoundMode::HalfExpand.round_by_i128(quantity, increment)
858
        };
859
        assert_eq!(26, round(20, 13));
860
861
        assert_eq!(0, round(29, 60));
862
        assert_eq!(60, round(30, 60));
863
        assert_eq!(60, round(31, 60));
864
865
        assert_eq!(0, round(3, 7));
866
        assert_eq!(7, round(4, 7));
867
    }
868
869
    // The Temporal tests are inspired by the table from here:
870
    // https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement
871
    //
872
    // The main difference is that our rounding function specifically does not
873
    // use floating point, so we tweak the values a bit.
874
875
    #[test]
876
    fn round_to_increment_temporal_table_ceil() {
877
        let round = |quantity: i128, increment: i128| -> i128 {
878
            RoundMode::Ceil.round_by_i128(quantity, increment)
879
        };
880
        assert_eq!(-10, round(-15, 10));
881
        assert_eq!(0, round(-5, 10));
882
        assert_eq!(10, round(4, 10));
883
        assert_eq!(10, round(5, 10));
884
        assert_eq!(10, round(6, 10));
885
        assert_eq!(20, round(15, 10));
886
887
        assert_eq!(i128::MAX, round(i128::MAX, 1));
888
        assert_eq!(i128::MAX, round(i128::MAX, i128::MAX));
889
890
        // TODO: This test currently panics on overflow.
891
        // We should overall scrutinize the input constraints
892
        // for our rounding routines and align with their call
893
        // sites. We were previously relying on ranged integers
894
        // to holistically verify we weren't doing anything that
895
        // could result in overflow. I suspect that we can't
896
        // actually support the i128 quantity range (and I don't
897
        // think we need to). The question is whether we should
898
        // make the rounding interface fallible or if this
899
        // should just document panicking conditions.
900
        //
901
        // Oh, we should introduce a new internal `Increment`
902
        // wrapper that provides guarantees about its range.
903
        // assert_eq!(i128::MIN, round(i128::MIN, -1));
904
        // assert_eq!(i128::MIN, round(i128::MIN, i128::MIN));
905
    }
906
907
    #[test]
908
    fn round_to_increment_temporal_table_floor() {
909
        let round = |quantity: i128, increment: i128| -> i128 {
910
            RoundMode::Floor.round_by_i128(quantity, increment)
911
        };
912
        assert_eq!(-20, round(-15, 10));
913
        assert_eq!(-10, round(-5, 10));
914
        assert_eq!(0, round(4, 10));
915
        assert_eq!(0, round(5, 10));
916
        assert_eq!(0, round(6, 10));
917
        assert_eq!(10, round(15, 10));
918
    }
919
920
    #[test]
921
    fn round_to_increment_temporal_table_expand() {
922
        let round = |quantity: i128, increment: i128| -> i128 {
923
            RoundMode::Expand.round_by_i128(quantity, increment)
924
        };
925
        assert_eq!(-20, round(-15, 10));
926
        assert_eq!(-10, round(-5, 10));
927
        assert_eq!(10, round(4, 10));
928
        assert_eq!(10, round(5, 10));
929
        assert_eq!(10, round(6, 10));
930
        assert_eq!(20, round(15, 10));
931
    }
932
933
    #[test]
934
    fn round_to_increment_temporal_table_trunc() {
935
        let round = |quantity: i128, increment: i128| -> i128 {
936
            RoundMode::Trunc.round_by_i128(quantity, increment)
937
        };
938
        assert_eq!(-10, round(-15, 10));
939
        assert_eq!(0, round(-5, 10));
940
        assert_eq!(0, round(4, 10));
941
        assert_eq!(0, round(5, 10));
942
        assert_eq!(0, round(6, 10));
943
        assert_eq!(10, round(15, 10));
944
    }
945
946
    #[test]
947
    fn round_to_increment_temporal_table_half_ceil() {
948
        let round = |quantity: i128, increment: i128| -> i128 {
949
            RoundMode::HalfCeil.round_by_i128(quantity, increment)
950
        };
951
        assert_eq!(-10, round(-15, 10));
952
        assert_eq!(0, round(-5, 10));
953
        assert_eq!(0, round(4, 10));
954
        assert_eq!(10, round(5, 10));
955
        assert_eq!(10, round(6, 10));
956
        assert_eq!(20, round(15, 10));
957
    }
958
959
    #[test]
960
    fn round_to_increment_temporal_table_half_floor() {
961
        let round = |quantity: i128, increment: i128| -> i128 {
962
            RoundMode::HalfFloor.round_by_i128(quantity, increment)
963
        };
964
        assert_eq!(-20, round(-15, 10));
965
        assert_eq!(-10, round(-5, 10));
966
        assert_eq!(0, round(4, 10));
967
        assert_eq!(0, round(5, 10));
968
        assert_eq!(10, round(6, 10));
969
        assert_eq!(10, round(15, 10));
970
    }
971
972
    #[test]
973
    fn round_to_increment_temporal_table_half_expand() {
974
        let round = |quantity: i128, increment: i128| -> i128 {
975
            RoundMode::HalfExpand.round_by_i128(quantity, increment)
976
        };
977
        assert_eq!(-20, round(-15, 10));
978
        assert_eq!(-10, round(-5, 10));
979
        assert_eq!(0, round(4, 10));
980
        assert_eq!(10, round(5, 10));
981
        assert_eq!(10, round(6, 10));
982
        assert_eq!(20, round(15, 10));
983
    }
984
985
    #[test]
986
    fn round_to_increment_temporal_table_half_trunc() {
987
        let round = |quantity: i128, increment: i128| -> i128 {
988
            RoundMode::HalfTrunc.round_by_i128(quantity, increment)
989
        };
990
        assert_eq!(-10, round(-15, 10));
991
        assert_eq!(0, round(-5, 10));
992
        assert_eq!(0, round(4, 10));
993
        assert_eq!(0, round(5, 10));
994
        assert_eq!(10, round(6, 10));
995
        assert_eq!(10, round(15, 10));
996
    }
997
998
    #[test]
999
    fn round_to_increment_temporal_table_half_even() {
1000
        let round = |quantity: i128, increment: i128| -> i128 {
1001
            RoundMode::HalfEven.round_by_i128(quantity, increment)
1002
        };
1003
        assert_eq!(-20, round(-15, 10));
1004
        assert_eq!(0, round(-5, 10));
1005
        assert_eq!(0, round(4, 10));
1006
        assert_eq!(0, round(5, 10));
1007
        assert_eq!(10, round(6, 10));
1008
        assert_eq!(20, round(15, 10));
1009
    }
1010
1011
    // Tests that the maximum possible increment, in units of nanoseconds,
1012
    // fits into a `SignedDuration`.
1013
    #[test]
1014
    fn maximum_increment_nanos() {
1015
        assert!(Increment::for_span(Unit::Week, 1_000_000_000).is_ok());
1016
        assert!(Increment::for_span(Unit::Week, 1_000_000_001).is_err());
1017
        assert_eq!(
1018
            Unit::Week.duration() * 1_000_000_000,
1019
            SignedDuration::from_secs(168_000_000_000 * 60 * 60)
1020
        );
1021
    }
1022
1023
    // Tests some edge cases where rounding can actually overflow. We ensure
1024
    // that an error is returned instead of panicking.
1025
    //
1026
    // I do not believe it's possible for these overflows to be observed using
1027
    // Jiff's public API however.
1028
    #[test]
1029
    fn round_by_duration_overflow() {
1030
        let mode = RoundMode::Expand;
1031
        let quantity = SignedDuration::MAX;
1032
        let increment = SignedDuration::MAX - SignedDuration::from_secs(1);
1033
        assert!(mode.round_by_duration(quantity, increment).is_err());
1034
1035
        let mode = RoundMode::Expand;
1036
        let quantity = SignedDuration::MIN;
1037
        let increment = SignedDuration::MAX;
1038
        assert!(mode.round_by_duration(quantity, increment).is_err());
1039
    }
1040
}