Coverage Report

Created: 2025-09-27 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/jiff-0.2.15/src/span.rs
Line
Count
Source
1
use core::{cmp::Ordering, time::Duration as UnsignedDuration};
2
3
use crate::{
4
    civil::{Date, DateTime, Time},
5
    duration::{Duration, SDuration},
6
    error::{err, Error, ErrorContext},
7
    fmt::{friendly, temporal},
8
    tz::TimeZone,
9
    util::{
10
        borrow::DumbCow,
11
        escape,
12
        rangeint::{ri64, ri8, RFrom, RInto, TryRFrom, TryRInto},
13
        round::increment,
14
        t::{self, Constant, NoUnits, NoUnits128, Sign, C},
15
    },
16
    RoundMode, SignedDuration, Timestamp, Zoned,
17
};
18
19
/// A macro helper, only used in tests, for comparing spans for equality.
20
#[cfg(test)]
21
macro_rules! span_eq {
22
    ($span1:expr, $span2:expr $(,)?) => {{
23
        assert_eq!($span1.fieldwise(), $span2.fieldwise());
24
    }};
25
    ($span1:expr, $span2:expr, $($tt:tt)*) => {{
26
        assert_eq!($span1.fieldwise(), $span2.fieldwise(), $($tt)*);
27
    }};
28
}
29
30
#[cfg(test)]
31
pub(crate) use span_eq;
32
33
/// A span of time represented via a mixture of calendar and clock units.
34
///
35
/// A span represents a duration of time in units of years, months, weeks,
36
/// days, hours, minutes, seconds, milliseconds, microseconds and nanoseconds.
37
/// Spans are used to as inputs to routines like
38
/// [`Zoned::checked_add`] and [`Date::saturating_sub`],
39
/// and are also outputs from routines like
40
/// [`Timestamp::since`] and [`DateTime::until`].
41
///
42
/// # Range of spans
43
///
44
/// Except for nanoseconds, each unit can represent the full span of time
45
/// expressible via any combination of datetime supported by Jiff. For example:
46
///
47
/// ```
48
/// use jiff::{civil::{DateTime, DateTimeDifference}, ToSpan, Unit};
49
///
50
/// let options = DateTimeDifference::new(DateTime::MAX).largest(Unit::Year);
51
/// assert_eq!(DateTime::MIN.until(options)?.get_years(), 19_998);
52
///
53
/// let options = options.largest(Unit::Day);
54
/// assert_eq!(DateTime::MIN.until(options)?.get_days(), 7_304_483);
55
///
56
/// let options = options.largest(Unit::Microsecond);
57
/// assert_eq!(
58
///     DateTime::MIN.until(options)?.get_microseconds(),
59
///     631_107_417_599_999_999i64,
60
/// );
61
///
62
/// let options = options.largest(Unit::Nanosecond);
63
/// // Span is too big, overflow!
64
/// assert!(DateTime::MIN.until(options).is_err());
65
///
66
/// # Ok::<(), Box<dyn std::error::Error>>(())
67
/// ```
68
///
69
/// # Building spans
70
///
71
/// A default or empty span corresponds to a duration of zero time:
72
///
73
/// ```
74
/// use jiff::Span;
75
///
76
/// assert!(Span::new().is_zero());
77
/// assert!(Span::default().is_zero());
78
/// ```
79
///
80
/// Spans are `Copy` types that have mutator methods on them for creating new
81
/// spans:
82
///
83
/// ```
84
/// use jiff::Span;
85
///
86
/// let span = Span::new().days(5).hours(8).minutes(1);
87
/// assert_eq!(span.to_string(), "P5DT8H1M");
88
/// ```
89
///
90
/// But Jiff provides a [`ToSpan`] trait that defines extension methods on
91
/// primitive signed integers to make span creation terser:
92
///
93
/// ```
94
/// use jiff::ToSpan;
95
///
96
/// let span = 5.days().hours(8).minutes(1);
97
/// assert_eq!(span.to_string(), "P5DT8H1M");
98
/// // singular units on integers can be used too:
99
/// let span = 1.day().hours(8).minutes(1);
100
/// assert_eq!(span.to_string(), "P1DT8H1M");
101
/// ```
102
///
103
/// # Negative spans
104
///
105
/// A span may be negative. All of these are equivalent:
106
///
107
/// ```
108
/// use jiff::{Span, ToSpan};
109
///
110
/// let span = -Span::new().days(5);
111
/// assert_eq!(span.to_string(), "-P5D");
112
///
113
/// let span = Span::new().days(5).negate();
114
/// assert_eq!(span.to_string(), "-P5D");
115
///
116
/// let span = Span::new().days(-5);
117
/// assert_eq!(span.to_string(), "-P5D");
118
///
119
/// let span = -Span::new().days(-5).negate();
120
/// assert_eq!(span.to_string(), "-P5D");
121
///
122
/// let span = -5.days();
123
/// assert_eq!(span.to_string(), "-P5D");
124
///
125
/// let span = (-5).days();
126
/// assert_eq!(span.to_string(), "-P5D");
127
///
128
/// let span = -(5.days());
129
/// assert_eq!(span.to_string(), "-P5D");
130
/// ```
131
///
132
/// The sign of a span applies to the entire span. When a span is negative,
133
/// then all of its units are negative:
134
///
135
/// ```
136
/// use jiff::ToSpan;
137
///
138
/// let span = -5.days().hours(10).minutes(1);
139
/// assert_eq!(span.get_days(), -5);
140
/// assert_eq!(span.get_hours(), -10);
141
/// assert_eq!(span.get_minutes(), -1);
142
/// ```
143
///
144
/// And if any of a span's units are negative, then the entire span is regarded
145
/// as negative:
146
///
147
/// ```
148
/// use jiff::ToSpan;
149
///
150
/// // It's the same thing.
151
/// let span = (-5).days().hours(-10).minutes(-1);
152
/// assert_eq!(span.get_days(), -5);
153
/// assert_eq!(span.get_hours(), -10);
154
/// assert_eq!(span.get_minutes(), -1);
155
///
156
/// // Still the same. All negative.
157
/// let span = 5.days().hours(-10).minutes(1);
158
/// assert_eq!(span.get_days(), -5);
159
/// assert_eq!(span.get_hours(), -10);
160
/// assert_eq!(span.get_minutes(), -1);
161
///
162
/// // But this is not! The negation in front applies
163
/// // to the entire span, which was already negative
164
/// // by virtue of at least one of its units being
165
/// // negative. So the negation operator in front turns
166
/// // the span positive.
167
/// let span = -5.days().hours(-10).minutes(-1);
168
/// assert_eq!(span.get_days(), 5);
169
/// assert_eq!(span.get_hours(), 10);
170
/// assert_eq!(span.get_minutes(), 1);
171
/// ```
172
///
173
/// You can also ask for the absolute value of a span:
174
///
175
/// ```
176
/// use jiff::Span;
177
///
178
/// let span = Span::new().days(5).hours(10).minutes(1).negate().abs();
179
/// assert_eq!(span.get_days(), 5);
180
/// assert_eq!(span.get_hours(), 10);
181
/// assert_eq!(span.get_minutes(), 1);
182
/// ```
183
///
184
/// # Parsing and printing
185
///
186
/// The `Span` type provides convenient trait implementations of
187
/// [`std::str::FromStr`] and [`std::fmt::Display`]:
188
///
189
/// ```
190
/// use jiff::{Span, ToSpan};
191
///
192
/// let span: Span = "P2m10dT2h30m".parse()?;
193
/// // By default, capital unit designator labels are used.
194
/// // This can be changed with `jiff::fmt::temporal::SpanPrinter::lowercase`.
195
/// assert_eq!(span.to_string(), "P2M10DT2H30M");
196
///
197
/// // Or use the "friendly" format by invoking the `Display` alternate:
198
/// assert_eq!(format!("{span:#}"), "2mo 10d 2h 30m");
199
///
200
/// // Parsing automatically supports both the ISO 8601 and "friendly"
201
/// // formats. Note that we use `Span::fieldwise` to create a `Span` that
202
/// // compares based on each field. To compare based on total duration, use
203
/// // `Span::compare` or `Span::total`.
204
/// let span: Span = "2mo 10d 2h 30m".parse()?;
205
/// assert_eq!(span, 2.months().days(10).hours(2).minutes(30).fieldwise());
206
/// let span: Span = "2 months, 10 days, 2 hours, 30 minutes".parse()?;
207
/// assert_eq!(span, 2.months().days(10).hours(2).minutes(30).fieldwise());
208
///
209
/// # Ok::<(), Box<dyn std::error::Error>>(())
210
/// ```
211
///
212
/// The format supported is a variation (nearly a subset) of the duration
213
/// format specified in [ISO 8601] _and_ a Jiff-specific "friendly" format.
214
/// Here are more examples:
215
///
216
/// ```
217
/// use jiff::{Span, ToSpan};
218
///
219
/// let spans = [
220
///     // ISO 8601
221
///     ("P40D", 40.days()),
222
///     ("P1y1d", 1.year().days(1)),
223
///     ("P3dT4h59m", 3.days().hours(4).minutes(59)),
224
///     ("PT2H30M", 2.hours().minutes(30)),
225
///     ("P1m", 1.month()),
226
///     ("P1w", 1.week()),
227
///     ("P1w4d", 1.week().days(4)),
228
///     ("PT1m", 1.minute()),
229
///     ("PT0.0021s", 2.milliseconds().microseconds(100)),
230
///     ("PT0s", 0.seconds()),
231
///     ("P0d", 0.seconds()),
232
///     (
233
///         "P1y1m1dT1h1m1.1s",
234
///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
235
///     ),
236
///     // Jiff's "friendly" format
237
///     ("40d", 40.days()),
238
///     ("40 days", 40.days()),
239
///     ("1y1d", 1.year().days(1)),
240
///     ("1yr 1d", 1.year().days(1)),
241
///     ("3d4h59m", 3.days().hours(4).minutes(59)),
242
///     ("3 days, 4 hours, 59 minutes", 3.days().hours(4).minutes(59)),
243
///     ("3d 4h 59m", 3.days().hours(4).minutes(59)),
244
///     ("2h30m", 2.hours().minutes(30)),
245
///     ("2h 30m", 2.hours().minutes(30)),
246
///     ("1mo", 1.month()),
247
///     ("1w", 1.week()),
248
///     ("1 week", 1.week()),
249
///     ("1w4d", 1.week().days(4)),
250
///     ("1 wk 4 days", 1.week().days(4)),
251
///     ("1m", 1.minute()),
252
///     ("0.0021s", 2.milliseconds().microseconds(100)),
253
///     ("0s", 0.seconds()),
254
///     ("0d", 0.seconds()),
255
///     ("0 days", 0.seconds()),
256
///     (
257
///         "1y1mo1d1h1m1.1s",
258
///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
259
///     ),
260
///     (
261
///         "1yr 1mo 1day 1hr 1min 1.1sec",
262
///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
263
///     ),
264
///     (
265
///         "1 year, 1 month, 1 day, 1 hour, 1 minute 1.1 seconds",
266
///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
267
///     ),
268
///     (
269
///         "1 year, 1 month, 1 day, 01:01:01.1",
270
///         1.year().months(1).days(1).hours(1).minutes(1).seconds(1).milliseconds(100),
271
///     ),
272
/// ];
273
/// for (string, span) in spans {
274
///     let parsed: Span = string.parse()?;
275
///     assert_eq!(
276
///         span.fieldwise(),
277
///         parsed.fieldwise(),
278
///         "result of parsing {string:?}",
279
///     );
280
/// }
281
///
282
/// # Ok::<(), Box<dyn std::error::Error>>(())
283
/// ```
284
///
285
/// For more details, see the [`fmt::temporal`](temporal) and
286
/// [`fmt::friendly`](friendly) modules.
287
///
288
/// [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html
289
///
290
/// # Comparisons
291
///
292
/// A `Span` does not implement the `PartialEq` or `Eq` traits. These traits
293
/// were implemented in an earlier version of Jiff, but they made it too
294
/// easy to introduce bugs. For example, `120.minutes()` and `2.hours()`
295
/// always correspond to the same total duration, but they have different
296
/// representations in memory and so didn't compare equivalent.
297
///
298
/// The reason why the `PartialEq` and `Eq` trait implementations do not do
299
/// comparisons with total duration is because it is fundamentally impossible
300
/// to do such comparisons without a reference date in all cases.
301
///
302
/// However, it is undeniably occasionally useful to do comparisons based
303
/// on the component fields, so long as such use cases can tolerate two
304
/// different spans comparing unequal even when their total durations are
305
/// equivalent. For example, many of the tests in Jiff (including the tests in
306
/// the documentation) work by comparing a `Span` to an expected result. This
307
/// is a good demonstration of when fieldwise comparisons are appropriate.
308
///
309
/// To do fieldwise comparisons with a span, use the [`Span::fieldwise`]
310
/// method. This method creates a [`SpanFieldwise`], which is just a `Span`
311
/// that implements `PartialEq` and `Eq` in a fieldwise manner. In other words,
312
/// it's a speed bump to ensure this is the kind of comparison you actually
313
/// want. For example:
314
///
315
/// ```
316
/// use jiff::ToSpan;
317
///
318
/// assert_ne!(1.hour().fieldwise(), 60.minutes().fieldwise());
319
/// // These also work since you only need one fieldwise span to do a compare:
320
/// assert_ne!(1.hour(), 60.minutes().fieldwise());
321
/// assert_ne!(1.hour().fieldwise(), 60.minutes());
322
/// ```
323
///
324
/// This is because doing true comparisons requires arithmetic and a relative
325
/// datetime in the general case, and which can fail due to overflow. This
326
/// operation is provided via [`Span::compare`]:
327
///
328
/// ```
329
/// use jiff::{civil::date, ToSpan};
330
///
331
/// // This doesn't need a reference date since it's only using time units.
332
/// assert_eq!(1.hour().compare(60.minutes())?, std::cmp::Ordering::Equal);
333
/// // But if you have calendar units, then you need a
334
/// // reference date at minimum:
335
/// assert!(1.month().compare(30.days()).is_err());
336
/// assert_eq!(
337
///     1.month().compare((30.days(), date(2025, 6, 1)))?,
338
///     std::cmp::Ordering::Equal,
339
/// );
340
/// // A month can be a differing number of days!
341
/// assert_eq!(
342
///     1.month().compare((30.days(), date(2025, 7, 1)))?,
343
///     std::cmp::Ordering::Greater,
344
/// );
345
///
346
/// # Ok::<(), Box<dyn std::error::Error>>(())
347
/// ```
348
///
349
/// # Arithmetic
350
///
351
/// Spans can be added or subtracted via [`Span::checked_add`] and
352
/// [`Span::checked_sub`]:
353
///
354
/// ```
355
/// use jiff::{Span, ToSpan};
356
///
357
/// let span1 = 2.hours().minutes(20);
358
/// let span2: Span = "PT89400s".parse()?;
359
/// assert_eq!(span1.checked_add(span2)?, 27.hours().minutes(10).fieldwise());
360
///
361
/// # Ok::<(), Box<dyn std::error::Error>>(())
362
/// ```
363
///
364
/// When your spans involve calendar units, a relative datetime must be
365
/// provided. (Because, for example, 1 month from March 1 is 31 days, but
366
/// 1 month from April 1 is 30 days.)
367
///
368
/// ```
369
/// use jiff::{civil::date, Span, ToSpan};
370
///
371
/// let span1 = 2.years().months(6).days(20);
372
/// let span2 = 400.days();
373
/// assert_eq!(
374
///     span1.checked_add((span2, date(2023, 1, 1)))?,
375
///     3.years().months(7).days(24).fieldwise(),
376
/// );
377
/// // The span changes when a leap year isn't included!
378
/// assert_eq!(
379
///     span1.checked_add((span2, date(2025, 1, 1)))?,
380
///     3.years().months(7).days(23).fieldwise(),
381
/// );
382
///
383
/// # Ok::<(), Box<dyn std::error::Error>>(())
384
/// ```
385
///
386
/// # Rounding and balancing
387
///
388
/// Unlike datetimes, multiple distinct `Span` values can actually correspond
389
/// to the same duration of time. For example, all of the following correspond
390
/// to the same duration:
391
///
392
/// * 2 hours, 30 minutes
393
/// * 150 minutes
394
/// * 1 hour, 90 minutes
395
///
396
/// The first is said to be balanced. That is, its biggest non-zero unit cannot
397
/// be expressed in an integer number of units bigger than hours. But the
398
/// second is unbalanced because 150 minutes can be split up into hours and
399
/// minutes. We call this sort of span a "top-heavy" unbalanced span. The third
400
/// span is also unbalanced, but it's "bottom-heavy" and rarely used. Jiff
401
/// will generally only produce spans of the first two types. In particular,
402
/// most `Span` producing APIs accept a "largest" [`Unit`] parameter, and the
403
/// result can be said to be a span "balanced up to the largest unit provided."
404
///
405
/// Balanced and unbalanced spans can be switched between as needed via
406
/// the [`Span::round`] API by providing a rounding configuration with
407
/// [`SpanRound::largest`]` set:
408
///
409
/// ```
410
/// use jiff::{SpanRound, ToSpan, Unit};
411
///
412
/// let span = 2.hours().minutes(30);
413
/// let unbalanced = span.round(SpanRound::new().largest(Unit::Minute))?;
414
/// assert_eq!(unbalanced, 150.minutes().fieldwise());
415
/// let balanced = unbalanced.round(SpanRound::new().largest(Unit::Hour))?;
416
/// assert_eq!(balanced, 2.hours().minutes(30).fieldwise());
417
///
418
/// # Ok::<(), Box<dyn std::error::Error>>(())
419
/// ```
420
///
421
/// Balancing can also be done as part of computing spans from two datetimes:
422
///
423
/// ```
424
/// use jiff::{civil::date, ToSpan, Unit};
425
///
426
/// let zdt1 = date(2024, 7, 7).at(15, 23, 0, 0).in_tz("America/New_York")?;
427
/// let zdt2 = date(2024, 11, 5).at(8, 0, 0, 0).in_tz("America/New_York")?;
428
///
429
/// // To make arithmetic reversible, the default largest unit for spans of
430
/// // time computed from zoned datetimes is hours:
431
/// assert_eq!(zdt1.until(&zdt2)?, 2_897.hour().minutes(37).fieldwise());
432
/// // But we can ask for the span to be balanced up to years:
433
/// assert_eq!(
434
///     zdt1.until((Unit::Year, &zdt2))?,
435
///     3.months().days(28).hours(16).minutes(37).fieldwise(),
436
/// );
437
///
438
/// # Ok::<(), Box<dyn std::error::Error>>(())
439
/// ```
440
///
441
/// While the [`Span::round`] API does balancing, it also, of course, does
442
/// rounding as well. Rounding occurs when the smallest unit is set to
443
/// something bigger than [`Unit::Nanosecond`]:
444
///
445
/// ```
446
/// use jiff::{ToSpan, Unit};
447
///
448
/// let span = 2.hours().minutes(30);
449
/// assert_eq!(span.round(Unit::Hour)?, 3.hours().fieldwise());
450
///
451
/// # Ok::<(), Box<dyn std::error::Error>>(())
452
/// ```
453
///
454
/// When rounding spans with calendar units (years, months or weeks), then a
455
/// relative datetime is required:
456
///
457
/// ```
458
/// use jiff::{civil::date, SpanRound, ToSpan, Unit};
459
///
460
/// let span = 10.years().months(11);
461
/// let options = SpanRound::new()
462
///     .smallest(Unit::Year)
463
///     .relative(date(2024, 1, 1));
464
/// assert_eq!(span.round(options)?, 11.years().fieldwise());
465
///
466
/// # Ok::<(), Box<dyn std::error::Error>>(())
467
/// ```
468
///
469
/// # Days are not always 24 hours!
470
///
471
/// That is, a `Span` is made up of uniform and non-uniform units.
472
///
473
/// A uniform unit is a unit whose elapsed duration is always the same.
474
/// A non-uniform unit is a unit whose elapsed duration is not always the same.
475
/// There are two things that can impact the length of a non-uniform unit:
476
/// the calendar date and the time zone.
477
///
478
/// Years and months are always considered non-uniform units. For example,
479
/// 1 month from `2024-04-01` is 30 days, while 1 month from `2024-05-01` is
480
/// 31 days. Similarly for years because of leap years.
481
///
482
/// Hours, minutes, seconds, milliseconds, microseconds and nanoseconds are
483
/// always considered uniform units.
484
///
485
/// Days are only considered non-uniform when in the presence of a zone aware
486
/// datetime. A day can be more or less than 24 hours, and it can be balanced
487
/// up and down, but only when a relative zoned datetime is given. This
488
/// typically happens because of DST (daylight saving time), but can also occur
489
/// because of other time zone transitions too.
490
///
491
/// ```
492
/// use jiff::{civil::date, SpanRound, ToSpan, Unit};
493
///
494
/// // 2024-03-10 in New York was 23 hours long,
495
/// // because of a jump to DST at 2am.
496
/// let zdt = date(2024, 3, 9).at(21, 0, 0, 0).in_tz("America/New_York")?;
497
/// // Goes from days to hours:
498
/// assert_eq!(
499
///     1.day().round(SpanRound::new().largest(Unit::Hour).relative(&zdt))?,
500
///     23.hours().fieldwise(),
501
/// );
502
/// // Goes from hours to days:
503
/// assert_eq!(
504
///     23.hours().round(SpanRound::new().largest(Unit::Day).relative(&zdt))?,
505
///     1.day().fieldwise(),
506
/// );
507
/// // 24 hours is more than 1 day starting at this time:
508
/// assert_eq!(
509
///     24.hours().round(SpanRound::new().largest(Unit::Day).relative(&zdt))?,
510
///     1.day().hours(1).fieldwise(),
511
/// );
512
///
513
/// # Ok::<(), Box<dyn std::error::Error>>(())
514
/// ```
515
///
516
/// And similarly, days can be longer than 24 hours:
517
///
518
/// ```
519
/// use jiff::{civil::date, SpanRound, ToSpan, Unit};
520
///
521
/// // 2024-11-03 in New York was 25 hours long,
522
/// // because of a repetition of the 1 o'clock AM hour.
523
/// let zdt = date(2024, 11, 2).at(21, 0, 0, 0).in_tz("America/New_York")?;
524
/// // Goes from days to hours:
525
/// assert_eq!(
526
///     1.day().round(SpanRound::new().largest(Unit::Hour).relative(&zdt))?,
527
///     25.hours().fieldwise(),
528
/// );
529
/// // Goes from hours to days:
530
/// assert_eq!(
531
///     25.hours().round(SpanRound::new().largest(Unit::Day).relative(&zdt))?,
532
///     1.day().fieldwise(),
533
/// );
534
/// // 24 hours is less than 1 day starting at this time,
535
/// // so it stays in units of hours even though we ask
536
/// // for days (because 24 isn't enough hours to make
537
/// // 1 day):
538
/// assert_eq!(
539
///     24.hours().round(SpanRound::new().largest(Unit::Day).relative(&zdt))?,
540
///     24.hours().fieldwise(),
541
/// );
542
///
543
/// # Ok::<(), Box<dyn std::error::Error>>(())
544
/// ```
545
///
546
/// The APIs on `Span` will otherwise treat days as non-uniform unless a
547
/// relative civil date is given, or there is an explicit opt-in to invariant
548
/// 24-hour days. For example:
549
///
550
/// ```
551
/// use jiff::{civil, SpanRelativeTo, ToSpan, Unit};
552
///
553
/// let span = 1.day();
554
///
555
/// // An error because days aren't always 24 hours:
556
/// assert_eq!(
557
///     span.total(Unit::Hour).unwrap_err().to_string(),
558
///     "using unit 'day' in a span or configuration requires that either \
559
///      a relative reference time be given or \
560
///      `SpanRelativeTo::days_are_24_hours()` is used to indicate \
561
///      invariant 24-hour days, but neither were provided",
562
/// );
563
/// // Opt into invariant 24 hour days without a relative date:
564
/// let marker = SpanRelativeTo::days_are_24_hours();
565
/// let hours = span.total((Unit::Hour, marker))?;
566
/// // Or use a relative civil date, and all days are 24 hours:
567
/// let date = civil::date(2020, 1, 1);
568
/// let hours = span.total((Unit::Hour, date))?;
569
/// assert_eq!(hours, 24.0);
570
///
571
/// # Ok::<(), Box<dyn std::error::Error>>(())
572
/// ```
573
///
574
/// In Jiff, all weeks are 7 days. And generally speaking, weeks only appear in
575
/// a `Span` if they were explicitly put there by the caller or if they were
576
/// explicitly requested by the caller in an API. For example:
577
///
578
/// ```
579
/// use jiff::{civil::date, ToSpan, Unit};
580
///
581
/// let dt1 = date(2024, 1, 1).at(0, 0, 0, 0);
582
/// let dt2 = date(2024, 7, 16).at(0, 0, 0, 0);
583
/// // Default units go up to days.
584
/// assert_eq!(dt1.until(dt2)?, 197.days().fieldwise());
585
/// // No weeks, even though we requested up to year.
586
/// assert_eq!(dt1.until((Unit::Year, dt2))?, 6.months().days(15).fieldwise());
587
/// // We get weeks only when we ask for them.
588
/// assert_eq!(dt1.until((Unit::Week, dt2))?, 28.weeks().days(1).fieldwise());
589
///
590
/// # Ok::<(), Box<dyn std::error::Error>>(())
591
/// ```
592
///
593
/// # Integration with [`std::time::Duration`] and [`SignedDuration`]
594
///
595
/// While Jiff primarily uses a `Span` for doing arithmetic on datetimes,
596
/// one can convert between a `Span` and a [`std::time::Duration`] or a
597
/// [`SignedDuration`]. The main difference between them is that a `Span`
598
/// always keeps tracks of its individual units, and a `Span` can represent
599
/// non-uniform units like months. In contrast, `Duration` and `SignedDuration`
600
/// are always an exact elapsed amount of time. They don't distinguish between
601
/// `120 seconds` and `2 minutes`. And they can't represent the concept of
602
/// "months" because a month doesn't have a single fixed amount of time.
603
///
604
/// However, an exact duration is still useful in certain contexts. Beyond
605
/// that, it serves as an interoperability point due to the presence of an
606
/// unsigned exact duration type in the standard library. Because of that,
607
/// Jiff provides `TryFrom` trait implementations for converting to and from a
608
/// `std::time::Duration` (and, of course, a `SignedDuration`). For example, to
609
/// convert from a `std::time::Duration` to a `Span`:
610
///
611
/// ```
612
/// use std::time::Duration;
613
///
614
/// use jiff::{Span, ToSpan};
615
///
616
/// let duration = Duration::new(86_400, 123_456_789);
617
/// let span = Span::try_from(duration)?;
618
/// // A duration-to-span conversion always results in a span with
619
/// // non-zero units no bigger than seconds.
620
/// assert_eq!(
621
///     span.fieldwise(),
622
///     86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
623
/// );
624
///
625
/// // Note that the conversion is fallible! For example:
626
/// assert!(Span::try_from(Duration::from_secs(u64::MAX)).is_err());
627
/// // At present, a Jiff `Span` can only represent a range of time equal to
628
/// // the range of time expressible via minimum and maximum Jiff timestamps.
629
/// // Which is roughly -9999-01-01 to 9999-12-31, or ~20,000 years.
630
/// assert!(Span::try_from(Duration::from_secs(999_999_999_999)).is_err());
631
///
632
/// # Ok::<(), Box<dyn std::error::Error>>(())
633
/// ```
634
///
635
/// And to convert from a `Span` to a `std::time::Duration`:
636
///
637
/// ```
638
/// use std::time::Duration;
639
///
640
/// use jiff::{Span, ToSpan};
641
///
642
/// let span = 86_400.seconds()
643
///     .milliseconds(123)
644
///     .microseconds(456)
645
///     .nanoseconds(789);
646
/// let duration = Duration::try_from(span)?;
647
/// assert_eq!(duration, Duration::new(86_400, 123_456_789));
648
///
649
/// # Ok::<(), Box<dyn std::error::Error>>(())
650
/// ```
651
///
652
/// Note that an error will occur when converting a `Span` to a
653
/// `std::time::Duration` using the `TryFrom` trait implementation with units
654
/// bigger than hours:
655
///
656
/// ```
657
/// use std::time::Duration;
658
///
659
/// use jiff::ToSpan;
660
///
661
/// let span = 2.days().hours(10);
662
/// assert_eq!(
663
///     Duration::try_from(span).unwrap_err().to_string(),
664
///     "failed to convert span to duration without relative datetime \
665
///      (must use `Span::to_duration` instead): using unit 'day' in a \
666
///      span or configuration requires that either a relative reference \
667
///      time be given or `SpanRelativeTo::days_are_24_hours()` is used \
668
///      to indicate invariant 24-hour days, but neither were provided",
669
/// );
670
///
671
/// # Ok::<(), Box<dyn std::error::Error>>(())
672
/// ```
673
///
674
/// Similar code can be written for `SignedDuration` as well.
675
///
676
/// If you need to convert such spans, then as the error suggests, you'll need
677
/// to use [`Span::to_duration`] with a relative date.
678
///
679
/// And note that since a `Span` is signed and a `std::time::Duration` is unsigned,
680
/// converting a negative `Span` to `std::time::Duration` will always fail. One can use
681
/// [`Span::signum`] to get the sign of the span and [`Span::abs`] to make the
682
/// span positive before converting it to a `Duration`:
683
///
684
/// ```
685
/// use std::time::Duration;
686
///
687
/// use jiff::{Span, ToSpan};
688
///
689
/// let span = -86_400.seconds().nanoseconds(1);
690
/// let (sign, duration) = (span.signum(), Duration::try_from(span.abs())?);
691
/// assert_eq!((sign, duration), (-1, Duration::new(86_400, 1)));
692
///
693
/// # Ok::<(), Box<dyn std::error::Error>>(())
694
/// ```
695
///
696
/// Or, consider using Jiff's own [`SignedDuration`] instead:
697
///
698
/// ```
699
/// # // See: https://github.com/rust-lang/rust/pull/121364
700
/// # #![allow(unknown_lints, ambiguous_negative_literals)]
701
/// use jiff::{SignedDuration, Span, ToSpan};
702
///
703
/// let span = -86_400.seconds().nanoseconds(1);
704
/// let duration = SignedDuration::try_from(span)?;
705
/// assert_eq!(duration, SignedDuration::new(-86_400, -1));
706
///
707
/// # Ok::<(), Box<dyn std::error::Error>>(())
708
/// ```
709
#[derive(Clone, Copy)]
710
pub struct Span {
711
    sign: Sign,
712
    units: UnitSet,
713
    years: t::SpanYears,
714
    months: t::SpanMonths,
715
    weeks: t::SpanWeeks,
716
    days: t::SpanDays,
717
    hours: t::SpanHours,
718
    minutes: t::SpanMinutes,
719
    seconds: t::SpanSeconds,
720
    milliseconds: t::SpanMilliseconds,
721
    microseconds: t::SpanMicroseconds,
722
    nanoseconds: t::SpanNanoseconds,
723
}
724
725
/// Infallible routines for setting units on a `Span`.
726
///
727
/// These are useful when the units are determined by the programmer or when
728
/// they have been validated elsewhere. In general, use these routines when
729
/// constructing an invalid `Span` should be considered a bug in the program.
730
impl Span {
731
    /// Creates a new span representing a zero duration. That is, a duration
732
    /// in which no time has passed.
733
5.20k
    pub fn new() -> Span {
734
5.20k
        Span::default()
735
5.20k
    }
736
737
    /// Set the number of years on this span. The value may be negative.
738
    ///
739
    /// The fallible version of this method is [`Span::try_years`].
740
    ///
741
    /// # Panics
742
    ///
743
    /// This panics when the number of years is too small or too big.
744
    /// The minimum value is `-19,998`.
745
    /// The maximum value is `19,998`.
746
    #[inline]
747
0
    pub fn years<I: Into<i64>>(self, years: I) -> Span {
748
0
        self.try_years(years).expect("value for years is out of bounds")
749
0
    }
750
751
    /// Set the number of months on this span. The value may be negative.
752
    ///
753
    /// The fallible version of this method is [`Span::try_months`].
754
    ///
755
    /// # Panics
756
    ///
757
    /// This panics when the number of months is too small or too big.
758
    /// The minimum value is `-239,976`.
759
    /// The maximum value is `239,976`.
760
    #[inline]
761
0
    pub fn months<I: Into<i64>>(self, months: I) -> Span {
762
0
        self.try_months(months).expect("value for months is out of bounds")
763
0
    }
764
765
    /// Set the number of weeks on this span. The value may be negative.
766
    ///
767
    /// The fallible version of this method is [`Span::try_weeks`].
768
    ///
769
    /// # Panics
770
    ///
771
    /// This panics when the number of weeks is too small or too big.
772
    /// The minimum value is `-1,043,497`.
773
    /// The maximum value is `1_043_497`.
774
    #[inline]
775
0
    pub fn weeks<I: Into<i64>>(self, weeks: I) -> Span {
776
0
        self.try_weeks(weeks).expect("value for weeks is out of bounds")
777
0
    }
778
779
    /// Set the number of days on this span. The value may be negative.
780
    ///
781
    /// The fallible version of this method is [`Span::try_days`].
782
    ///
783
    /// # Panics
784
    ///
785
    /// This panics when the number of days is too small or too big.
786
    /// The minimum value is `-7,304,484`.
787
    /// The maximum value is `7,304,484`.
788
    #[inline]
789
0
    pub fn days<I: Into<i64>>(self, days: I) -> Span {
790
0
        self.try_days(days).expect("value for days is out of bounds")
791
0
    }
792
793
    /// Set the number of hours on this span. The value may be negative.
794
    ///
795
    /// The fallible version of this method is [`Span::try_hours`].
796
    ///
797
    /// # Panics
798
    ///
799
    /// This panics when the number of hours is too small or too big.
800
    /// The minimum value is `-175,307,616`.
801
    /// The maximum value is `175,307,616`.
802
    #[inline]
803
0
    pub fn hours<I: Into<i64>>(self, hours: I) -> Span {
804
0
        self.try_hours(hours).expect("value for hours is out of bounds")
805
0
    }
806
807
    /// Set the number of minutes on this span. The value may be negative.
808
    ///
809
    /// The fallible version of this method is [`Span::try_minutes`].
810
    ///
811
    /// # Panics
812
    ///
813
    /// This panics when the number of minutes is too small or too big.
814
    /// The minimum value is `-10,518,456,960`.
815
    /// The maximum value is `10,518,456,960`.
816
    #[inline]
817
0
    pub fn minutes<I: Into<i64>>(self, minutes: I) -> Span {
818
0
        self.try_minutes(minutes).expect("value for minutes is out of bounds")
819
0
    }
820
821
    /// Set the number of seconds on this span. The value may be negative.
822
    ///
823
    /// The fallible version of this method is [`Span::try_seconds`].
824
    ///
825
    /// # Panics
826
    ///
827
    /// This panics when the number of seconds is too small or too big.
828
    /// The minimum value is `-631,107,417,600`.
829
    /// The maximum value is `631,107,417,600`.
830
    #[inline]
831
0
    pub fn seconds<I: Into<i64>>(self, seconds: I) -> Span {
832
0
        self.try_seconds(seconds).expect("value for seconds is out of bounds")
833
0
    }
834
835
    /// Set the number of milliseconds on this span. The value may be negative.
836
    ///
837
    /// The fallible version of this method is [`Span::try_milliseconds`].
838
    ///
839
    /// # Panics
840
    ///
841
    /// This panics when the number of milliseconds is too small or too big.
842
    /// The minimum value is `-631,107,417,600,000`.
843
    /// The maximum value is `631,107,417,600,000`.
844
    #[inline]
845
0
    pub fn milliseconds<I: Into<i64>>(self, milliseconds: I) -> Span {
846
0
        self.try_milliseconds(milliseconds)
847
0
            .expect("value for milliseconds is out of bounds")
848
0
    }
849
850
    /// Set the number of microseconds on this span. The value may be negative.
851
    ///
852
    /// The fallible version of this method is [`Span::try_microseconds`].
853
    ///
854
    /// # Panics
855
    ///
856
    /// This panics when the number of microseconds is too small or too big.
857
    /// The minimum value is `-631,107,417,600,000,000`.
858
    /// The maximum value is `631,107,417,600,000,000`.
859
    #[inline]
860
0
    pub fn microseconds<I: Into<i64>>(self, microseconds: I) -> Span {
861
0
        self.try_microseconds(microseconds)
862
0
            .expect("value for microseconds is out of bounds")
863
0
    }
864
865
    /// Set the number of nanoseconds on this span. The value may be negative.
866
    ///
867
    /// Note that unlike all other units, a 64-bit integer number of
868
    /// nanoseconds is not big enough to represent all possible spans between
869
    /// all possible datetimes supported by Jiff. This means, for example, that
870
    /// computing a span between two datetimes that are far enough apart _and_
871
    /// requesting a largest unit of [`Unit::Nanosecond`], might return an
872
    /// error due to lack of precision.
873
    ///
874
    /// The fallible version of this method is [`Span::try_nanoseconds`].
875
    ///
876
    /// # Panics
877
    ///
878
    /// This panics when the number of nanoseconds is too small or too big.
879
    /// The minimum value is `-9,223,372,036,854,775,807`.
880
    /// The maximum value is `9,223,372,036,854,775,807`.
881
    #[inline]
882
0
    pub fn nanoseconds<I: Into<i64>>(self, nanoseconds: I) -> Span {
883
0
        self.try_nanoseconds(nanoseconds)
884
0
            .expect("value for nanoseconds is out of bounds")
885
0
    }
886
}
887
888
/// Fallible methods for setting units on a `Span`.
889
///
890
/// These methods are useful when the span is made up of user provided values
891
/// that may not be in range.
892
impl Span {
893
    /// Set the number of years on this span. The value may be negative.
894
    ///
895
    /// The panicking version of this method is [`Span::years`].
896
    ///
897
    /// # Errors
898
    ///
899
    /// This returns an error when the number of years is too small or too big.
900
    /// The minimum value is `-19,998`.
901
    /// The maximum value is `19,998`.
902
    #[inline]
903
591
    pub fn try_years<I: Into<i64>>(self, years: I) -> Result<Span, Error> {
904
591
        let years = t::SpanYears::try_new("years", years)?;
905
256
        Ok(self.years_ranged(years))
906
591
    }
Unexecuted instantiation: <jiff::span::Span>::try_years::<i64>
Unexecuted instantiation: <jiff::span::Span>::try_years::<_>
<jiff::span::Span>::try_years::<i64>
Line
Count
Source
903
411
    pub fn try_years<I: Into<i64>>(self, years: I) -> Result<Span, Error> {
904
411
        let years = t::SpanYears::try_new("years", years)?;
905
192
        Ok(self.years_ranged(years))
906
411
    }
Unexecuted instantiation: <jiff::span::Span>::try_years::<i64>
<jiff::span::Span>::try_years::<i64>
Line
Count
Source
903
180
    pub fn try_years<I: Into<i64>>(self, years: I) -> Result<Span, Error> {
904
180
        let years = t::SpanYears::try_new("years", years)?;
905
64
        Ok(self.years_ranged(years))
906
180
    }
907
908
    /// Set the number of months on this span. The value may be negative.
909
    ///
910
    /// The panicking version of this method is [`Span::months`].
911
    ///
912
    /// # Errors
913
    ///
914
    /// This returns an error when the number of months is too small or too big.
915
    /// The minimum value is `-239,976`.
916
    /// The maximum value is `239,976`.
917
    #[inline]
918
451
    pub fn try_months<I: Into<i64>>(self, months: I) -> Result<Span, Error> {
919
        type Range = ri64<{ t::SpanMonths::MIN }, { t::SpanMonths::MAX }>;
920
451
        let months = Range::try_new("months", months)?;
921
216
        Ok(self.months_ranged(months.rinto()))
922
451
    }
Unexecuted instantiation: <jiff::span::Span>::try_months::<i64>
Unexecuted instantiation: <jiff::span::Span>::try_months::<_>
<jiff::span::Span>::try_months::<i64>
Line
Count
Source
918
318
    pub fn try_months<I: Into<i64>>(self, months: I) -> Result<Span, Error> {
919
        type Range = ri64<{ t::SpanMonths::MIN }, { t::SpanMonths::MAX }>;
920
318
        let months = Range::try_new("months", months)?;
921
165
        Ok(self.months_ranged(months.rinto()))
922
318
    }
Unexecuted instantiation: <jiff::span::Span>::try_months::<i64>
<jiff::span::Span>::try_months::<i64>
Line
Count
Source
918
133
    pub fn try_months<I: Into<i64>>(self, months: I) -> Result<Span, Error> {
919
        type Range = ri64<{ t::SpanMonths::MIN }, { t::SpanMonths::MAX }>;
920
133
        let months = Range::try_new("months", months)?;
921
51
        Ok(self.months_ranged(months.rinto()))
922
133
    }
923
924
    /// Set the number of weeks on this span. The value may be negative.
925
    ///
926
    /// The panicking version of this method is [`Span::weeks`].
927
    ///
928
    /// # Errors
929
    ///
930
    /// This returns an error when the number of weeks is too small or too big.
931
    /// The minimum value is `-1,043,497`.
932
    /// The maximum value is `1_043_497`.
933
    #[inline]
934
513
    pub fn try_weeks<I: Into<i64>>(self, weeks: I) -> Result<Span, Error> {
935
        type Range = ri64<{ t::SpanWeeks::MIN }, { t::SpanWeeks::MAX }>;
936
513
        let weeks = Range::try_new("weeks", weeks)?;
937
294
        Ok(self.weeks_ranged(weeks.rinto()))
938
513
    }
Unexecuted instantiation: <jiff::span::Span>::try_weeks::<i64>
Unexecuted instantiation: <jiff::span::Span>::try_weeks::<_>
<jiff::span::Span>::try_weeks::<i64>
Line
Count
Source
934
352
    pub fn try_weeks<I: Into<i64>>(self, weeks: I) -> Result<Span, Error> {
935
        type Range = ri64<{ t::SpanWeeks::MIN }, { t::SpanWeeks::MAX }>;
936
352
        let weeks = Range::try_new("weeks", weeks)?;
937
222
        Ok(self.weeks_ranged(weeks.rinto()))
938
352
    }
Unexecuted instantiation: <jiff::span::Span>::try_weeks::<i64>
<jiff::span::Span>::try_weeks::<i64>
Line
Count
Source
934
161
    pub fn try_weeks<I: Into<i64>>(self, weeks: I) -> Result<Span, Error> {
935
        type Range = ri64<{ t::SpanWeeks::MIN }, { t::SpanWeeks::MAX }>;
936
161
        let weeks = Range::try_new("weeks", weeks)?;
937
72
        Ok(self.weeks_ranged(weeks.rinto()))
938
161
    }
939
940
    /// Set the number of days on this span. The value may be negative.
941
    ///
942
    /// The panicking version of this method is [`Span::days`].
943
    ///
944
    /// # Errors
945
    ///
946
    /// This returns an error when the number of days is too small or too big.
947
    /// The minimum value is `-7,304,484`.
948
    /// The maximum value is `7,304,484`.
949
    #[inline]
950
561
    pub fn try_days<I: Into<i64>>(self, days: I) -> Result<Span, Error> {
951
        type Range = ri64<{ t::SpanDays::MIN }, { t::SpanDays::MAX }>;
952
561
        let days = Range::try_new("days", days)?;
953
342
        Ok(self.days_ranged(days.rinto()))
954
561
    }
Unexecuted instantiation: <jiff::span::Span>::try_days::<i64>
Unexecuted instantiation: <jiff::span::Span>::try_days::<_>
<jiff::span::Span>::try_days::<i64>
Line
Count
Source
950
415
    pub fn try_days<I: Into<i64>>(self, days: I) -> Result<Span, Error> {
951
        type Range = ri64<{ t::SpanDays::MIN }, { t::SpanDays::MAX }>;
952
415
        let days = Range::try_new("days", days)?;
953
269
        Ok(self.days_ranged(days.rinto()))
954
415
    }
Unexecuted instantiation: <jiff::span::Span>::try_days::<i64>
<jiff::span::Span>::try_days::<i64>
Line
Count
Source
950
146
    pub fn try_days<I: Into<i64>>(self, days: I) -> Result<Span, Error> {
951
        type Range = ri64<{ t::SpanDays::MIN }, { t::SpanDays::MAX }>;
952
146
        let days = Range::try_new("days", days)?;
953
73
        Ok(self.days_ranged(days.rinto()))
954
146
    }
955
956
    /// Set the number of hours on this span. The value may be negative.
957
    ///
958
    /// The panicking version of this method is [`Span::hours`].
959
    ///
960
    /// # Errors
961
    ///
962
    /// This returns an error when the number of hours is too small or too big.
963
    /// The minimum value is `-175,307,616`.
964
    /// The maximum value is `175,307,616`.
965
    #[inline]
966
569
    pub fn try_hours<I: Into<i64>>(self, hours: I) -> Result<Span, Error> {
967
        type Range = ri64<{ t::SpanHours::MIN }, { t::SpanHours::MAX }>;
968
569
        let hours = Range::try_new("hours", hours)?;
969
337
        Ok(self.hours_ranged(hours.rinto()))
970
569
    }
Unexecuted instantiation: <jiff::span::Span>::try_hours::<i64>
Unexecuted instantiation: <jiff::span::Span>::try_hours::<_>
<jiff::span::Span>::try_hours::<i64>
Line
Count
Source
966
386
    pub fn try_hours<I: Into<i64>>(self, hours: I) -> Result<Span, Error> {
967
        type Range = ri64<{ t::SpanHours::MIN }, { t::SpanHours::MAX }>;
968
386
        let hours = Range::try_new("hours", hours)?;
969
230
        Ok(self.hours_ranged(hours.rinto()))
970
386
    }
Unexecuted instantiation: <jiff::span::Span>::try_hours::<i64>
<jiff::span::Span>::try_hours::<i64>
Line
Count
Source
966
183
    pub fn try_hours<I: Into<i64>>(self, hours: I) -> Result<Span, Error> {
967
        type Range = ri64<{ t::SpanHours::MIN }, { t::SpanHours::MAX }>;
968
183
        let hours = Range::try_new("hours", hours)?;
969
107
        Ok(self.hours_ranged(hours.rinto()))
970
183
    }
971
972
    /// Set the number of minutes on this span. The value may be negative.
973
    ///
974
    /// The panicking version of this method is [`Span::minutes`].
975
    ///
976
    /// # Errors
977
    ///
978
    /// This returns an error when the number of minutes is too small or too big.
979
    /// The minimum value is `-10,518,456,960`.
980
    /// The maximum value is `10,518,456,960`.
981
    #[inline]
982
662
    pub fn try_minutes<I: Into<i64>>(self, minutes: I) -> Result<Span, Error> {
983
        type Range = ri64<{ t::SpanMinutes::MIN }, { t::SpanMinutes::MAX }>;
984
662
        let minutes = Range::try_new("minutes", minutes.into())?;
985
458
        Ok(self.minutes_ranged(minutes))
986
662
    }
Unexecuted instantiation: <jiff::span::Span>::try_minutes::<i64>
Unexecuted instantiation: <jiff::span::Span>::try_minutes::<_>
<jiff::span::Span>::try_minutes::<i64>
Line
Count
Source
982
449
    pub fn try_minutes<I: Into<i64>>(self, minutes: I) -> Result<Span, Error> {
983
        type Range = ri64<{ t::SpanMinutes::MIN }, { t::SpanMinutes::MAX }>;
984
449
        let minutes = Range::try_new("minutes", minutes.into())?;
985
319
        Ok(self.minutes_ranged(minutes))
986
449
    }
Unexecuted instantiation: <jiff::span::Span>::try_minutes::<i64>
<jiff::span::Span>::try_minutes::<i64>
Line
Count
Source
982
213
    pub fn try_minutes<I: Into<i64>>(self, minutes: I) -> Result<Span, Error> {
983
        type Range = ri64<{ t::SpanMinutes::MIN }, { t::SpanMinutes::MAX }>;
984
213
        let minutes = Range::try_new("minutes", minutes.into())?;
985
139
        Ok(self.minutes_ranged(minutes))
986
213
    }
987
988
    /// Set the number of seconds on this span. The value may be negative.
989
    ///
990
    /// The panicking version of this method is [`Span::seconds`].
991
    ///
992
    /// # Errors
993
    ///
994
    /// This returns an error when the number of seconds is too small or too big.
995
    /// The minimum value is `-631,107,417,600`.
996
    /// The maximum value is `631,107,417,600`.
997
    #[inline]
998
1.85k
    pub fn try_seconds<I: Into<i64>>(self, seconds: I) -> Result<Span, Error> {
999
        type Range = ri64<{ t::SpanSeconds::MIN }, { t::SpanSeconds::MAX }>;
1000
1.85k
        let seconds = Range::try_new("seconds", seconds.into())?;
1001
1.47k
        Ok(self.seconds_ranged(seconds))
1002
1.85k
    }
Unexecuted instantiation: <jiff::span::Span>::try_seconds::<i64>
Unexecuted instantiation: <jiff::span::Span>::try_seconds::<jiff::util::rangeint::ri128<-0x80000000000000000000000000000000, 0x7fffffffffffffffffffffffffffffff>>
<jiff::span::Span>::try_seconds::<i64>
Line
Count
Source
998
1.31k
    pub fn try_seconds<I: Into<i64>>(self, seconds: I) -> Result<Span, Error> {
999
        type Range = ri64<{ t::SpanSeconds::MIN }, { t::SpanSeconds::MAX }>;
1000
1.31k
        let seconds = Range::try_new("seconds", seconds.into())?;
1001
1.07k
        Ok(self.seconds_ranged(seconds))
1002
1.31k
    }
Unexecuted instantiation: <jiff::span::Span>::try_seconds::<i64>
<jiff::span::Span>::try_seconds::<i64>
Line
Count
Source
998
538
    pub fn try_seconds<I: Into<i64>>(self, seconds: I) -> Result<Span, Error> {
999
        type Range = ri64<{ t::SpanSeconds::MIN }, { t::SpanSeconds::MAX }>;
1000
538
        let seconds = Range::try_new("seconds", seconds.into())?;
1001
403
        Ok(self.seconds_ranged(seconds))
1002
538
    }
1003
1004
    /// Set the number of milliseconds on this span. The value may be negative.
1005
    ///
1006
    /// The panicking version of this method is [`Span::milliseconds`].
1007
    ///
1008
    /// # Errors
1009
    ///
1010
    /// This returns an error when the number of milliseconds is too small or
1011
    /// too big.
1012
    /// The minimum value is `-631,107,417,600,000`.
1013
    /// The maximum value is `631,107,417,600,000`.
1014
    #[inline]
1015
0
    pub fn try_milliseconds<I: Into<i64>>(
1016
0
        self,
1017
0
        milliseconds: I,
1018
0
    ) -> Result<Span, Error> {
1019
        type Range =
1020
            ri64<{ t::SpanMilliseconds::MIN }, { t::SpanMilliseconds::MAX }>;
1021
0
        let milliseconds =
1022
0
            Range::try_new("milliseconds", milliseconds.into())?;
1023
0
        Ok(self.milliseconds_ranged(milliseconds))
1024
0
    }
1025
1026
    /// Set the number of microseconds on this span. The value may be negative.
1027
    ///
1028
    /// The panicking version of this method is [`Span::microseconds`].
1029
    ///
1030
    /// # Errors
1031
    ///
1032
    /// This returns an error when the number of microseconds is too small or
1033
    /// too big.
1034
    /// The minimum value is `-631,107,417,600,000,000`.
1035
    /// The maximum value is `631,107,417,600,000,000`.
1036
    #[inline]
1037
0
    pub fn try_microseconds<I: Into<i64>>(
1038
0
        self,
1039
0
        microseconds: I,
1040
0
    ) -> Result<Span, Error> {
1041
        type Range =
1042
            ri64<{ t::SpanMicroseconds::MIN }, { t::SpanMicroseconds::MAX }>;
1043
0
        let microseconds =
1044
0
            Range::try_new("microseconds", microseconds.into())?;
1045
0
        Ok(self.microseconds_ranged(microseconds))
1046
0
    }
1047
1048
    /// Set the number of nanoseconds on this span. The value may be negative.
1049
    ///
1050
    /// Note that unlike all other units, a 64-bit integer number of
1051
    /// nanoseconds is not big enough to represent all possible spans between
1052
    /// all possible datetimes supported by Jiff. This means, for example, that
1053
    /// computing a span between two datetimes that are far enough apart _and_
1054
    /// requesting a largest unit of [`Unit::Nanosecond`], might return an
1055
    /// error due to lack of precision.
1056
    ///
1057
    /// The panicking version of this method is [`Span::nanoseconds`].
1058
    ///
1059
    /// # Errors
1060
    ///
1061
    /// This returns an error when the number of nanoseconds is too small or
1062
    /// too big.
1063
    /// The minimum value is `-9,223,372,036,854,775,807`.
1064
    /// The maximum value is `9,223,372,036,854,775,807`.
1065
    #[inline]
1066
0
    pub fn try_nanoseconds<I: Into<i64>>(
1067
0
        self,
1068
0
        nanoseconds: I,
1069
0
    ) -> Result<Span, Error> {
1070
        type Range =
1071
            ri64<{ t::SpanNanoseconds::MIN }, { t::SpanNanoseconds::MAX }>;
1072
0
        let nanoseconds = Range::try_new("nanoseconds", nanoseconds.into())?;
1073
0
        Ok(self.nanoseconds_ranged(nanoseconds))
1074
0
    }
1075
}
1076
1077
/// Routines for accessing the individual units in a `Span`.
1078
impl Span {
1079
    /// Returns the number of year units in this span.
1080
    ///
1081
    /// Note that this is not the same as the total number of years in the
1082
    /// span. To get that, you'll need to use either [`Span::round`] or
1083
    /// [`Span::total`].
1084
    ///
1085
    /// # Example
1086
    ///
1087
    /// ```
1088
    /// use jiff::{civil::date, ToSpan, Unit};
1089
    ///
1090
    /// let span = 3.years().months(24);
1091
    /// assert_eq!(3, span.get_years());
1092
    /// assert_eq!(5.0, span.total((Unit::Year, date(2024, 1, 1)))?);
1093
    ///
1094
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1095
    /// ```
1096
    #[inline]
1097
0
    pub fn get_years(&self) -> i16 {
1098
0
        self.get_years_ranged().get()
1099
0
    }
1100
1101
    /// Returns the number of month units in this span.
1102
    ///
1103
    /// Note that this is not the same as the total number of months in the
1104
    /// span. To get that, you'll need to use either [`Span::round`] or
1105
    /// [`Span::total`].
1106
    ///
1107
    /// # Example
1108
    ///
1109
    /// ```
1110
    /// use jiff::{civil::date, ToSpan, Unit};
1111
    ///
1112
    /// let span = 7.months().days(59);
1113
    /// assert_eq!(7, span.get_months());
1114
    /// assert_eq!(9.0, span.total((Unit::Month, date(2022, 6, 1)))?);
1115
    ///
1116
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1117
    /// ```
1118
    #[inline]
1119
0
    pub fn get_months(&self) -> i32 {
1120
0
        self.get_months_ranged().get()
1121
0
    }
1122
1123
    /// Returns the number of week units in this span.
1124
    ///
1125
    /// Note that this is not the same as the total number of weeks in the
1126
    /// span. To get that, you'll need to use either [`Span::round`] or
1127
    /// [`Span::total`].
1128
    ///
1129
    /// # Example
1130
    ///
1131
    /// ```
1132
    /// use jiff::{civil::date, ToSpan, Unit};
1133
    ///
1134
    /// let span = 3.weeks().days(14);
1135
    /// assert_eq!(3, span.get_weeks());
1136
    /// assert_eq!(5.0, span.total((Unit::Week, date(2024, 1, 1)))?);
1137
    ///
1138
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1139
    /// ```
1140
    #[inline]
1141
0
    pub fn get_weeks(&self) -> i32 {
1142
0
        self.get_weeks_ranged().get()
1143
0
    }
1144
1145
    /// Returns the number of day units in this span.
1146
    ///
1147
    /// Note that this is not the same as the total number of days in the
1148
    /// span. To get that, you'll need to use either [`Span::round`] or
1149
    /// [`Span::total`].
1150
    ///
1151
    /// # Example
1152
    ///
1153
    /// ```
1154
    /// use jiff::{ToSpan, Unit, Zoned};
1155
    ///
1156
    /// let span = 3.days().hours(47);
1157
    /// assert_eq!(3, span.get_days());
1158
    ///
1159
    /// let zdt: Zoned = "2024-03-07[America/New_York]".parse()?;
1160
    /// assert_eq!(5.0, span.total((Unit::Day, &zdt))?);
1161
    ///
1162
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1163
    /// ```
1164
    #[inline]
1165
0
    pub fn get_days(&self) -> i32 {
1166
0
        self.get_days_ranged().get()
1167
0
    }
1168
1169
    /// Returns the number of hour units in this span.
1170
    ///
1171
    /// Note that this is not the same as the total number of hours in the
1172
    /// span. To get that, you'll need to use either [`Span::round`] or
1173
    /// [`Span::total`].
1174
    ///
1175
    /// # Example
1176
    ///
1177
    /// ```
1178
    /// use jiff::{ToSpan, Unit};
1179
    ///
1180
    /// let span = 3.hours().minutes(120);
1181
    /// assert_eq!(3, span.get_hours());
1182
    /// assert_eq!(5.0, span.total(Unit::Hour)?);
1183
    ///
1184
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1185
    /// ```
1186
    #[inline]
1187
0
    pub fn get_hours(&self) -> i32 {
1188
0
        self.get_hours_ranged().get()
1189
0
    }
1190
1191
    /// Returns the number of minute units in this span.
1192
    ///
1193
    /// Note that this is not the same as the total number of minutes in the
1194
    /// span. To get that, you'll need to use either [`Span::round`] or
1195
    /// [`Span::total`].
1196
    ///
1197
    /// # Example
1198
    ///
1199
    /// ```
1200
    /// use jiff::{ToSpan, Unit};
1201
    ///
1202
    /// let span = 3.minutes().seconds(120);
1203
    /// assert_eq!(3, span.get_minutes());
1204
    /// assert_eq!(5.0, span.total(Unit::Minute)?);
1205
    ///
1206
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1207
    /// ```
1208
    #[inline]
1209
0
    pub fn get_minutes(&self) -> i64 {
1210
0
        self.get_minutes_ranged().get()
1211
0
    }
1212
1213
    /// Returns the number of second units in this span.
1214
    ///
1215
    /// Note that this is not the same as the total number of seconds in the
1216
    /// span. To get that, you'll need to use either [`Span::round`] or
1217
    /// [`Span::total`].
1218
    ///
1219
    /// # Example
1220
    ///
1221
    /// ```
1222
    /// use jiff::{ToSpan, Unit};
1223
    ///
1224
    /// let span = 3.seconds().milliseconds(2_000);
1225
    /// assert_eq!(3, span.get_seconds());
1226
    /// assert_eq!(5.0, span.total(Unit::Second)?);
1227
    ///
1228
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1229
    /// ```
1230
    #[inline]
1231
0
    pub fn get_seconds(&self) -> i64 {
1232
0
        self.get_seconds_ranged().get()
1233
0
    }
1234
1235
    /// Returns the number of millisecond units in this span.
1236
    ///
1237
    /// Note that this is not the same as the total number of milliseconds in
1238
    /// the span. To get that, you'll need to use either [`Span::round`] or
1239
    /// [`Span::total`].
1240
    ///
1241
    /// # Example
1242
    ///
1243
    /// ```
1244
    /// use jiff::{ToSpan, Unit};
1245
    ///
1246
    /// let span = 3.milliseconds().microseconds(2_000);
1247
    /// assert_eq!(3, span.get_milliseconds());
1248
    /// assert_eq!(5.0, span.total(Unit::Millisecond)?);
1249
    ///
1250
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1251
    /// ```
1252
    #[inline]
1253
0
    pub fn get_milliseconds(&self) -> i64 {
1254
0
        self.get_milliseconds_ranged().get()
1255
0
    }
1256
1257
    /// Returns the number of microsecond units in this span.
1258
    ///
1259
    /// Note that this is not the same as the total number of microseconds in
1260
    /// the span. To get that, you'll need to use either [`Span::round`] or
1261
    /// [`Span::total`].
1262
    ///
1263
    /// # Example
1264
    ///
1265
    /// ```
1266
    /// use jiff::{ToSpan, Unit};
1267
    ///
1268
    /// let span = 3.microseconds().nanoseconds(2_000);
1269
    /// assert_eq!(3, span.get_microseconds());
1270
    /// assert_eq!(5.0, span.total(Unit::Microsecond)?);
1271
    ///
1272
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1273
    /// ```
1274
    #[inline]
1275
0
    pub fn get_microseconds(&self) -> i64 {
1276
0
        self.get_microseconds_ranged().get()
1277
0
    }
1278
1279
    /// Returns the number of nanosecond units in this span.
1280
    ///
1281
    /// Note that this is not the same as the total number of nanoseconds in
1282
    /// the span. To get that, you'll need to use either [`Span::round`] or
1283
    /// [`Span::total`].
1284
    ///
1285
    /// # Example
1286
    ///
1287
    /// ```
1288
    /// use jiff::{ToSpan, Unit};
1289
    ///
1290
    /// let span = 3.microseconds().nanoseconds(2_000);
1291
    /// assert_eq!(2_000, span.get_nanoseconds());
1292
    /// assert_eq!(5_000.0, span.total(Unit::Nanosecond)?);
1293
    ///
1294
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1295
    /// ```
1296
    #[inline]
1297
0
    pub fn get_nanoseconds(&self) -> i64 {
1298
0
        self.get_nanoseconds_ranged().get()
1299
0
    }
1300
}
1301
1302
/// Routines for manipulating, comparing and inspecting `Span` values.
1303
impl Span {
1304
    /// Returns a new span that is the absolute value of this span.
1305
    ///
1306
    /// If this span is zero or positive, then this is a no-op.
1307
    ///
1308
    /// # Example
1309
    ///
1310
    /// ```
1311
    /// use jiff::ToSpan;
1312
    ///
1313
    /// let span = -100.seconds();
1314
    /// assert_eq!(span.to_string(), "-PT100S");
1315
    /// let span = span.abs();
1316
    /// assert_eq!(span.to_string(), "PT100S");
1317
    /// ```
1318
    #[inline]
1319
0
    pub fn abs(self) -> Span {
1320
0
        if self.is_zero() {
1321
0
            return self;
1322
0
        }
1323
0
        Span { sign: ri8::N::<1>(), ..self }
1324
0
    }
1325
1326
    /// Returns a new span that negates this span.
1327
    ///
1328
    /// If this span is zero, then this is a no-op. If this span is negative,
1329
    /// then the returned span is positive. If this span is positive, then
1330
    /// the returned span is negative.
1331
    ///
1332
    /// # Example
1333
    ///
1334
    /// ```
1335
    /// use jiff::ToSpan;
1336
    ///
1337
    /// let span = 100.days();
1338
    /// assert_eq!(span.to_string(), "P100D");
1339
    /// let span = span.negate();
1340
    /// assert_eq!(span.to_string(), "-P100D");
1341
    /// ```
1342
    ///
1343
    /// # Example: available via the negation operator
1344
    ///
1345
    /// This routine can also be used via `-`:
1346
    ///
1347
    /// ```
1348
    /// use jiff::ToSpan;
1349
    ///
1350
    /// let span = 100.days();
1351
    /// assert_eq!(span.to_string(), "P100D");
1352
    /// let span = -span;
1353
    /// assert_eq!(span.to_string(), "-P100D");
1354
    /// ```
1355
    #[inline]
1356
1.78k
    pub fn negate(self) -> Span {
1357
1.78k
        Span { sign: -self.sign, ..self }
1358
1.78k
    }
1359
1360
    /// Returns the "sign number" or "signum" of this span.
1361
    ///
1362
    /// The number returned is `-1` when this span is negative,
1363
    /// `0` when this span is zero and `1` when this span is positive.
1364
    #[inline]
1365
0
    pub fn signum(self) -> i8 {
1366
0
        self.sign.signum().get()
1367
0
    }
1368
1369
    /// Returns true if and only if this span is positive.
1370
    ///
1371
    /// This returns false when the span is zero or negative.
1372
    ///
1373
    /// # Example
1374
    ///
1375
    /// ```
1376
    /// use jiff::ToSpan;
1377
    ///
1378
    /// assert!(!2.months().is_negative());
1379
    /// assert!((-2.months()).is_negative());
1380
    /// ```
1381
    #[inline]
1382
0
    pub fn is_positive(self) -> bool {
1383
0
        self.get_sign_ranged() > C(0)
1384
0
    }
1385
1386
    /// Returns true if and only if this span is negative.
1387
    ///
1388
    /// This returns false when the span is zero or positive.
1389
    ///
1390
    /// # Example
1391
    ///
1392
    /// ```
1393
    /// use jiff::ToSpan;
1394
    ///
1395
    /// assert!(!2.months().is_negative());
1396
    /// assert!((-2.months()).is_negative());
1397
    /// ```
1398
    #[inline]
1399
4.84k
    pub fn is_negative(self) -> bool {
1400
4.84k
        self.get_sign_ranged() < C(0)
1401
4.84k
    }
1402
1403
    /// Returns true if and only if every field in this span is set to `0`.
1404
    ///
1405
    /// # Example
1406
    ///
1407
    /// ```
1408
    /// use jiff::{Span, ToSpan};
1409
    ///
1410
    /// assert!(Span::new().is_zero());
1411
    /// assert!(Span::default().is_zero());
1412
    /// assert!(0.seconds().is_zero());
1413
    /// assert!(!0.seconds().seconds(1).is_zero());
1414
    /// assert!(0.seconds().seconds(1).seconds(0).is_zero());
1415
    /// ```
1416
    #[inline]
1417
6.22k
    pub fn is_zero(self) -> bool {
1418
6.22k
        self.sign == C(0)
1419
6.22k
    }
1420
1421
    /// Returns this `Span` as a value with a type that implements the
1422
    /// `Hash`, `Eq` and `PartialEq` traits in a fieldwise fashion.
1423
    ///
1424
    /// A `SpanFieldwise` is meant to make it easy to compare two spans in a
1425
    /// "dumb" way based purely on its unit values. This is distinct from
1426
    /// something like [`Span::compare`] that performs a comparison on the
1427
    /// actual elapsed time of two spans.
1428
    ///
1429
    /// It is generally discouraged to use `SpanFieldwise` since spans that
1430
    /// represent an equivalent elapsed amount of time may compare unequal.
1431
    /// However, in some cases, it is useful to be able to assert precise
1432
    /// field values. For example, Jiff itself makes heavy use of fieldwise
1433
    /// comparisons for tests.
1434
    ///
1435
    /// # Example: the difference between `SpanFieldwise` and `Span::compare`
1436
    ///
1437
    /// In short, `SpanFieldwise` considers `2 hours` and `120 minutes` to be
1438
    /// distinct values, but `Span::compare` considers them to be equivalent:
1439
    ///
1440
    /// ```
1441
    /// use std::cmp::Ordering;
1442
    /// use jiff::ToSpan;
1443
    ///
1444
    /// assert_ne!(120.minutes().fieldwise(), 2.hours().fieldwise());
1445
    /// assert_eq!(120.minutes().compare(2.hours())?, Ordering::Equal);
1446
    ///
1447
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1448
    /// ```
1449
    #[inline]
1450
0
    pub fn fieldwise(self) -> SpanFieldwise {
1451
0
        SpanFieldwise(self)
1452
0
    }
1453
1454
    /// Multiplies each field in this span by a given integer.
1455
    ///
1456
    /// If this would cause any individual field in this span to overflow, then
1457
    /// this returns an error.
1458
    ///
1459
    /// # Example
1460
    ///
1461
    /// ```
1462
    /// use jiff::ToSpan;
1463
    ///
1464
    /// let span = 4.days().seconds(8);
1465
    /// assert_eq!(span.checked_mul(2)?, 8.days().seconds(16).fieldwise());
1466
    /// assert_eq!(span.checked_mul(-3)?, -12.days().seconds(24).fieldwise());
1467
    /// // Notice that no re-balancing is done. It's "just" multiplication.
1468
    /// assert_eq!(span.checked_mul(10)?, 40.days().seconds(80).fieldwise());
1469
    ///
1470
    /// let span = 10_000.years();
1471
    /// // too big!
1472
    /// assert!(span.checked_mul(3).is_err());
1473
    ///
1474
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1475
    /// ```
1476
    ///
1477
    /// # Example: available via the multiplication operator
1478
    ///
1479
    /// This method can be used via the `*` operator. Note though that a panic
1480
    /// happens on overflow.
1481
    ///
1482
    /// ```
1483
    /// use jiff::ToSpan;
1484
    ///
1485
    /// let span = 4.days().seconds(8);
1486
    /// assert_eq!(span * 2, 8.days().seconds(16).fieldwise());
1487
    /// assert_eq!(2 * span, 8.days().seconds(16).fieldwise());
1488
    /// assert_eq!(span * -3, -12.days().seconds(24).fieldwise());
1489
    /// assert_eq!(-3 * span, -12.days().seconds(24).fieldwise());
1490
    ///
1491
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1492
    /// ```
1493
    #[inline]
1494
0
    pub fn checked_mul(mut self, rhs: i64) -> Result<Span, Error> {
1495
0
        if rhs == 0 {
1496
0
            return Ok(Span::default());
1497
0
        } else if rhs == 1 {
1498
0
            return Ok(self);
1499
0
        }
1500
0
        self.sign *= t::Sign::try_new("span factor", rhs.signum())
1501
0
            .expect("signum fits in ri8");
1502
        // This is all somewhat odd, but since each of our span fields uses
1503
        // a different primitive representation and range of allowed values,
1504
        // we only seek to perform multiplications when they will actually
1505
        // do something. Otherwise, we risk multiplying the mins/maxs of a
1506
        // ranged integer and causing a spurious panic. Basically, the idea
1507
        // here is the allowable values for our multiple depend on what we're
1508
        // actually going to multiply with it. If our span has non-zero years,
1509
        // then our multiple can't exceed the bounds of `SpanYears`, otherwise
1510
        // it is guaranteed to overflow.
1511
0
        if self.years != C(0) {
1512
0
            let rhs = t::SpanYears::try_new("years multiple", rhs)?;
1513
0
            self.years = self.years.try_checked_mul("years", rhs.abs())?;
1514
0
        }
1515
0
        if self.months != C(0) {
1516
0
            let rhs = t::SpanMonths::try_new("months multiple", rhs)?;
1517
0
            self.months = self.months.try_checked_mul("months", rhs.abs())?;
1518
0
        }
1519
0
        if self.weeks != C(0) {
1520
0
            let rhs = t::SpanWeeks::try_new("weeks multiple", rhs)?;
1521
0
            self.weeks = self.weeks.try_checked_mul("weeks", rhs.abs())?;
1522
0
        }
1523
0
        if self.days != C(0) {
1524
0
            let rhs = t::SpanDays::try_new("days multiple", rhs)?;
1525
0
            self.days = self.days.try_checked_mul("days", rhs.abs())?;
1526
0
        }
1527
0
        if self.hours != C(0) {
1528
0
            let rhs = t::SpanHours::try_new("hours multiple", rhs)?;
1529
0
            self.hours = self.hours.try_checked_mul("hours", rhs.abs())?;
1530
0
        }
1531
0
        if self.minutes != C(0) {
1532
0
            let rhs = t::SpanMinutes::try_new("minutes multiple", rhs)?;
1533
            self.minutes =
1534
0
                self.minutes.try_checked_mul("minutes", rhs.abs())?;
1535
0
        }
1536
0
        if self.seconds != C(0) {
1537
0
            let rhs = t::SpanSeconds::try_new("seconds multiple", rhs)?;
1538
            self.seconds =
1539
0
                self.seconds.try_checked_mul("seconds", rhs.abs())?;
1540
0
        }
1541
0
        if self.milliseconds != C(0) {
1542
0
            let rhs =
1543
0
                t::SpanMilliseconds::try_new("milliseconds multiple", rhs)?;
1544
0
            self.milliseconds = self
1545
0
                .milliseconds
1546
0
                .try_checked_mul("milliseconds", rhs.abs())?;
1547
0
        }
1548
0
        if self.microseconds != C(0) {
1549
0
            let rhs =
1550
0
                t::SpanMicroseconds::try_new("microseconds multiple", rhs)?;
1551
0
            self.microseconds = self
1552
0
                .microseconds
1553
0
                .try_checked_mul("microseconds", rhs.abs())?;
1554
0
        }
1555
0
        if self.nanoseconds != C(0) {
1556
0
            let rhs =
1557
0
                t::SpanNanoseconds::try_new("nanoseconds multiple", rhs)?;
1558
            self.nanoseconds =
1559
0
                self.nanoseconds.try_checked_mul("nanoseconds", rhs.abs())?;
1560
0
        }
1561
        // N.B. We don't need to update `self.units` here since it shouldn't
1562
        // change. The only way it could is if a unit goes from zero to
1563
        // non-zero (which can't happen, because multiplication by zero is
1564
        // always zero), or if a unit goes from non-zero to zero. That also
1565
        // can't happen because we handle the case of the factor being zero
1566
        // specially above, and it returns a `Span` will all units zero
1567
        // correctly.
1568
0
        Ok(self)
1569
0
    }
1570
1571
    /// Adds a span to this one and returns the sum as a new span.
1572
    ///
1573
    /// When adding a span with units greater than hours, callers must provide
1574
    /// a relative datetime to anchor the spans.
1575
    ///
1576
    /// Arithmetic proceeds as specified in [RFC 5545]. Bigger units are
1577
    /// added together before smaller units.
1578
    ///
1579
    /// This routine accepts anything that implements `Into<SpanArithmetic>`.
1580
    /// There are some trait implementations that make using this routine
1581
    /// ergonomic:
1582
    ///
1583
    /// * `From<Span> for SpanArithmetic` adds the given span to this one.
1584
    /// * `From<(Span, civil::Date)> for SpanArithmetic` adds the given
1585
    /// span to this one relative to the given date. There are also `From`
1586
    /// implementations for `civil::DateTime` and `Zoned`.
1587
    ///
1588
    /// This also works with different duration types, such as
1589
    /// [`SignedDuration`] and [`std::time::Duration`], via additional trait
1590
    /// implementations:
1591
    ///
1592
    /// * `From<SignedDuration> for SpanArithmetic` adds the given duration to
1593
    /// this one.
1594
    /// * `From<(SignedDuration, civil::Date)> for SpanArithmetic` adds the
1595
    /// given duration to this one relative to the given date. There are also
1596
    /// `From` implementations for `civil::DateTime` and `Zoned`.
1597
    ///
1598
    /// And similarly for `std::time::Duration`.
1599
    ///
1600
    /// Adding a negative span is equivalent to subtracting its absolute value.
1601
    ///
1602
    /// The largest non-zero unit in the span returned is at most the largest
1603
    /// non-zero unit among the two spans being added. For an absolute
1604
    /// duration, its "largest" unit is considered to be nanoseconds.
1605
    ///
1606
    /// The sum returned is automatically re-balanced so that the span is not
1607
    /// "bottom heavy."
1608
    ///
1609
    /// [RFC 5545]: https://datatracker.ietf.org/doc/html/rfc5545
1610
    ///
1611
    /// # Errors
1612
    ///
1613
    /// This returns an error when adding the two spans would overflow any
1614
    /// individual field of a span. This will also return an error if either
1615
    /// of the spans have non-zero units of days or greater and no relative
1616
    /// reference time is provided.
1617
    ///
1618
    /// Callers may use [`SpanArithmetic::days_are_24_hours`] as a special
1619
    /// marker instead of providing a relative civil date to indicate that
1620
    /// all days should be 24 hours long. This also results in treating all
1621
    /// weeks as seven 24 hour days (168 hours).
1622
    ///
1623
    /// # Example
1624
    ///
1625
    /// ```
1626
    /// use jiff::ToSpan;
1627
    ///
1628
    /// assert_eq!(
1629
    ///     1.hour().checked_add(30.minutes())?,
1630
    ///     1.hour().minutes(30).fieldwise(),
1631
    /// );
1632
    ///
1633
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1634
    /// ```
1635
    ///
1636
    /// # Example: re-balancing
1637
    ///
1638
    /// This example shows how units are automatically rebalanced into bigger
1639
    /// units when appropriate.
1640
    ///
1641
    /// ```
1642
    /// use jiff::ToSpan;
1643
    ///
1644
    /// let span1 = 2.hours().minutes(59);
1645
    /// let span2 = 2.minutes();
1646
    /// assert_eq!(span1.checked_add(span2)?, 3.hours().minutes(1).fieldwise());
1647
    ///
1648
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1649
    /// ```
1650
    ///
1651
    /// # Example: days are not assumed to be 24 hours by default
1652
    ///
1653
    /// When dealing with units involving days or weeks, one must either
1654
    /// provide a relative datetime (shown in the following examples) or opt
1655
    /// into invariant 24 hour days:
1656
    ///
1657
    /// ```
1658
    /// use jiff::{SpanRelativeTo, ToSpan};
1659
    ///
1660
    /// let span1 = 2.days().hours(23);
1661
    /// let span2 = 2.hours();
1662
    /// assert_eq!(
1663
    ///     span1.checked_add((span2, SpanRelativeTo::days_are_24_hours()))?,
1664
    ///     3.days().hours(1).fieldwise(),
1665
    /// );
1666
    ///
1667
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1668
    /// ```
1669
    ///
1670
    /// # Example: adding spans with calendar units
1671
    ///
1672
    /// If you try to add two spans with calendar units without specifying a
1673
    /// relative datetime, you'll get an error:
1674
    ///
1675
    /// ```
1676
    /// use jiff::ToSpan;
1677
    ///
1678
    /// let span1 = 1.month().days(15);
1679
    /// let span2 = 15.days();
1680
    /// assert!(span1.checked_add(span2).is_err());
1681
    /// ```
1682
    ///
1683
    /// A relative datetime is needed because calendar spans may correspond to
1684
    /// different actual durations depending on where the span begins:
1685
    ///
1686
    /// ```
1687
    /// use jiff::{civil::date, ToSpan};
1688
    ///
1689
    /// let span1 = 1.month().days(15);
1690
    /// let span2 = 15.days();
1691
    /// // 1 month from March 1 is 31 days...
1692
    /// assert_eq!(
1693
    ///     span1.checked_add((span2, date(2008, 3, 1)))?,
1694
    ///     2.months().fieldwise(),
1695
    /// );
1696
    /// // ... but 1 month from April 1 is 30 days!
1697
    /// assert_eq!(
1698
    ///     span1.checked_add((span2, date(2008, 4, 1)))?,
1699
    ///     1.month().days(30).fieldwise(),
1700
    /// );
1701
    ///
1702
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1703
    /// ```
1704
    ///
1705
    /// # Example: error on overflow
1706
    ///
1707
    /// Adding two spans can overflow, and this will result in an error:
1708
    ///
1709
    /// ```
1710
    /// use jiff::ToSpan;
1711
    ///
1712
    /// assert!(19_998.years().checked_add(1.year()).is_err());
1713
    /// ```
1714
    ///
1715
    /// # Example: adding an absolute duration to a span
1716
    ///
1717
    /// This shows how one isn't limited to just adding two spans together.
1718
    /// One can also add absolute durations to a span.
1719
    ///
1720
    /// ```
1721
    /// use std::time::Duration;
1722
    ///
1723
    /// use jiff::{SignedDuration, ToSpan};
1724
    ///
1725
    /// assert_eq!(
1726
    ///     1.hour().checked_add(SignedDuration::from_mins(30))?,
1727
    ///     1.hour().minutes(30).fieldwise(),
1728
    /// );
1729
    /// assert_eq!(
1730
    ///     1.hour().checked_add(Duration::from_secs(30 * 60))?,
1731
    ///     1.hour().minutes(30).fieldwise(),
1732
    /// );
1733
    ///
1734
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1735
    /// ```
1736
    ///
1737
    /// Note that even when adding an absolute duration, if the span contains
1738
    /// non-uniform units, you still need to provide a relative datetime:
1739
    ///
1740
    /// ```
1741
    /// use jiff::{civil::date, SignedDuration, ToSpan};
1742
    ///
1743
    /// // Might be 1 month or less than 1 month!
1744
    /// let dur = SignedDuration::from_hours(30 * 24);
1745
    /// // No relative datetime provided even when the span
1746
    /// // contains non-uniform units results in an error.
1747
    /// assert!(1.month().checked_add(dur).is_err());
1748
    /// // In this case, 30 days is one month (April).
1749
    /// assert_eq!(
1750
    ///     1.month().checked_add((dur, date(2024, 3, 1)))?,
1751
    ///     2.months().fieldwise(),
1752
    /// );
1753
    /// // In this case, 30 days is less than one month (May).
1754
    /// assert_eq!(
1755
    ///     1.month().checked_add((dur, date(2024, 4, 1)))?,
1756
    ///     1.month().days(30).fieldwise(),
1757
    /// );
1758
    ///
1759
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1760
    /// ```
1761
    #[inline]
1762
0
    pub fn checked_add<'a, A: Into<SpanArithmetic<'a>>>(
1763
0
        &self,
1764
0
        options: A,
1765
0
    ) -> Result<Span, Error> {
1766
0
        let options: SpanArithmetic<'_> = options.into();
1767
0
        options.checked_add(*self)
1768
0
    }
1769
1770
    #[inline]
1771
0
    fn checked_add_span<'a>(
1772
0
        &self,
1773
0
        relative: Option<SpanRelativeTo<'a>>,
1774
0
        span: &Span,
1775
0
    ) -> Result<Span, Error> {
1776
0
        let (span1, span2) = (*self, *span);
1777
0
        let unit = span1.largest_unit().max(span2.largest_unit());
1778
0
        let start = match relative {
1779
0
            Some(r) => match r.to_relative(unit)? {
1780
0
                None => return span1.checked_add_invariant(unit, &span2),
1781
0
                Some(r) => r,
1782
            },
1783
            None => {
1784
0
                requires_relative_date_err(unit)?;
1785
0
                return span1.checked_add_invariant(unit, &span2);
1786
            }
1787
        };
1788
0
        let mid = start.checked_add(span1)?;
1789
0
        let end = mid.checked_add(span2)?;
1790
0
        start.until(unit, &end)
1791
0
    }
1792
1793
    #[inline]
1794
0
    fn checked_add_duration<'a>(
1795
0
        &self,
1796
0
        relative: Option<SpanRelativeTo<'a>>,
1797
0
        duration: SignedDuration,
1798
0
    ) -> Result<Span, Error> {
1799
0
        let (span1, dur2) = (*self, duration);
1800
0
        let unit = span1.largest_unit();
1801
0
        let start = match relative {
1802
0
            Some(r) => match r.to_relative(unit)? {
1803
                None => {
1804
0
                    return span1.checked_add_invariant_duration(unit, dur2)
1805
                }
1806
0
                Some(r) => r,
1807
            },
1808
            None => {
1809
0
                requires_relative_date_err(unit)?;
1810
0
                return span1.checked_add_invariant_duration(unit, dur2);
1811
            }
1812
        };
1813
0
        let mid = start.checked_add(span1)?;
1814
0
        let end = mid.checked_add_duration(dur2)?;
1815
0
        start.until(unit, &end)
1816
0
    }
1817
1818
    /// Like `checked_add`, but only applies for invariant units. That is,
1819
    /// when *both* spans whose non-zero units are all hours or smaller
1820
    /// (or weeks or smaller with the "days are 24 hours" marker).
1821
    #[inline]
1822
0
    fn checked_add_invariant(
1823
0
        &self,
1824
0
        unit: Unit,
1825
0
        span: &Span,
1826
0
    ) -> Result<Span, Error> {
1827
0
        assert!(unit <= Unit::Week);
1828
0
        let nanos1 = self.to_invariant_nanoseconds();
1829
0
        let nanos2 = span.to_invariant_nanoseconds();
1830
0
        let sum = nanos1 + nanos2;
1831
0
        Span::from_invariant_nanoseconds(unit, sum)
1832
0
    }
1833
1834
    /// Like `checked_add_invariant`, but adds an absolute duration.
1835
    #[inline]
1836
0
    fn checked_add_invariant_duration(
1837
0
        &self,
1838
0
        unit: Unit,
1839
0
        duration: SignedDuration,
1840
0
    ) -> Result<Span, Error> {
1841
0
        assert!(unit <= Unit::Week);
1842
0
        let nanos1 = self.to_invariant_nanoseconds();
1843
0
        let nanos2 = t::NoUnits96::new_unchecked(duration.as_nanos());
1844
0
        let sum = nanos1 + nanos2;
1845
0
        Span::from_invariant_nanoseconds(unit, sum)
1846
0
    }
1847
1848
    /// This routine is identical to [`Span::checked_add`] with the given
1849
    /// duration negated.
1850
    ///
1851
    /// # Errors
1852
    ///
1853
    /// This has the same error conditions as [`Span::checked_add`].
1854
    ///
1855
    /// # Example
1856
    ///
1857
    /// ```
1858
    /// use std::time::Duration;
1859
    ///
1860
    /// use jiff::{SignedDuration, ToSpan};
1861
    ///
1862
    /// assert_eq!(
1863
    ///     1.hour().checked_sub(30.minutes())?,
1864
    ///     30.minutes().fieldwise(),
1865
    /// );
1866
    /// assert_eq!(
1867
    ///     1.hour().checked_sub(SignedDuration::from_mins(30))?,
1868
    ///     30.minutes().fieldwise(),
1869
    /// );
1870
    /// assert_eq!(
1871
    ///     1.hour().checked_sub(Duration::from_secs(30 * 60))?,
1872
    ///     30.minutes().fieldwise(),
1873
    /// );
1874
    ///
1875
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1876
    /// ```
1877
    #[inline]
1878
0
    pub fn checked_sub<'a, A: Into<SpanArithmetic<'a>>>(
1879
0
        &self,
1880
0
        options: A,
1881
0
    ) -> Result<Span, Error> {
1882
0
        let mut options: SpanArithmetic<'_> = options.into();
1883
0
        options.duration = options.duration.checked_neg()?;
1884
0
        options.checked_add(*self)
1885
0
    }
1886
1887
    /// Compares two spans in terms of how long they are. Negative spans are
1888
    /// considered shorter than the zero span.
1889
    ///
1890
    /// Two spans compare equal when they correspond to the same duration
1891
    /// of time, even if their individual fields are different. This is in
1892
    /// contrast to the `Eq` trait implementation of `SpanFieldwise` (created
1893
    /// by [`Span::fieldwise`]), which performs exact field-wise comparisons.
1894
    /// This split exists because the comparison provided by this routine is
1895
    /// "heavy" in that it may need to do datetime arithmetic to return an
1896
    /// answer. In contrast, the `Eq` trait implementation is "cheap."
1897
    ///
1898
    /// This routine accepts anything that implements `Into<SpanCompare>`.
1899
    /// There are some trait implementations that make using this routine
1900
    /// ergonomic:
1901
    ///
1902
    /// * `From<Span> for SpanCompare` compares the given span to this one.
1903
    /// * `From<(Span, civil::Date)> for SpanArithmetic` compares the given
1904
    /// span to this one relative to the given date. There are also `From`
1905
    /// implementations for `civil::DateTime` and `Zoned`.
1906
    ///
1907
    /// # Errors
1908
    ///
1909
    /// If either of the spans being compared have a non-zero calendar unit
1910
    /// (units bigger than hours), then this routine requires a relative
1911
    /// datetime. If one is not provided, then an error is returned.
1912
    ///
1913
    /// An error can also occur when adding either span to the relative
1914
    /// datetime given results in overflow.
1915
    ///
1916
    /// Callers may use [`SpanArithmetic::days_are_24_hours`] as a special
1917
    /// marker instead of providing a relative civil date to indicate that
1918
    /// all days should be 24 hours long. This also results in treating all
1919
    /// weeks as seven 24 hour days (168 hours).
1920
    ///
1921
    /// # Example
1922
    ///
1923
    /// ```
1924
    /// use jiff::ToSpan;
1925
    ///
1926
    /// let span1 = 3.hours();
1927
    /// let span2 = 180.minutes();
1928
    /// assert_eq!(span1.compare(span2)?, std::cmp::Ordering::Equal);
1929
    /// // But notice that the two spans are not equal via `Eq`:
1930
    /// assert_ne!(span1.fieldwise(), span2.fieldwise());
1931
    ///
1932
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1933
    /// ```
1934
    ///
1935
    /// # Example: negative spans are less than zero
1936
    ///
1937
    /// ```
1938
    /// use jiff::ToSpan;
1939
    ///
1940
    /// let span1 = -1.second();
1941
    /// let span2 = 0.seconds();
1942
    /// assert_eq!(span1.compare(span2)?, std::cmp::Ordering::Less);
1943
    ///
1944
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1945
    /// ```
1946
    ///
1947
    /// # Example: comparisons take DST into account
1948
    ///
1949
    /// When a relative datetime is time zone aware, then DST is taken into
1950
    /// account when comparing spans:
1951
    ///
1952
    /// ```
1953
    /// use jiff::{civil, ToSpan, Zoned};
1954
    ///
1955
    /// let span1 = 79.hours().minutes(10);
1956
    /// let span2 = 3.days().hours(7).seconds(630);
1957
    /// let span3 = 3.days().hours(6).minutes(50);
1958
    ///
1959
    /// let relative: Zoned = "2020-11-01T00-07[America/Los_Angeles]".parse()?;
1960
    /// let mut spans = [span1, span2, span3];
1961
    /// spans.sort_by(|s1, s2| s1.compare((s2, &relative)).unwrap());
1962
    /// assert_eq!(
1963
    ///     spans.map(|sp| sp.fieldwise()),
1964
    ///     [span1.fieldwise(), span3.fieldwise(), span2.fieldwise()],
1965
    /// );
1966
    ///
1967
    /// // Compare with the result of sorting without taking DST into account.
1968
    /// // We can that by providing a relative civil date:
1969
    /// let relative = civil::date(2020, 11, 1);
1970
    /// spans.sort_by(|s1, s2| s1.compare((s2, relative)).unwrap());
1971
    /// assert_eq!(
1972
    ///     spans.map(|sp| sp.fieldwise()),
1973
    ///     [span3.fieldwise(), span1.fieldwise(), span2.fieldwise()],
1974
    /// );
1975
    ///
1976
    /// # Ok::<(), Box<dyn std::error::Error>>(())
1977
    /// ```
1978
    ///
1979
    /// See the examples for [`Span::total`] if you want to sort spans without
1980
    /// an `unwrap()` call.
1981
    #[inline]
1982
0
    pub fn compare<'a, C: Into<SpanCompare<'a>>>(
1983
0
        &self,
1984
0
        options: C,
1985
0
    ) -> Result<Ordering, Error> {
1986
0
        let options: SpanCompare<'_> = options.into();
1987
0
        options.compare(*self)
1988
0
    }
1989
1990
    /// Returns a floating point number representing the total number of a
1991
    /// specific unit (as given) in this span. If the span is not evenly
1992
    /// divisible by the requested units, then the number returned may have a
1993
    /// fractional component.
1994
    ///
1995
    /// This routine accepts anything that implements `Into<SpanTotal>`. There
1996
    /// are some trait implementations that make using this routine ergonomic:
1997
    ///
1998
    /// * `From<Unit> for SpanTotal` computes a total for the given unit in
1999
    /// this span.
2000
    /// * `From<(Unit, civil::Date)> for SpanTotal` computes a total for the
2001
    /// given unit in this span, relative to the given date. There are also
2002
    /// `From` implementations for `civil::DateTime` and `Zoned`.
2003
    ///
2004
    /// # Errors
2005
    ///
2006
    /// If this span has any non-zero calendar unit (units bigger than hours),
2007
    /// then this routine requires a relative datetime. If one is not provided,
2008
    /// then an error is returned.
2009
    ///
2010
    /// An error can also occur when adding the span to the relative
2011
    /// datetime given results in overflow.
2012
    ///
2013
    /// Callers may use [`SpanArithmetic::days_are_24_hours`] as a special
2014
    /// marker instead of providing a relative civil date to indicate that
2015
    /// all days should be 24 hours long. This also results in treating all
2016
    /// weeks as seven 24 hour days (168 hours).
2017
    ///
2018
    /// # Example
2019
    ///
2020
    /// This example shows how to find the number of seconds in a particular
2021
    /// span:
2022
    ///
2023
    /// ```
2024
    /// use jiff::{ToSpan, Unit};
2025
    ///
2026
    /// let span = 3.hours().minutes(10);
2027
    /// assert_eq!(span.total(Unit::Second)?, 11_400.0);
2028
    ///
2029
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2030
    /// ```
2031
    ///
2032
    /// # Example: 24 hour days
2033
    ///
2034
    /// This shows how to find the total number of 24 hour days in
2035
    /// `123,456,789` seconds.
2036
    ///
2037
    /// ```
2038
    /// use jiff::{SpanTotal, ToSpan, Unit};
2039
    ///
2040
    /// let span = 123_456_789.seconds();
2041
    /// assert_eq!(
2042
    ///     span.total(SpanTotal::from(Unit::Day).days_are_24_hours())?,
2043
    ///     1428.8980208333332,
2044
    /// );
2045
    ///
2046
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2047
    /// ```
2048
    ///
2049
    /// # Example: DST is taken into account
2050
    ///
2051
    /// The month of March 2024 in `America/New_York` had 31 days, but one of
2052
    /// those days was 23 hours long due a transition into daylight saving
2053
    /// time:
2054
    ///
2055
    /// ```
2056
    /// use jiff::{civil::date, ToSpan, Unit};
2057
    ///
2058
    /// let span = 744.hours();
2059
    /// let relative = date(2024, 3, 1).in_tz("America/New_York")?;
2060
    /// // Because of the short day, 744 hours is actually a little *more* than
2061
    /// // 1 month starting from 2024-03-01.
2062
    /// assert_eq!(span.total((Unit::Month, &relative))?, 1.0013888888888889);
2063
    ///
2064
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2065
    /// ```
2066
    ///
2067
    /// Now compare what happens when the relative datetime is civil and not
2068
    /// time zone aware:
2069
    ///
2070
    /// ```
2071
    /// use jiff::{civil::date, ToSpan, Unit};
2072
    ///
2073
    /// let span = 744.hours();
2074
    /// let relative = date(2024, 3, 1);
2075
    /// assert_eq!(span.total((Unit::Month, relative))?, 1.0);
2076
    ///
2077
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2078
    /// ```
2079
    ///
2080
    /// # Example: infallible sorting
2081
    ///
2082
    /// The sorting example in [`Span::compare`] has to use `unwrap()` in
2083
    /// its `sort_by(..)` call because `Span::compare` may fail and there
2084
    /// is no "fallible" sorting routine in Rust's standard library (as of
2085
    /// 2024-07-07). While the ways in which `Span::compare` can fail for
2086
    /// a valid configuration are limited to overflow for "extreme" values, it
2087
    /// is possible to sort spans infallibly by computing floating point
2088
    /// representations for each span up-front:
2089
    ///
2090
    /// ```
2091
    /// use jiff::{civil::Date, ToSpan, Unit, Zoned};
2092
    ///
2093
    /// let span1 = 79.hours().minutes(10);
2094
    /// let span2 = 3.days().hours(7).seconds(630);
2095
    /// let span3 = 3.days().hours(6).minutes(50);
2096
    ///
2097
    /// let relative: Zoned = "2020-11-01T00-07[America/Los_Angeles]".parse()?;
2098
    /// let mut spans = [
2099
    ///     (span1, span1.total((Unit::Day, &relative))?),
2100
    ///     (span2, span2.total((Unit::Day, &relative))?),
2101
    ///     (span3, span3.total((Unit::Day, &relative))?),
2102
    /// ];
2103
    /// spans.sort_by(|&(_, total1), &(_, total2)| total1.total_cmp(&total2));
2104
    /// assert_eq!(
2105
    ///     spans.map(|(sp, _)| sp.fieldwise()),
2106
    ///     [span1.fieldwise(), span3.fieldwise(), span2.fieldwise()],
2107
    /// );
2108
    ///
2109
    /// // Compare with the result of sorting without taking DST into account.
2110
    /// // We do that here by providing a relative civil date.
2111
    /// let relative: Date = "2020-11-01".parse()?;
2112
    /// let mut spans = [
2113
    ///     (span1, span1.total((Unit::Day, relative))?),
2114
    ///     (span2, span2.total((Unit::Day, relative))?),
2115
    ///     (span3, span3.total((Unit::Day, relative))?),
2116
    /// ];
2117
    /// spans.sort_by(|&(_, total1), &(_, total2)| total1.total_cmp(&total2));
2118
    /// assert_eq!(
2119
    ///     spans.map(|(sp, _)| sp.fieldwise()),
2120
    ///     [span3.fieldwise(), span1.fieldwise(), span2.fieldwise()],
2121
    /// );
2122
    ///
2123
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2124
    /// ```
2125
    #[inline]
2126
0
    pub fn total<'a, T: Into<SpanTotal<'a>>>(
2127
0
        &self,
2128
0
        options: T,
2129
0
    ) -> Result<f64, Error> {
2130
0
        let options: SpanTotal<'_> = options.into();
2131
0
        options.total(*self)
2132
0
    }
2133
2134
    /// Returns a new span that is balanced and rounded.
2135
    ///
2136
    /// Rounding a span has a number of parameters, all of which are optional.
2137
    /// When no parameters are given, then no rounding or balancing is done,
2138
    /// and the span as given is returned. That is, it's a no-op.
2139
    ///
2140
    /// The parameters are, in brief:
2141
    ///
2142
    /// * [`SpanRound::largest`] sets the largest [`Unit`] that is allowed to
2143
    /// be non-zero in the span returned. When _only_ the largest unit is set,
2144
    /// rounding itself doesn't occur and instead the span is merely balanced.
2145
    /// * [`SpanRound::smallest`] sets the smallest [`Unit`] that is allowed to
2146
    /// be non-zero in the span returned. By default, it is set to
2147
    /// [`Unit::Nanosecond`], i.e., no rounding occurs. When the smallest unit
2148
    /// is set to something bigger than nanoseconds, then the non-zero units
2149
    /// in the span smaller than the smallest unit are used to determine how
2150
    /// the span should be rounded. For example, rounding `1 hour 59 minutes`
2151
    /// to the nearest hour using the default rounding mode would produce
2152
    /// `2 hours`.
2153
    /// * [`SpanRound::mode`] determines how to handle the remainder when
2154
    /// rounding. The default is [`RoundMode::HalfExpand`], which corresponds
2155
    /// to how you were taught to round in school. Alternative modes, like
2156
    /// [`RoundMode::Trunc`], exist too. For example, a truncating rounding of
2157
    /// `1 hour 59 minutes` to the nearest hour would produce `1 hour`.
2158
    /// * [`SpanRound::increment`] sets the rounding granularity to use for
2159
    /// the configured smallest unit. For example, if the smallest unit is
2160
    /// minutes and the increment is 5, then the span returned will always have
2161
    /// its minute units set to a multiple of `5`.
2162
    /// * [`SpanRound::relative`] sets the datetime from which to interpret the
2163
    /// span. This is required when rounding spans with calendar units (years,
2164
    /// months or weeks). When a relative datetime is time zone aware, then
2165
    /// rounding accounts for the fact that not all days are 24 hours long.
2166
    /// When a relative datetime is omitted or is civil (not time zone aware),
2167
    /// then days are always 24 hours long.
2168
    ///
2169
    /// # Constructing a [`SpanRound`]
2170
    ///
2171
    /// This routine accepts anything that implements `Into<SpanRound>`. There
2172
    /// are a few key trait implementations that make this convenient:
2173
    ///
2174
    /// * `From<Unit> for SpanRound` will construct a rounding configuration
2175
    /// where the smallest unit is set to the one given.
2176
    /// * `From<(Unit, i64)> for SpanRound` will construct a rounding
2177
    /// configuration where the smallest unit and the rounding increment are
2178
    /// set to the ones given.
2179
    ///
2180
    /// To set other options (like the largest unit, the rounding mode and the
2181
    /// relative datetime), one must explicitly create a `SpanRound` and pass
2182
    /// it to this routine.
2183
    ///
2184
    /// # Errors
2185
    ///
2186
    /// In general, there are two main ways for rounding to fail: an improper
2187
    /// configuration like trying to round a span with calendar units but
2188
    /// without a relative datetime, or when overflow occurs. Overflow can
2189
    /// occur when the span, added to the relative datetime if given, would
2190
    /// exceed the minimum or maximum datetime values. Overflow can also occur
2191
    /// if the span is too big to fit into the requested unit configuration.
2192
    /// For example, a span like `19_998.years()` cannot be represented with a
2193
    /// 64-bit integer number of nanoseconds.
2194
    ///
2195
    /// Callers may use [`SpanArithmetic::days_are_24_hours`] as a special
2196
    /// marker instead of providing a relative civil date to indicate that
2197
    /// all days should be 24 hours long. This also results in treating all
2198
    /// weeks as seven 24 hour days (168 hours).
2199
    ///
2200
    /// # Example: balancing
2201
    ///
2202
    /// This example demonstrates balancing, not rounding. And in particular,
2203
    /// this example shows how to balance a span as much as possible (i.e.,
2204
    /// with units of hours or smaller) without needing to specify a relative
2205
    /// datetime:
2206
    ///
2207
    /// ```
2208
    /// use jiff::{SpanRound, ToSpan, Unit};
2209
    ///
2210
    /// let span = 123_456_789_123_456_789i64.nanoseconds();
2211
    /// assert_eq!(
2212
    ///     span.round(SpanRound::new().largest(Unit::Hour))?.fieldwise(),
2213
    ///     34_293.hours().minutes(33).seconds(9)
2214
    ///         .milliseconds(123).microseconds(456).nanoseconds(789),
2215
    /// );
2216
    ///
2217
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2218
    /// ```
2219
    ///
2220
    /// Or you can opt into invariant 24-hour days (and 7-day weeks) without a
2221
    /// relative date with [`SpanRound::days_are_24_hours`]:
2222
    ///
2223
    /// ```
2224
    /// use jiff::{SpanRound, ToSpan, Unit};
2225
    ///
2226
    /// let span = 123_456_789_123_456_789i64.nanoseconds();
2227
    /// assert_eq!(
2228
    ///     span.round(
2229
    ///         SpanRound::new().largest(Unit::Day).days_are_24_hours(),
2230
    ///     )?.fieldwise(),
2231
    ///     1_428.days()
2232
    ///         .hours(21).minutes(33).seconds(9)
2233
    ///         .milliseconds(123).microseconds(456).nanoseconds(789),
2234
    /// );
2235
    ///
2236
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2237
    /// ```
2238
    ///
2239
    /// # Example: balancing and rounding
2240
    ///
2241
    /// This example is like the one before it, but where we round to the
2242
    /// nearest second:
2243
    ///
2244
    /// ```
2245
    /// use jiff::{SpanRound, ToSpan, Unit};
2246
    ///
2247
    /// let span = 123_456_789_123_456_789i64.nanoseconds();
2248
    /// assert_eq!(
2249
    ///     span.round(SpanRound::new().largest(Unit::Hour).smallest(Unit::Second))?,
2250
    ///     34_293.hours().minutes(33).seconds(9).fieldwise(),
2251
    /// );
2252
    ///
2253
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2254
    /// ```
2255
    ///
2256
    /// Or, just rounding to the nearest hour can make use of the
2257
    /// `From<Unit> for SpanRound` trait implementation:
2258
    ///
2259
    /// ```
2260
    /// use jiff::{ToSpan, Unit};
2261
    ///
2262
    /// let span = 123_456_789_123_456_789i64.nanoseconds();
2263
    /// assert_eq!(span.round(Unit::Hour)?, 34_294.hours().fieldwise());
2264
    ///
2265
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2266
    /// ```
2267
    ///
2268
    /// # Example: balancing with a relative datetime
2269
    ///
2270
    /// Even with calendar units, so long as a relative datetime is provided,
2271
    /// it's easy to turn days into bigger units:
2272
    ///
2273
    /// ```
2274
    /// use jiff::{civil::date, SpanRound, ToSpan, Unit};
2275
    ///
2276
    /// let span = 1_000.days();
2277
    /// let relative = date(2000, 1, 1);
2278
    /// let options = SpanRound::new().largest(Unit::Year).relative(relative);
2279
    /// assert_eq!(span.round(options)?, 2.years().months(8).days(26).fieldwise());
2280
    ///
2281
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2282
    /// ```
2283
    ///
2284
    /// # Example: round to the nearest half-hour
2285
    ///
2286
    /// ```
2287
    /// use jiff::{Span, ToSpan, Unit};
2288
    ///
2289
    /// let span: Span = "PT23h50m3.123s".parse()?;
2290
    /// assert_eq!(span.round((Unit::Minute, 30))?, 24.hours().fieldwise());
2291
    ///
2292
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2293
    /// ```
2294
    ///
2295
    /// # Example: yearly quarters in a span
2296
    ///
2297
    /// This example shows how to find how many full 3 month quarters are in a
2298
    /// particular span of time.
2299
    ///
2300
    /// ```
2301
    /// use jiff::{civil::date, RoundMode, SpanRound, ToSpan, Unit};
2302
    ///
2303
    /// let span1 = 10.months().days(15);
2304
    /// let round = SpanRound::new()
2305
    ///     .smallest(Unit::Month)
2306
    ///     .increment(3)
2307
    ///     .mode(RoundMode::Trunc)
2308
    ///     // A relative datetime must be provided when
2309
    ///     // rounding involves calendar units.
2310
    ///     .relative(date(2024, 1, 1));
2311
    /// let span2 = span1.round(round)?;
2312
    /// assert_eq!(span2.get_months() / 3, 3);
2313
    ///
2314
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2315
    /// ```
2316
    #[inline]
2317
0
    pub fn round<'a, R: Into<SpanRound<'a>>>(
2318
0
        self,
2319
0
        options: R,
2320
0
    ) -> Result<Span, Error> {
2321
0
        let options: SpanRound<'a> = options.into();
2322
0
        options.round(self)
2323
0
    }
2324
2325
    /// Converts a `Span` to a [`SignedDuration`] relative to the date given.
2326
    ///
2327
    /// In most cases, it is unlikely that you'll need to use this routine to
2328
    /// convert a `Span` to a `SignedDuration`. Namely, by default:
2329
    ///
2330
    /// * [`Zoned::until`] guarantees that the biggest non-zero unit is hours.
2331
    /// * [`Timestamp::until`] guarantees that the biggest non-zero unit is
2332
    /// seconds.
2333
    /// * [`DateTime::until`] guarantees that the biggest non-zero unit is
2334
    /// days.
2335
    /// * [`Date::until`] guarantees that the biggest non-zero unit is days.
2336
    /// * [`Time::until`] guarantees that the biggest non-zero unit is hours.
2337
    ///
2338
    /// In the above, only [`DateTime::until`] and [`Date::until`] return
2339
    /// calendar units by default. In which case, one may pass
2340
    /// [`SpanRelativeTo::days_are_24_hours`] or an actual relative date to
2341
    /// resolve the length of a day.
2342
    ///
2343
    /// Of course, any of the above can be changed by asking, for example,
2344
    /// `Zoned::until` to return units up to years.
2345
    ///
2346
    /// # Errors
2347
    ///
2348
    /// This returns an error if adding this span to the date given results in
2349
    /// overflow. This can also return an error if one uses
2350
    /// [`SpanRelativeTo::days_are_24_hours`] with a `Span` that has non-zero
2351
    /// units greater than weeks.
2352
    ///
2353
    /// # Example: converting a span with calendar units to a `SignedDuration`
2354
    ///
2355
    /// This compares the number of seconds in a non-leap year with a leap
2356
    /// year:
2357
    ///
2358
    /// ```
2359
    /// use jiff::{civil::date, SignedDuration, ToSpan};
2360
    ///
2361
    /// let span = 1.year();
2362
    ///
2363
    /// let duration = span.to_duration(date(2024, 1, 1))?;
2364
    /// assert_eq!(duration, SignedDuration::from_secs(31_622_400));
2365
    /// let duration = span.to_duration(date(2023, 1, 1))?;
2366
    /// assert_eq!(duration, SignedDuration::from_secs(31_536_000));
2367
    ///
2368
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2369
    /// ```
2370
    ///
2371
    /// # Example: converting a span without a relative datetime
2372
    ///
2373
    /// If for some reason it doesn't make sense to include a
2374
    /// relative datetime, you can use this routine to convert a
2375
    /// `Span` with units up to weeks to a `SignedDuration` via the
2376
    /// [`SpanRelativeTo::days_are_24_hours`] marker:
2377
    ///
2378
    /// ```
2379
    /// use jiff::{civil::date, SignedDuration, SpanRelativeTo, ToSpan};
2380
    ///
2381
    /// let span = 1.week().days(1);
2382
    ///
2383
    /// let duration = span.to_duration(SpanRelativeTo::days_are_24_hours())?;
2384
    /// assert_eq!(duration, SignedDuration::from_hours(192));
2385
    ///
2386
    /// # Ok::<(), Box<dyn std::error::Error>>(())
2387
    /// ```
2388
    #[inline]
2389
0
    pub fn to_duration<'a, R: Into<SpanRelativeTo<'a>>>(
2390
0
        &self,
2391
0
        relative: R,
2392
0
    ) -> Result<SignedDuration, Error> {
2393
0
        let max_unit = self.largest_unit();
2394
0
        let relative: SpanRelativeTo<'a> = relative.into();
2395
0
        let Some(result) = relative.to_relative(max_unit).transpose() else {
2396
0
            return Ok(self.to_duration_invariant());
2397
        };
2398
0
        let relspan = result
2399
0
            .and_then(|r| r.into_relative_span(Unit::Second, *self))
2400
0
            .with_context(|| match relative.kind {
2401
0
                SpanRelativeToKind::Civil(dt) => {
2402
0
                    err!(
2403
0
                        "could not compute normalized relative span \
2404
0
                         from datetime {dt} and span {self}",
2405
                    )
2406
                }
2407
0
                SpanRelativeToKind::Zoned(ref zdt) => {
2408
0
                    err!(
2409
0
                        "could not compute normalized relative span \
2410
0
                         from datetime {zdt} and span {self}",
2411
                    )
2412
                }
2413
                SpanRelativeToKind::DaysAre24Hours => {
2414
0
                    err!(
2415
0
                        "could not compute normalized relative span \
2416
0
                         from {self} when all days are assumed to be \
2417
0
                         24 hours",
2418
                    )
2419
                }
2420
0
            })?;
2421
0
        debug_assert!(relspan.span.largest_unit() <= Unit::Second);
2422
0
        Ok(relspan.span.to_duration_invariant())
2423
0
    }
2424
2425
    /// Converts an entirely invariant span to a `SignedDuration`.
2426
    ///
2427
    /// Callers must ensure that this span has no units greater than weeks.
2428
    /// If it does have non-zero units of days or weeks, then every day is
2429
    /// considered 24 hours and every week 7 days. Generally speaking, callers
2430
    /// should also ensure that if this span does have non-zero day/week units,
2431
    /// then callers have either provided a civil relative date or the special
2432
    /// `SpanRelativeTo::days_are_24_hours()` marker.
2433
    #[inline]
2434
0
    pub(crate) fn to_duration_invariant(&self) -> SignedDuration {
2435
        // This guarantees, at compile time, that a maximal invariant Span
2436
        // (that is, all units are days or lower and all units are set to their
2437
        // maximum values) will still balance out to a number of seconds that
2438
        // fits into a `i64`. This in turn implies that a `SignedDuration` can
2439
        // represent all possible invariant positive spans.
2440
        const _FITS_IN_U64: () = {
2441
            debug_assert!(
2442
                i64::MAX as i128
2443
                    > ((t::SpanWeeks::MAX
2444
                        * t::SECONDS_PER_CIVIL_WEEK.bound())
2445
                        + (t::SpanDays::MAX
2446
                            * t::SECONDS_PER_CIVIL_DAY.bound())
2447
                        + (t::SpanHours::MAX * t::SECONDS_PER_HOUR.bound())
2448
                        + (t::SpanMinutes::MAX
2449
                            * t::SECONDS_PER_MINUTE.bound())
2450
                        + t::SpanSeconds::MAX
2451
                        + (t::SpanMilliseconds::MAX
2452
                            / t::MILLIS_PER_SECOND.bound())
2453
                        + (t::SpanMicroseconds::MAX
2454
                            / t::MICROS_PER_SECOND.bound())
2455
                        + (t::SpanNanoseconds::MAX
2456
                            / t::NANOS_PER_SECOND.bound())),
2457
            );
2458
            ()
2459
        };
2460
2461
0
        let nanos = self.to_invariant_nanoseconds();
2462
0
        debug_assert!(
2463
0
            self.largest_unit() <= Unit::Week,
2464
0
            "units must be weeks or lower"
2465
        );
2466
2467
0
        let seconds = nanos / t::NANOS_PER_SECOND;
2468
0
        let seconds = i64::from(seconds);
2469
0
        let subsec_nanos = nanos % t::NANOS_PER_SECOND;
2470
        // OK because % 1_000_000_000 above guarantees that the result fits
2471
        // in a i32.
2472
0
        let subsec_nanos = i32::try_from(subsec_nanos).unwrap();
2473
2474
        // SignedDuration::new can panic if |subsec_nanos| >= 1_000_000_000
2475
        // and seconds == {i64::MIN,i64::MAX}. But this can never happen
2476
        // because we guaranteed by construction above that |subsec_nanos| <
2477
        // 1_000_000_000.
2478
0
        SignedDuration::new(seconds, subsec_nanos)
2479
0
    }
2480
}
2481
2482
/// Crate internal APIs that operate on ranged integer types.
2483
impl Span {
2484
    #[inline]
2485
256
    pub(crate) fn years_ranged(self, years: t::SpanYears) -> Span {
2486
256
        let mut span = Span { years: years.abs(), ..self };
2487
256
        span.sign = self.resign(years, &span);
2488
256
        span.units = span.units.set(Unit::Year, years == C(0));
2489
256
        span
2490
256
    }
2491
2492
    #[inline]
2493
216
    pub(crate) fn months_ranged(self, months: t::SpanMonths) -> Span {
2494
216
        let mut span = Span { months: months.abs(), ..self };
2495
216
        span.sign = self.resign(months, &span);
2496
216
        span.units = span.units.set(Unit::Month, months == C(0));
2497
216
        span
2498
216
    }
2499
2500
    #[inline]
2501
294
    pub(crate) fn weeks_ranged(self, weeks: t::SpanWeeks) -> Span {
2502
294
        let mut span = Span { weeks: weeks.abs(), ..self };
2503
294
        span.sign = self.resign(weeks, &span);
2504
294
        span.units = span.units.set(Unit::Week, weeks == C(0));
2505
294
        span
2506
294
    }
2507
2508
    #[inline]
2509
342
    pub(crate) fn days_ranged(self, days: t::SpanDays) -> Span {
2510
342
        let mut span = Span { days: days.abs(), ..self };
2511
342
        span.sign = self.resign(days, &span);
2512
342
        span.units = span.units.set(Unit::Day, days == C(0));
2513
342
        span
2514
342
    }
2515
2516
    #[inline]
2517
337
    pub(crate) fn hours_ranged(self, hours: t::SpanHours) -> Span {
2518
337
        let mut span = Span { hours: hours.abs(), ..self };
2519
337
        span.sign = self.resign(hours, &span);
2520
337
        span.units = span.units.set(Unit::Hour, hours == C(0));
2521
337
        span
2522
337
    }
2523
2524
    #[inline]
2525
458
    pub(crate) fn minutes_ranged(self, minutes: t::SpanMinutes) -> Span {
2526
458
        let mut span = Span { minutes: minutes.abs(), ..self };
2527
458
        span.sign = self.resign(minutes, &span);
2528
458
        span.units = span.units.set(Unit::Minute, minutes == C(0));
2529
458
        span
2530
458
    }
2531
2532
    #[inline]
2533
1.47k
    pub(crate) fn seconds_ranged(self, seconds: t::SpanSeconds) -> Span {
2534
1.47k
        let mut span = Span { seconds: seconds.abs(), ..self };
2535
1.47k
        span.sign = self.resign(seconds, &span);
2536
1.47k
        span.units = span.units.set(Unit::Second, seconds == C(0));
2537
1.47k
        span
2538
1.47k
    }
2539
2540
    #[inline]
2541
0
    fn milliseconds_ranged(self, milliseconds: t::SpanMilliseconds) -> Span {
2542
0
        let mut span = Span { milliseconds: milliseconds.abs(), ..self };
2543
0
        span.sign = self.resign(milliseconds, &span);
2544
0
        span.units = span.units.set(Unit::Millisecond, milliseconds == C(0));
2545
0
        span
2546
0
    }
2547
2548
    #[inline]
2549
0
    fn microseconds_ranged(self, microseconds: t::SpanMicroseconds) -> Span {
2550
0
        let mut span = Span { microseconds: microseconds.abs(), ..self };
2551
0
        span.sign = self.resign(microseconds, &span);
2552
0
        span.units = span.units.set(Unit::Microsecond, microseconds == C(0));
2553
0
        span
2554
0
    }
2555
2556
    #[inline]
2557
0
    pub(crate) fn nanoseconds_ranged(
2558
0
        self,
2559
0
        nanoseconds: t::SpanNanoseconds,
2560
0
    ) -> Span {
2561
0
        let mut span = Span { nanoseconds: nanoseconds.abs(), ..self };
2562
0
        span.sign = self.resign(nanoseconds, &span);
2563
0
        span.units = span.units.set(Unit::Nanosecond, nanoseconds == C(0));
2564
0
        span
2565
0
    }
2566
2567
    #[inline]
2568
0
    fn try_days_ranged(
2569
0
        self,
2570
0
        days: impl TryRInto<t::SpanDays>,
2571
0
    ) -> Result<Span, Error> {
2572
0
        let days = days.try_rinto("days")?;
2573
0
        Ok(self.days_ranged(days))
2574
0
    }
2575
2576
    #[inline]
2577
0
    pub(crate) fn try_hours_ranged(
2578
0
        self,
2579
0
        hours: impl TryRInto<t::SpanHours>,
2580
0
    ) -> Result<Span, Error> {
2581
0
        let hours = hours.try_rinto("hours")?;
2582
0
        Ok(self.hours_ranged(hours))
2583
0
    }
2584
2585
    #[inline]
2586
0
    pub(crate) fn try_minutes_ranged(
2587
0
        self,
2588
0
        minutes: impl TryRInto<t::SpanMinutes>,
2589
0
    ) -> Result<Span, Error> {
2590
0
        let minutes = minutes.try_rinto("minutes")?;
2591
0
        Ok(self.minutes_ranged(minutes))
2592
0
    }
2593
2594
    #[inline]
2595
0
    pub(crate) fn try_seconds_ranged(
2596
0
        self,
2597
0
        seconds: impl TryRInto<t::SpanSeconds>,
2598
0
    ) -> Result<Span, Error> {
2599
0
        let seconds = seconds.try_rinto("seconds")?;
2600
0
        Ok(self.seconds_ranged(seconds))
2601
0
    }
2602
2603
    #[inline]
2604
0
    pub(crate) fn try_milliseconds_ranged(
2605
0
        self,
2606
0
        milliseconds: impl TryRInto<t::SpanMilliseconds>,
2607
0
    ) -> Result<Span, Error> {
2608
0
        let milliseconds = milliseconds.try_rinto("milliseconds")?;
2609
0
        Ok(self.milliseconds_ranged(milliseconds))
2610
0
    }
2611
2612
    #[inline]
2613
0
    pub(crate) fn try_microseconds_ranged(
2614
0
        self,
2615
0
        microseconds: impl TryRInto<t::SpanMicroseconds>,
2616
0
    ) -> Result<Span, Error> {
2617
0
        let microseconds = microseconds.try_rinto("microseconds")?;
2618
0
        Ok(self.microseconds_ranged(microseconds))
2619
0
    }
2620
2621
    #[inline]
2622
0
    pub(crate) fn try_nanoseconds_ranged(
2623
0
        self,
2624
0
        nanoseconds: impl TryRInto<t::SpanNanoseconds>,
2625
0
    ) -> Result<Span, Error> {
2626
0
        let nanoseconds = nanoseconds.try_rinto("nanoseconds")?;
2627
0
        Ok(self.nanoseconds_ranged(nanoseconds))
2628
0
    }
2629
2630
    #[inline]
2631
0
    pub(crate) fn try_units_ranged(
2632
0
        self,
2633
0
        unit: Unit,
2634
0
        value: NoUnits,
2635
0
    ) -> Result<Span, Error> {
2636
0
        Ok(match unit {
2637
0
            Unit::Year => self.years_ranged(value.try_rinto("years")?),
2638
0
            Unit::Month => self.months_ranged(value.try_rinto("months")?),
2639
0
            Unit::Week => self.weeks_ranged(value.try_rinto("weeks")?),
2640
0
            Unit::Day => self.days_ranged(value.try_rinto("days")?),
2641
0
            Unit::Hour => self.hours_ranged(value.try_rinto("hours")?),
2642
0
            Unit::Minute => self.minutes_ranged(value.try_rinto("minutes")?),
2643
0
            Unit::Second => self.seconds_ranged(value.try_rinto("seconds")?),
2644
            Unit::Millisecond => {
2645
0
                self.milliseconds_ranged(value.try_rinto("milliseconds")?)
2646
            }
2647
            Unit::Microsecond => {
2648
0
                self.microseconds_ranged(value.try_rinto("microseconds")?)
2649
            }
2650
            Unit::Nanosecond => {
2651
0
                self.nanoseconds_ranged(value.try_rinto("nanoseconds")?)
2652
            }
2653
        })
2654
0
    }
2655
2656
    #[inline]
2657
1.99k
    pub(crate) fn get_years_ranged(&self) -> t::SpanYears {
2658
1.99k
        self.years * self.sign
2659
1.99k
    }
2660
2661
    #[inline]
2662
1.98k
    pub(crate) fn get_months_ranged(&self) -> t::SpanMonths {
2663
1.98k
        self.months * self.sign
2664
1.98k
    }
2665
2666
    #[inline]
2667
2.99k
    pub(crate) fn get_weeks_ranged(&self) -> t::SpanWeeks {
2668
2.99k
        self.weeks * self.sign
2669
2.99k
    }
2670
2671
    #[inline]
2672
3.34k
    pub(crate) fn get_days_ranged(&self) -> t::SpanDays {
2673
3.34k
        self.days * self.sign
2674
3.34k
    }
2675
2676
    #[inline]
2677
2.67k
    pub(crate) fn get_hours_ranged(&self) -> t::SpanHours {
2678
2.67k
        self.hours * self.sign
2679
2.67k
    }
2680
2681
    #[inline]
2682
2.82k
    pub(crate) fn get_minutes_ranged(&self) -> t::SpanMinutes {
2683
2.82k
        self.minutes * self.sign
2684
2.82k
    }
2685
2686
    #[inline]
2687
2.52k
    pub(crate) fn get_seconds_ranged(&self) -> t::SpanSeconds {
2688
2.52k
        self.seconds * self.sign
2689
2.52k
    }
2690
2691
    #[inline]
2692
2.52k
    pub(crate) fn get_milliseconds_ranged(&self) -> t::SpanMilliseconds {
2693
2.52k
        self.milliseconds * self.sign
2694
2.52k
    }
2695
2696
    #[inline]
2697
2.52k
    pub(crate) fn get_microseconds_ranged(&self) -> t::SpanMicroseconds {
2698
2.52k
        self.microseconds * self.sign
2699
2.52k
    }
2700
2701
    #[inline]
2702
2.52k
    pub(crate) fn get_nanoseconds_ranged(&self) -> t::SpanNanoseconds {
2703
2.52k
        self.nanoseconds * self.sign
2704
2.52k
    }
2705
2706
    #[inline]
2707
4.84k
    fn get_sign_ranged(&self) -> ri8<-1, 1> {
2708
4.84k
        self.sign
2709
4.84k
    }
2710
2711
    #[inline]
2712
0
    fn get_units_ranged(&self, unit: Unit) -> NoUnits {
2713
0
        match unit {
2714
0
            Unit::Year => self.get_years_ranged().rinto(),
2715
0
            Unit::Month => self.get_months_ranged().rinto(),
2716
0
            Unit::Week => self.get_weeks_ranged().rinto(),
2717
0
            Unit::Day => self.get_days_ranged().rinto(),
2718
0
            Unit::Hour => self.get_hours_ranged().rinto(),
2719
0
            Unit::Minute => self.get_minutes_ranged().rinto(),
2720
0
            Unit::Second => self.get_seconds_ranged().rinto(),
2721
0
            Unit::Millisecond => self.get_milliseconds_ranged().rinto(),
2722
0
            Unit::Microsecond => self.get_microseconds_ranged().rinto(),
2723
0
            Unit::Nanosecond => self.get_nanoseconds_ranged().rinto(),
2724
        }
2725
0
    }
2726
}
2727
2728
/// Crate internal helper routines.
2729
impl Span {
2730
    /// Converts the given number of nanoseconds to a `Span` whose units do not
2731
    /// exceed `largest`.
2732
    ///
2733
    /// Note that `largest` is capped at `Unit::Week`. Note though that if
2734
    /// any unit greater than `Unit::Week` is given, then it is treated as
2735
    /// `Unit::Day`. The only way to get weeks in the `Span` returned is to
2736
    /// specifically request `Unit::Week`.
2737
    ///
2738
    /// And also note that days in this context are civil days. That is, they
2739
    /// are always 24 hours long. Callers needing to deal with variable length
2740
    /// days should do so outside of this routine and should not provide a
2741
    /// `largest` unit bigger than `Unit::Hour`.
2742
0
    pub(crate) fn from_invariant_nanoseconds(
2743
0
        largest: Unit,
2744
0
        nanos: NoUnits128,
2745
0
    ) -> Result<Span, Error> {
2746
0
        let mut span = Span::new();
2747
0
        match largest {
2748
            Unit::Week => {
2749
0
                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2750
0
                span = span.try_nanoseconds_ranged(
2751
0
                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2752
0
                )?;
2753
0
                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2754
0
                span = span.try_microseconds_ranged(
2755
0
                    micros.rem_ceil(t::MICROS_PER_MILLI),
2756
0
                )?;
2757
0
                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2758
0
                span = span.try_milliseconds_ranged(
2759
0
                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2760
0
                )?;
2761
0
                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2762
0
                span = span.try_seconds_ranged(
2763
0
                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2764
0
                )?;
2765
0
                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2766
0
                span = span
2767
0
                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2768
0
                let days = hours.div_ceil(t::HOURS_PER_CIVIL_DAY);
2769
0
                span = span.try_hours_ranged(
2770
0
                    hours.rem_ceil(t::HOURS_PER_CIVIL_DAY),
2771
0
                )?;
2772
0
                let weeks = days.div_ceil(t::DAYS_PER_CIVIL_WEEK);
2773
0
                span = span
2774
0
                    .try_days_ranged(days.rem_ceil(t::DAYS_PER_CIVIL_WEEK))?;
2775
0
                span = span.weeks_ranged(weeks.try_rinto("weeks")?);
2776
0
                Ok(span)
2777
            }
2778
            Unit::Year | Unit::Month | Unit::Day => {
2779
                // Unit::Year | Unit::Month | Unit::Week | Unit::Day => {
2780
0
                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2781
0
                span = span.try_nanoseconds_ranged(
2782
0
                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2783
0
                )?;
2784
0
                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2785
0
                span = span.try_microseconds_ranged(
2786
0
                    micros.rem_ceil(t::MICROS_PER_MILLI),
2787
0
                )?;
2788
0
                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2789
0
                span = span.try_milliseconds_ranged(
2790
0
                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2791
0
                )?;
2792
0
                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2793
0
                span = span.try_seconds_ranged(
2794
0
                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2795
0
                )?;
2796
0
                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2797
0
                span = span
2798
0
                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2799
0
                let days = hours.div_ceil(t::HOURS_PER_CIVIL_DAY);
2800
0
                span = span.try_hours_ranged(
2801
0
                    hours.rem_ceil(t::HOURS_PER_CIVIL_DAY),
2802
0
                )?;
2803
0
                span = span.try_days_ranged(days)?;
2804
0
                Ok(span)
2805
            }
2806
            Unit::Hour => {
2807
0
                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2808
0
                span = span.try_nanoseconds_ranged(
2809
0
                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2810
0
                )?;
2811
0
                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2812
0
                span = span.try_microseconds_ranged(
2813
0
                    micros.rem_ceil(t::MICROS_PER_MILLI),
2814
0
                )?;
2815
0
                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2816
0
                span = span.try_milliseconds_ranged(
2817
0
                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2818
0
                )?;
2819
0
                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2820
0
                span = span.try_seconds_ranged(
2821
0
                    secs.rem_ceil(t::SECONDS_PER_MINUTE),
2822
0
                )?;
2823
0
                let hours = mins.div_ceil(t::MINUTES_PER_HOUR);
2824
0
                span = span
2825
0
                    .try_minutes_ranged(mins.rem_ceil(t::MINUTES_PER_HOUR))?;
2826
0
                span = span.try_hours_ranged(hours)?;
2827
0
                Ok(span)
2828
            }
2829
            Unit::Minute => {
2830
0
                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2831
0
                span = span.try_nanoseconds_ranged(
2832
0
                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2833
0
                )?;
2834
0
                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2835
0
                span = span.try_microseconds_ranged(
2836
0
                    micros.rem_ceil(t::MICROS_PER_MILLI),
2837
0
                )?;
2838
0
                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2839
0
                span = span.try_milliseconds_ranged(
2840
0
                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2841
0
                )?;
2842
0
                let mins = secs.div_ceil(t::SECONDS_PER_MINUTE);
2843
                span =
2844
0
                    span.try_seconds(secs.rem_ceil(t::SECONDS_PER_MINUTE))?;
2845
0
                span = span.try_minutes_ranged(mins)?;
2846
0
                Ok(span)
2847
            }
2848
            Unit::Second => {
2849
0
                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2850
0
                span = span.try_nanoseconds_ranged(
2851
0
                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2852
0
                )?;
2853
0
                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2854
0
                span = span.try_microseconds_ranged(
2855
0
                    micros.rem_ceil(t::MICROS_PER_MILLI),
2856
0
                )?;
2857
0
                let secs = millis.div_ceil(t::MILLIS_PER_SECOND);
2858
0
                span = span.try_milliseconds_ranged(
2859
0
                    millis.rem_ceil(t::MILLIS_PER_SECOND),
2860
0
                )?;
2861
0
                span = span.try_seconds_ranged(secs)?;
2862
0
                Ok(span)
2863
            }
2864
            Unit::Millisecond => {
2865
0
                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2866
0
                span = span.try_nanoseconds_ranged(
2867
0
                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2868
0
                )?;
2869
0
                let millis = micros.div_ceil(t::MICROS_PER_MILLI);
2870
0
                span = span.try_microseconds_ranged(
2871
0
                    micros.rem_ceil(t::MICROS_PER_MILLI),
2872
0
                )?;
2873
0
                span = span.try_milliseconds_ranged(millis)?;
2874
0
                Ok(span)
2875
            }
2876
            Unit::Microsecond => {
2877
0
                let micros = nanos.div_ceil(t::NANOS_PER_MICRO);
2878
0
                span = span.try_nanoseconds_ranged(
2879
0
                    nanos.rem_ceil(t::NANOS_PER_MICRO),
2880
0
                )?;
2881
0
                span = span.try_microseconds_ranged(micros)?;
2882
0
                Ok(span)
2883
            }
2884
            Unit::Nanosecond => {
2885
0
                span = span.try_nanoseconds_ranged(nanos)?;
2886
0
                Ok(span)
2887
            }
2888
        }
2889
0
    }
2890
2891
    /// Converts the non-variable units of this `Span` to a total number of
2892
    /// nanoseconds.
2893
    ///
2894
    /// This includes days and weeks, even though they can be of irregular
2895
    /// length during time zone transitions. If this applies, then callers
2896
    /// should set the days and weeks to `0` before calling this routine.
2897
    ///
2898
    /// All units above weeks are always ignored.
2899
    #[inline]
2900
1.06k
    pub(crate) fn to_invariant_nanoseconds(&self) -> NoUnits128 {
2901
1.06k
        let mut nanos = NoUnits128::rfrom(self.get_nanoseconds_ranged());
2902
1.06k
        nanos += NoUnits128::rfrom(self.get_microseconds_ranged())
2903
1.06k
            * t::NANOS_PER_MICRO;
2904
1.06k
        nanos += NoUnits128::rfrom(self.get_milliseconds_ranged())
2905
1.06k
            * t::NANOS_PER_MILLI;
2906
1.06k
        nanos +=
2907
1.06k
            NoUnits128::rfrom(self.get_seconds_ranged()) * t::NANOS_PER_SECOND;
2908
1.06k
        nanos +=
2909
1.06k
            NoUnits128::rfrom(self.get_minutes_ranged()) * t::NANOS_PER_MINUTE;
2910
1.06k
        nanos +=
2911
1.06k
            NoUnits128::rfrom(self.get_hours_ranged()) * t::NANOS_PER_HOUR;
2912
1.06k
        nanos +=
2913
1.06k
            NoUnits128::rfrom(self.get_days_ranged()) * t::NANOS_PER_CIVIL_DAY;
2914
1.06k
        nanos += NoUnits128::rfrom(self.get_weeks_ranged())
2915
1.06k
            * t::NANOS_PER_CIVIL_WEEK;
2916
1.06k
        nanos
2917
1.06k
    }
2918
2919
    /// Converts the non-variable units of this `Span` to a total number of
2920
    /// seconds if there is no fractional second component. Otherwise,
2921
    /// `None` is returned.
2922
    ///
2923
    /// This is useful for short-circuiting in arithmetic operations when
2924
    /// it's faster to only deal with seconds. And in particular, acknowledges
2925
    /// that nanosecond precision durations are somewhat rare.
2926
    ///
2927
    /// This includes days and weeks, even though they can be of irregular
2928
    /// length during time zone transitions. If this applies, then callers
2929
    /// should set the days and weeks to `0` before calling this routine.
2930
    ///
2931
    /// All units above weeks are always ignored.
2932
    #[inline]
2933
0
    pub(crate) fn to_invariant_seconds(&self) -> Option<NoUnits> {
2934
0
        if self.has_fractional_seconds() {
2935
0
            return None;
2936
0
        }
2937
0
        let mut seconds = NoUnits::rfrom(self.get_seconds_ranged());
2938
0
        seconds +=
2939
0
            NoUnits::rfrom(self.get_minutes_ranged()) * t::SECONDS_PER_MINUTE;
2940
0
        seconds +=
2941
0
            NoUnits::rfrom(self.get_hours_ranged()) * t::SECONDS_PER_HOUR;
2942
0
        seconds +=
2943
0
            NoUnits::rfrom(self.get_days_ranged()) * t::SECONDS_PER_CIVIL_DAY;
2944
0
        seconds += NoUnits::rfrom(self.get_weeks_ranged())
2945
0
            * t::SECONDS_PER_CIVIL_WEEK;
2946
0
        Some(seconds)
2947
0
    }
2948
2949
    /// Rebalances the invariant units (days or lower) on this span so that
2950
    /// the largest possible non-zero unit is the one given.
2951
    ///
2952
    /// Units above day are ignored and dropped.
2953
    ///
2954
    /// If the given unit is greater than days, then it is treated as-if it
2955
    /// were days.
2956
    ///
2957
    /// # Errors
2958
    ///
2959
    /// This can return an error in the case of lop-sided units. For example,
2960
    /// if this span has maximal values for all units, then rebalancing is
2961
    /// not possible because the number of days after balancing would exceed
2962
    /// the limit.
2963
    #[cfg(test)] // currently only used in zic parser?
2964
    #[inline]
2965
    pub(crate) fn rebalance(self, unit: Unit) -> Result<Span, Error> {
2966
        Span::from_invariant_nanoseconds(unit, self.to_invariant_nanoseconds())
2967
    }
2968
2969
    /// Returns true if and only if this span has at least one non-zero
2970
    /// fractional second unit.
2971
    #[inline]
2972
0
    pub(crate) fn has_fractional_seconds(&self) -> bool {
2973
0
        self.milliseconds != C(0)
2974
0
            || self.microseconds != C(0)
2975
0
            || self.nanoseconds != C(0)
2976
0
    }
2977
2978
    /// Returns an equivalent span, but with all non-calendar (units below
2979
    /// days) set to zero.
2980
    #[cfg_attr(feature = "perf-inline", inline(always))]
2981
1.78k
    pub(crate) fn only_calendar(self) -> Span {
2982
1.78k
        let mut span = self;
2983
1.78k
        span.hours = t::SpanHours::N::<0>();
2984
1.78k
        span.minutes = t::SpanMinutes::N::<0>();
2985
1.78k
        span.seconds = t::SpanSeconds::N::<0>();
2986
1.78k
        span.milliseconds = t::SpanMilliseconds::N::<0>();
2987
1.78k
        span.microseconds = t::SpanMicroseconds::N::<0>();
2988
1.78k
        span.nanoseconds = t::SpanNanoseconds::N::<0>();
2989
1.78k
        if span.sign != C(0)
2990
1.69k
            && span.years == C(0)
2991
1.54k
            && span.months == C(0)
2992
1.42k
            && span.weeks == C(0)
2993
1.25k
            && span.days == C(0)
2994
1.06k
        {
2995
1.06k
            span.sign = t::Sign::N::<0>();
2996
1.06k
        }
2997
1.78k
        span.units = span.units.only_calendar();
2998
1.78k
        span
2999
1.78k
    }
3000
3001
    /// Returns an equivalent span, but with all calendar (units above
3002
    /// hours) set to zero.
3003
    #[cfg_attr(feature = "perf-inline", inline(always))]
3004
629
    pub(crate) fn only_time(self) -> Span {
3005
629
        let mut span = self;
3006
629
        span.years = t::SpanYears::N::<0>();
3007
629
        span.months = t::SpanMonths::N::<0>();
3008
629
        span.weeks = t::SpanWeeks::N::<0>();
3009
629
        span.days = t::SpanDays::N::<0>();
3010
629
        if span.sign != C(0)
3011
629
            && span.hours == C(0)
3012
629
            && span.minutes == C(0)
3013
629
            && span.seconds == C(0)
3014
629
            && span.milliseconds == C(0)
3015
629
            && span.microseconds == C(0)
3016
629
            && span.nanoseconds == C(0)
3017
629
        {
3018
629
            span.sign = t::Sign::N::<0>();
3019
629
        }
3020
629
        span.units = span.units.only_time();
3021
629
        span
3022
629
    }
3023
3024
    /// Returns an equivalent span, but with all units greater than or equal to
3025
    /// the one given set to zero.
3026
    #[cfg_attr(feature = "perf-inline", inline(always))]
3027
0
    pub(crate) fn only_lower(self, unit: Unit) -> Span {
3028
0
        let mut span = self;
3029
        // Unit::Nanosecond is the minimum, so nothing can be smaller than it.
3030
0
        if unit <= Unit::Microsecond {
3031
0
            span = span.microseconds_ranged(C(0).rinto());
3032
0
        }
3033
0
        if unit <= Unit::Millisecond {
3034
0
            span = span.milliseconds_ranged(C(0).rinto());
3035
0
        }
3036
0
        if unit <= Unit::Second {
3037
0
            span = span.seconds_ranged(C(0).rinto());
3038
0
        }
3039
0
        if unit <= Unit::Minute {
3040
0
            span = span.minutes_ranged(C(0).rinto());
3041
0
        }
3042
0
        if unit <= Unit::Hour {
3043
0
            span = span.hours_ranged(C(0).rinto());
3044
0
        }
3045
0
        if unit <= Unit::Day {
3046
0
            span = span.days_ranged(C(0).rinto());
3047
0
        }
3048
0
        if unit <= Unit::Week {
3049
0
            span = span.weeks_ranged(C(0).rinto());
3050
0
        }
3051
0
        if unit <= Unit::Month {
3052
0
            span = span.months_ranged(C(0).rinto());
3053
0
        }
3054
0
        if unit <= Unit::Year {
3055
0
            span = span.years_ranged(C(0).rinto());
3056
0
        }
3057
0
        span
3058
0
    }
3059
3060
    /// Returns an equivalent span, but with all units less than the one given
3061
    /// set to zero.
3062
    #[cfg_attr(feature = "perf-inline", inline(always))]
3063
0
    pub(crate) fn without_lower(self, unit: Unit) -> Span {
3064
0
        let mut span = self;
3065
0
        if unit > Unit::Nanosecond {
3066
0
            span = span.nanoseconds_ranged(C(0).rinto());
3067
0
        }
3068
0
        if unit > Unit::Microsecond {
3069
0
            span = span.microseconds_ranged(C(0).rinto());
3070
0
        }
3071
0
        if unit > Unit::Millisecond {
3072
0
            span = span.milliseconds_ranged(C(0).rinto());
3073
0
        }
3074
0
        if unit > Unit::Second {
3075
0
            span = span.seconds_ranged(C(0).rinto());
3076
0
        }
3077
0
        if unit > Unit::Minute {
3078
0
            span = span.minutes_ranged(C(0).rinto());
3079
0
        }
3080
0
        if unit > Unit::Hour {
3081
0
            span = span.hours_ranged(C(0).rinto());
3082
0
        }
3083
0
        if unit > Unit::Day {
3084
0
            span = span.days_ranged(C(0).rinto());
3085
0
        }
3086
0
        if unit > Unit::Week {
3087
0
            span = span.weeks_ranged(C(0).rinto());
3088
0
        }
3089
0
        if unit > Unit::Month {
3090
0
            span = span.months_ranged(C(0).rinto());
3091
0
        }
3092
        // Unit::Year is the max, so nothing can be bigger than it.
3093
0
        span
3094
0
    }
3095
3096
    /// Returns an error corresponding to the smallest non-time non-zero unit.
3097
    ///
3098
    /// If all non-time units are zero, then this returns `None`.
3099
    #[cfg_attr(feature = "perf-inline", inline(always))]
3100
1.49k
    pub(crate) fn smallest_non_time_non_zero_unit_error(
3101
1.49k
        &self,
3102
1.49k
    ) -> Option<Error> {
3103
1.49k
        let non_time_unit = self.largest_calendar_unit()?;
3104
0
        Some(err!(
3105
0
            "operation can only be performed with units of hours \
3106
0
             or smaller, but found non-zero {unit} units \
3107
0
             (operations on `Timestamp`, `tz::Offset` and `civil::Time` \
3108
0
              don't support calendar units in a `Span`)",
3109
0
            unit = non_time_unit.singular(),
3110
0
        ))
3111
1.49k
    }
3112
3113
    /// Returns the largest non-zero calendar unit, or `None` if there are no
3114
    /// non-zero calendar units.
3115
    #[inline]
3116
1.49k
    pub(crate) fn largest_calendar_unit(&self) -> Option<Unit> {
3117
1.49k
        self.units().only_calendar().largest_unit()
3118
1.49k
    }
3119
3120
    /// Returns the largest non-zero unit in this span.
3121
    ///
3122
    /// If all components of this span are zero, then `Unit::Nanosecond` is
3123
    /// returned.
3124
    #[inline]
3125
0
    pub(crate) fn largest_unit(&self) -> Unit {
3126
0
        self.units().largest_unit().unwrap_or(Unit::Nanosecond)
3127
0
    }
3128
3129
    /// Returns the set of units on this `Span`.
3130
    #[inline]
3131
2.99k
    pub(crate) fn units(&self) -> UnitSet {
3132
2.99k
        self.units
3133
2.99k
    }
3134
3135
    /// Returns a string containing the value of all non-zero fields.
3136
    ///
3137
    /// This is useful for debugging. Normally, this would be the "alternate"
3138
    /// debug impl (perhaps), but that's what insta uses and I preferred having
3139
    /// the friendly format used there since it is much more terse.
3140
    #[cfg(feature = "alloc")]
3141
    #[allow(dead_code)]
3142
0
    pub(crate) fn debug(&self) -> alloc::string::String {
3143
        use core::fmt::Write;
3144
3145
0
        let mut buf = alloc::string::String::new();
3146
0
        write!(buf, "Span {{ sign: {:?}, units: {:?}", self.sign, self.units)
3147
0
            .unwrap();
3148
0
        if self.years != C(0) {
3149
0
            write!(buf, ", years: {:?}", self.years).unwrap();
3150
0
        }
3151
0
        if self.months != C(0) {
3152
0
            write!(buf, ", months: {:?}", self.months).unwrap();
3153
0
        }
3154
0
        if self.weeks != C(0) {
3155
0
            write!(buf, ", weeks: {:?}", self.weeks).unwrap();
3156
0
        }
3157
0
        if self.days != C(0) {
3158
0
            write!(buf, ", days: {:?}", self.days).unwrap();
3159
0
        }
3160
0
        if self.hours != C(0) {
3161
0
            write!(buf, ", hours: {:?}", self.hours).unwrap();
3162
0
        }
3163
0
        if self.minutes != C(0) {
3164
0
            write!(buf, ", minutes: {:?}", self.minutes).unwrap();
3165
0
        }
3166
0
        if self.seconds != C(0) {
3167
0
            write!(buf, ", seconds: {:?}", self.seconds).unwrap();
3168
0
        }
3169
0
        if self.milliseconds != C(0) {
3170
0
            write!(buf, ", milliseconds: {:?}", self.milliseconds).unwrap();
3171
0
        }
3172
0
        if self.microseconds != C(0) {
3173
0
            write!(buf, ", microseconds: {:?}", self.microseconds).unwrap();
3174
0
        }
3175
0
        if self.nanoseconds != C(0) {
3176
0
            write!(buf, ", nanoseconds: {:?}", self.nanoseconds).unwrap();
3177
0
        }
3178
0
        write!(buf, " }}").unwrap();
3179
0
        buf
3180
0
    }
3181
3182
    /// Given some new units to set on this span and the span updates with the
3183
    /// new units, this determines the what the sign of `new` should be.
3184
    #[inline]
3185
3.38k
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
3.38k
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
3.38k
            if units < C(0) {
3189
1.06k
                return Sign::N::<-1>();
3190
2.31k
            }
3191
2.31k
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
2.31k
            if units == C(0) {
3197
118
                new_is_zero = new.years == C(0)
3198
118
                    && new.months == C(0)
3199
118
                    && new.weeks == C(0)
3200
118
                    && new.days == C(0)
3201
118
                    && new.hours == C(0)
3202
118
                    && new.minutes == C(0)
3203
118
                    && new.seconds == C(0)
3204
118
                    && new.milliseconds == C(0)
3205
118
                    && new.microseconds == C(0)
3206
118
                    && new.nanoseconds == C(0);
3207
2.19k
            }
3208
2.31k
            match (span.is_zero(), new_is_zero) {
3209
118
                (_, true) => Sign::N::<0>(),
3210
2.19k
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
0
                (false, false) => new.sign,
3214
            }
3215
3.38k
        }
3216
3.38k
        imp(self, units.rinto(), new)
3217
3.38k
    }
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri16<-19998, 19998>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-239976, 239976>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-7304484, 7304484>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-175307616, 175307616>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-1043497, 1043497>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000, 631107417600000>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-10518456960, 10518456960>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000000, 631107417600000000>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600, 631107417600>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri16<-19998, 19998>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-239976, 239976>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-7304484, 7304484>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-175307616, 175307616>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-1043497, 1043497>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000, 631107417600000>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-10518456960, 10518456960>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-9223372036854775807, 9223372036854775807>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000000, 631107417600000000>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600, 631107417600>>
<jiff::span::Span>::resign::<jiff::util::rangeint::ri16<-19998, 19998>>
Line
Count
Source
3185
192
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
192
        imp(self, units.rinto(), new)
3217
192
    }
<jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-239976, 239976>>
Line
Count
Source
3185
165
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
165
        imp(self, units.rinto(), new)
3217
165
    }
<jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-7304484, 7304484>>
Line
Count
Source
3185
269
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
269
        imp(self, units.rinto(), new)
3217
269
    }
<jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-175307616, 175307616>>
Line
Count
Source
3185
230
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
230
        imp(self, units.rinto(), new)
3217
230
    }
<jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-1043497, 1043497>>
Line
Count
Source
3185
222
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
222
        imp(self, units.rinto(), new)
3217
222
    }
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000, 631107417600000>>
<jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-10518456960, 10518456960>>
Line
Count
Source
3185
319
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
319
        imp(self, units.rinto(), new)
3217
319
    }
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000000, 631107417600000000>>
<jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600, 631107417600>>
Line
Count
Source
3185
1.07k
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
1.07k
        imp(self, units.rinto(), new)
3217
1.07k
    }
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri16<-19998, 19998>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-239976, 239976>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-7304484, 7304484>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-175307616, 175307616>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-1043497, 1043497>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000, 631107417600000>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-10518456960, 10518456960>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000000, 631107417600000000>>
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600, 631107417600>>
<jiff::span::Span>::resign::<jiff::util::rangeint::ri16<-19998, 19998>>
Line
Count
Source
3185
64
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
64
        imp(self, units.rinto(), new)
3217
64
    }
<jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-239976, 239976>>
Line
Count
Source
3185
51
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
51
        imp(self, units.rinto(), new)
3217
51
    }
<jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-7304484, 7304484>>
Line
Count
Source
3185
73
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
73
        imp(self, units.rinto(), new)
3217
73
    }
<jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-175307616, 175307616>>
Line
Count
Source
3185
107
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
107
        imp(self, units.rinto(), new)
3217
107
    }
<jiff::span::Span>::resign::<jiff::util::rangeint::ri32<-1043497, 1043497>>
Line
Count
Source
3185
72
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
72
        imp(self, units.rinto(), new)
3217
72
    }
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000, 631107417600000>>
<jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-10518456960, 10518456960>>
Line
Count
Source
3185
139
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
139
        imp(self, units.rinto(), new)
3217
139
    }
Unexecuted instantiation: <jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600000000, 631107417600000000>>
<jiff::span::Span>::resign::<jiff::util::rangeint::ri64<-631107417600, 631107417600>>
Line
Count
Source
3185
403
    fn resign(&self, units: impl RInto<NoUnits>, new: &Span) -> Sign {
3186
        fn imp(span: &Span, units: NoUnits, new: &Span) -> Sign {
3187
            // Negative units anywhere always makes the entire span negative.
3188
            if units < C(0) {
3189
                return Sign::N::<-1>();
3190
            }
3191
            let mut new_is_zero = new.sign == C(0) && units == C(0);
3192
            // When `units == 0` and it was previously non-zero, then
3193
            // `new.sign` won't be `0` and thus `new_is_zero` will be false
3194
            // when it should be true. So in this case, we need to re-check all
3195
            // the units to set the sign correctly.
3196
            if units == C(0) {
3197
                new_is_zero = new.years == C(0)
3198
                    && new.months == C(0)
3199
                    && new.weeks == C(0)
3200
                    && new.days == C(0)
3201
                    && new.hours == C(0)
3202
                    && new.minutes == C(0)
3203
                    && new.seconds == C(0)
3204
                    && new.milliseconds == C(0)
3205
                    && new.microseconds == C(0)
3206
                    && new.nanoseconds == C(0);
3207
            }
3208
            match (span.is_zero(), new_is_zero) {
3209
                (_, true) => Sign::N::<0>(),
3210
                (true, false) => units.signum().rinto(),
3211
                // If the old and new span are both non-zero, and we know our new
3212
                // units are not negative, then the sign remains unchanged.
3213
                (false, false) => new.sign,
3214
            }
3215
        }
3216
403
        imp(self, units.rinto(), new)
3217
403
    }
3218
}
3219
3220
impl Default for Span {
3221
    #[inline]
3222
5.20k
    fn default() -> Span {
3223
5.20k
        Span {
3224
5.20k
            sign: ri8::N::<0>(),
3225
5.20k
            units: UnitSet::empty(),
3226
5.20k
            years: C(0).rinto(),
3227
5.20k
            months: C(0).rinto(),
3228
5.20k
            weeks: C(0).rinto(),
3229
5.20k
            days: C(0).rinto(),
3230
5.20k
            hours: C(0).rinto(),
3231
5.20k
            minutes: C(0).rinto(),
3232
5.20k
            seconds: C(0).rinto(),
3233
5.20k
            milliseconds: C(0).rinto(),
3234
5.20k
            microseconds: C(0).rinto(),
3235
5.20k
            nanoseconds: C(0).rinto(),
3236
5.20k
        }
3237
5.20k
    }
3238
}
3239
3240
impl core::fmt::Debug for Span {
3241
    #[inline]
3242
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3243
        use crate::fmt::StdFmtWrite;
3244
3245
0
        friendly::DEFAULT_SPAN_PRINTER
3246
0
            .print_span(self, StdFmtWrite(f))
3247
0
            .map_err(|_| core::fmt::Error)
3248
0
    }
3249
}
3250
3251
impl core::fmt::Display for Span {
3252
    #[inline]
3253
1.46k
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3254
        use crate::fmt::StdFmtWrite;
3255
3256
1.46k
        if f.alternate() {
3257
0
            friendly::DEFAULT_SPAN_PRINTER
3258
0
                .print_span(self, StdFmtWrite(f))
3259
0
                .map_err(|_| core::fmt::Error)
3260
        } else {
3261
1.46k
            temporal::DEFAULT_SPAN_PRINTER
3262
1.46k
                .print_span(self, StdFmtWrite(f))
3263
1.46k
                .map_err(|_| core::fmt::Error)
3264
        }
3265
1.46k
    }
3266
}
3267
3268
impl core::str::FromStr for Span {
3269
    type Err = Error;
3270
3271
    #[inline]
3272
0
    fn from_str(string: &str) -> Result<Span, Error> {
3273
0
        parse_iso_or_friendly(string.as_bytes())
3274
0
    }
3275
}
3276
3277
impl core::ops::Neg for Span {
3278
    type Output = Span;
3279
3280
    #[inline]
3281
0
    fn neg(self) -> Span {
3282
0
        self.negate()
3283
0
    }
3284
}
3285
3286
/// This multiplies each unit in a span by an integer.
3287
///
3288
/// This panics on overflow. For checked arithmetic, use [`Span::checked_mul`].
3289
impl core::ops::Mul<i64> for Span {
3290
    type Output = Span;
3291
3292
    #[inline]
3293
0
    fn mul(self, rhs: i64) -> Span {
3294
0
        self.checked_mul(rhs)
3295
0
            .expect("multiplying `Span` by a scalar overflowed")
3296
0
    }
3297
}
3298
3299
/// This multiplies each unit in a span by an integer.
3300
///
3301
/// This panics on overflow. For checked arithmetic, use [`Span::checked_mul`].
3302
impl core::ops::Mul<Span> for i64 {
3303
    type Output = Span;
3304
3305
    #[inline]
3306
0
    fn mul(self, rhs: Span) -> Span {
3307
0
        rhs.checked_mul(self)
3308
0
            .expect("multiplying `Span` by a scalar overflowed")
3309
0
    }
3310
}
3311
3312
/// Converts a `Span` to a [`std::time::Duration`].
3313
///
3314
/// # Errors
3315
///
3316
/// This can fail for only two reasons:
3317
///
3318
/// * The span is negative. This is an error because a `std::time::Duration` is
3319
///   unsigned.)
3320
/// * The span has any non-zero units greater than hours. This is an error
3321
///   because it's impossible to determine the length of, e.g., a month without
3322
///   a reference date.
3323
///
3324
/// This can never result in overflow because a `Duration` can represent a
3325
/// bigger span of time than `Span` when limited to units of hours or lower.
3326
///
3327
/// If you need to convert a `Span` to a `Duration` that has non-zero
3328
/// units bigger than hours, then please use [`Span::to_duration`] with a
3329
/// corresponding relative date.
3330
///
3331
/// # Example: maximal span
3332
///
3333
/// This example shows the maximum possible span using units of hours or
3334
/// smaller, and the corresponding `Duration` value:
3335
///
3336
/// ```
3337
/// use std::time::Duration;
3338
///
3339
/// use jiff::Span;
3340
///
3341
/// let sp = Span::new()
3342
///     .hours(175_307_616)
3343
///     .minutes(10_518_456_960i64)
3344
///     .seconds(631_107_417_600i64)
3345
///     .milliseconds(631_107_417_600_000i64)
3346
///     .microseconds(631_107_417_600_000_000i64)
3347
///     .nanoseconds(9_223_372_036_854_775_807i64);
3348
/// let duration = Duration::try_from(sp)?;
3349
/// assert_eq!(duration, Duration::new(3_164_760_460_036, 854_775_807));
3350
///
3351
/// # Ok::<(), Box<dyn std::error::Error>>(())
3352
/// ```
3353
///
3354
/// # Example: converting a negative span
3355
///
3356
/// Since a `Span` is signed and a `Duration` is unsigned, converting
3357
/// a negative `Span` to `Duration` will always fail. One can use
3358
/// [`Span::signum`] to get the sign of the span and [`Span::abs`] to make the
3359
/// span positive before converting it to a `Duration`:
3360
///
3361
/// ```
3362
/// use std::time::Duration;
3363
///
3364
/// use jiff::{Span, ToSpan};
3365
///
3366
/// let span = -86_400.seconds().nanoseconds(1);
3367
/// let (sign, duration) = (span.signum(), Duration::try_from(span.abs())?);
3368
/// assert_eq!((sign, duration), (-1, Duration::new(86_400, 1)));
3369
///
3370
/// # Ok::<(), Box<dyn std::error::Error>>(())
3371
/// ```
3372
impl TryFrom<Span> for UnsignedDuration {
3373
    type Error = Error;
3374
3375
    #[inline]
3376
0
    fn try_from(sp: Span) -> Result<UnsignedDuration, Error> {
3377
        // This isn't needed, but improves error messages.
3378
0
        if sp.is_negative() {
3379
0
            return Err(err!(
3380
0
                "cannot convert negative span {sp:?} \
3381
0
                 to unsigned std::time::Duration",
3382
0
            ));
3383
0
        }
3384
0
        SignedDuration::try_from(sp).and_then(UnsignedDuration::try_from)
3385
0
    }
3386
}
3387
3388
/// Converts a [`std::time::Duration`] to a `Span`.
3389
///
3390
/// The span returned from this conversion will only ever have non-zero units
3391
/// of seconds or smaller.
3392
///
3393
/// # Errors
3394
///
3395
/// This only fails when the given `Duration` overflows the maximum number of
3396
/// seconds representable by a `Span`.
3397
///
3398
/// # Example
3399
///
3400
/// This shows a basic conversion:
3401
///
3402
/// ```
3403
/// use std::time::Duration;
3404
///
3405
/// use jiff::{Span, ToSpan};
3406
///
3407
/// let duration = Duration::new(86_400, 123_456_789);
3408
/// let span = Span::try_from(duration)?;
3409
/// // A duration-to-span conversion always results in a span with
3410
/// // non-zero units no bigger than seconds.
3411
/// assert_eq!(
3412
///     span.fieldwise(),
3413
///     86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
3414
/// );
3415
///
3416
/// # Ok::<(), Box<dyn std::error::Error>>(())
3417
/// ```
3418
///
3419
/// # Example: rounding
3420
///
3421
/// This example shows how to convert a `Duration` to a `Span`, and then round
3422
/// it up to bigger units given a relative date:
3423
///
3424
/// ```
3425
/// use std::time::Duration;
3426
///
3427
/// use jiff::{civil::date, Span, SpanRound, ToSpan, Unit};
3428
///
3429
/// let duration = Duration::new(450 * 86_401, 0);
3430
/// let span = Span::try_from(duration)?;
3431
/// // We get back a simple span of just seconds:
3432
/// assert_eq!(span.fieldwise(), Span::new().seconds(450 * 86_401));
3433
/// // But we can balance it up to bigger units:
3434
/// let options = SpanRound::new()
3435
///     .largest(Unit::Year)
3436
///     .relative(date(2024, 1, 1));
3437
/// assert_eq!(
3438
///     span.round(options)?,
3439
///     1.year().months(2).days(25).minutes(7).seconds(30).fieldwise(),
3440
/// );
3441
///
3442
/// # Ok::<(), Box<dyn std::error::Error>>(())
3443
/// ```
3444
impl TryFrom<UnsignedDuration> for Span {
3445
    type Error = Error;
3446
3447
    #[inline]
3448
0
    fn try_from(d: UnsignedDuration) -> Result<Span, Error> {
3449
0
        let seconds = i64::try_from(d.as_secs()).map_err(|_| {
3450
0
            err!("seconds from {d:?} overflows a 64-bit signed integer")
3451
0
        })?;
3452
0
        let nanoseconds = i64::from(d.subsec_nanos());
3453
0
        let milliseconds = nanoseconds / t::NANOS_PER_MILLI.value();
3454
0
        let microseconds = (nanoseconds % t::NANOS_PER_MILLI.value())
3455
0
            / t::NANOS_PER_MICRO.value();
3456
0
        let nanoseconds = nanoseconds % t::NANOS_PER_MICRO.value();
3457
3458
0
        let span = Span::new().try_seconds(seconds).with_context(|| {
3459
0
            err!("duration {d:?} overflows limits of a Jiff `Span`")
3460
0
        })?;
3461
        // These are all OK because `Duration::subsec_nanos` is guaranteed to
3462
        // return less than 1_000_000_000 nanoseconds. And splitting that up
3463
        // into millis, micros and nano components is guaranteed to fit into
3464
        // the limits of a `Span`.
3465
0
        Ok(span
3466
0
            .milliseconds(milliseconds)
3467
0
            .microseconds(microseconds)
3468
0
            .nanoseconds(nanoseconds))
3469
0
    }
3470
}
3471
3472
/// Converts a `Span` to a [`SignedDuration`].
3473
///
3474
/// # Errors
3475
///
3476
/// This can fail for only when the span has any non-zero units greater than
3477
/// hours. This is an error because it's impossible to determine the length of,
3478
/// e.g., a month without a reference date.
3479
///
3480
/// This can never result in overflow because a `SignedDuration` can represent
3481
/// a bigger span of time than `Span` when limited to units of hours or lower.
3482
///
3483
/// If you need to convert a `Span` to a `SignedDuration` that has non-zero
3484
/// units bigger than hours, then please use [`Span::to_duration`] with a
3485
/// corresponding relative date.
3486
///
3487
/// # Example: maximal span
3488
///
3489
/// This example shows the maximum possible span using units of hours or
3490
/// smaller, and the corresponding `SignedDuration` value:
3491
///
3492
/// ```
3493
/// use jiff::{SignedDuration, Span};
3494
///
3495
/// let sp = Span::new()
3496
///     .hours(175_307_616)
3497
///     .minutes(10_518_456_960i64)
3498
///     .seconds(631_107_417_600i64)
3499
///     .milliseconds(631_107_417_600_000i64)
3500
///     .microseconds(631_107_417_600_000_000i64)
3501
///     .nanoseconds(9_223_372_036_854_775_807i64);
3502
/// let duration = SignedDuration::try_from(sp)?;
3503
/// assert_eq!(duration, SignedDuration::new(3_164_760_460_036, 854_775_807));
3504
///
3505
/// # Ok::<(), Box<dyn std::error::Error>>(())
3506
/// ```
3507
impl TryFrom<Span> for SignedDuration {
3508
    type Error = Error;
3509
3510
    #[inline]
3511
0
    fn try_from(sp: Span) -> Result<SignedDuration, Error> {
3512
0
        requires_relative_date_err(sp.largest_unit()).context(
3513
            "failed to convert span to duration without relative datetime \
3514
             (must use `Span::to_duration` instead)",
3515
0
        )?;
3516
0
        Ok(sp.to_duration_invariant())
3517
0
    }
3518
}
3519
3520
/// Converts a [`SignedDuration`] to a `Span`.
3521
///
3522
/// The span returned from this conversion will only ever have non-zero units
3523
/// of seconds or smaller.
3524
///
3525
/// # Errors
3526
///
3527
/// This only fails when the given `SignedDuration` overflows the maximum
3528
/// number of seconds representable by a `Span`.
3529
///
3530
/// # Example
3531
///
3532
/// This shows a basic conversion:
3533
///
3534
/// ```
3535
/// use jiff::{SignedDuration, Span, ToSpan};
3536
///
3537
/// let duration = SignedDuration::new(86_400, 123_456_789);
3538
/// let span = Span::try_from(duration)?;
3539
/// // A duration-to-span conversion always results in a span with
3540
/// // non-zero units no bigger than seconds.
3541
/// assert_eq!(
3542
///     span.fieldwise(),
3543
///     86_400.seconds().milliseconds(123).microseconds(456).nanoseconds(789),
3544
/// );
3545
///
3546
/// # Ok::<(), Box<dyn std::error::Error>>(())
3547
/// ```
3548
///
3549
/// # Example: rounding
3550
///
3551
/// This example shows how to convert a `SignedDuration` to a `Span`, and then
3552
/// round it up to bigger units given a relative date:
3553
///
3554
/// ```
3555
/// use jiff::{civil::date, SignedDuration, Span, SpanRound, ToSpan, Unit};
3556
///
3557
/// let duration = SignedDuration::new(450 * 86_401, 0);
3558
/// let span = Span::try_from(duration)?;
3559
/// // We get back a simple span of just seconds:
3560
/// assert_eq!(span.fieldwise(), Span::new().seconds(450 * 86_401));
3561
/// // But we can balance it up to bigger units:
3562
/// let options = SpanRound::new()
3563
///     .largest(Unit::Year)
3564
///     .relative(date(2024, 1, 1));
3565
/// assert_eq!(
3566
///     span.round(options)?,
3567
///     1.year().months(2).days(25).minutes(7).seconds(30).fieldwise(),
3568
/// );
3569
///
3570
/// # Ok::<(), Box<dyn std::error::Error>>(())
3571
/// ```
3572
impl TryFrom<SignedDuration> for Span {
3573
    type Error = Error;
3574
3575
    #[inline]
3576
0
    fn try_from(d: SignedDuration) -> Result<Span, Error> {
3577
0
        let seconds = d.as_secs();
3578
0
        let nanoseconds = i64::from(d.subsec_nanos());
3579
0
        let milliseconds = nanoseconds / t::NANOS_PER_MILLI.value();
3580
0
        let microseconds = (nanoseconds % t::NANOS_PER_MILLI.value())
3581
0
            / t::NANOS_PER_MICRO.value();
3582
0
        let nanoseconds = nanoseconds % t::NANOS_PER_MICRO.value();
3583
3584
0
        let span = Span::new().try_seconds(seconds).with_context(|| {
3585
0
            err!("signed duration {d:?} overflows limits of a Jiff `Span`")
3586
0
        })?;
3587
        // These are all OK because `|SignedDuration::subsec_nanos|` is
3588
        // guaranteed to return less than 1_000_000_000 nanoseconds. And
3589
        // splitting that up into millis, micros and nano components is
3590
        // guaranteed to fit into the limits of a `Span`.
3591
0
        Ok(span
3592
0
            .milliseconds(milliseconds)
3593
0
            .microseconds(microseconds)
3594
0
            .nanoseconds(nanoseconds))
3595
0
    }
3596
}
3597
3598
#[cfg(feature = "serde")]
3599
impl serde::Serialize for Span {
3600
    #[inline]
3601
    fn serialize<S: serde::Serializer>(
3602
        &self,
3603
        serializer: S,
3604
    ) -> Result<S::Ok, S::Error> {
3605
        serializer.collect_str(self)
3606
    }
3607
}
3608
3609
#[cfg(feature = "serde")]
3610
impl<'de> serde::Deserialize<'de> for Span {
3611
    #[inline]
3612
    fn deserialize<D: serde::Deserializer<'de>>(
3613
        deserializer: D,
3614
    ) -> Result<Span, D::Error> {
3615
        use serde::de;
3616
3617
        struct SpanVisitor;
3618
3619
        impl<'de> de::Visitor<'de> for SpanVisitor {
3620
            type Value = Span;
3621
3622
            fn expecting(
3623
                &self,
3624
                f: &mut core::fmt::Formatter,
3625
            ) -> core::fmt::Result {
3626
                f.write_str("a span duration string")
3627
            }
3628
3629
            #[inline]
3630
            fn visit_bytes<E: de::Error>(
3631
                self,
3632
                value: &[u8],
3633
            ) -> Result<Span, E> {
3634
                parse_iso_or_friendly(value).map_err(de::Error::custom)
3635
            }
3636
3637
            #[inline]
3638
            fn visit_str<E: de::Error>(self, value: &str) -> Result<Span, E> {
3639
                self.visit_bytes(value.as_bytes())
3640
            }
3641
        }
3642
3643
        deserializer.deserialize_str(SpanVisitor)
3644
    }
3645
}
3646
3647
#[cfg(test)]
3648
impl quickcheck::Arbitrary for Span {
3649
    fn arbitrary(g: &mut quickcheck::Gen) -> Span {
3650
        // In order to sample from the full space of possible spans, we need
3651
        // to provide a relative datetime. But if we do that, then it's
3652
        // possible the span plus the datetime overflows. So we pick one
3653
        // datetime and shrink the size of the span we can produce.
3654
        type Nanos = ri64<-631_107_417_600_000_000, 631_107_417_600_000_000>;
3655
        let nanos = Nanos::arbitrary(g).get();
3656
        let relative =
3657
            SpanRelativeTo::from(DateTime::constant(0, 1, 1, 0, 0, 0, 0));
3658
        let round =
3659
            SpanRound::new().largest(Unit::arbitrary(g)).relative(relative);
3660
        Span::new().nanoseconds(nanos).round(round).unwrap()
3661
    }
3662
3663
    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
3664
        alloc::boxed::Box::new(
3665
            (
3666
                (
3667
                    self.get_years_ranged(),
3668
                    self.get_months_ranged(),
3669
                    self.get_weeks_ranged(),
3670
                    self.get_days_ranged(),
3671
                ),
3672
                (
3673
                    self.get_hours_ranged(),
3674
                    self.get_minutes_ranged(),
3675
                    self.get_seconds_ranged(),
3676
                    self.get_milliseconds_ranged(),
3677
                ),
3678
                (
3679
                    self.get_microseconds_ranged(),
3680
                    self.get_nanoseconds_ranged(),
3681
                ),
3682
            )
3683
                .shrink()
3684
                .filter_map(
3685
                    |(
3686
                        (years, months, weeks, days),
3687
                        (hours, minutes, seconds, milliseconds),
3688
                        (microseconds, nanoseconds),
3689
                    )| {
3690
                        let span = Span::new()
3691
                            .years_ranged(years)
3692
                            .months_ranged(months)
3693
                            .weeks_ranged(weeks)
3694
                            .days_ranged(days)
3695
                            .hours_ranged(hours)
3696
                            .minutes_ranged(minutes)
3697
                            .seconds_ranged(seconds)
3698
                            .milliseconds_ranged(milliseconds)
3699
                            .microseconds_ranged(microseconds)
3700
                            .nanoseconds_ranged(nanoseconds);
3701
                        Some(span)
3702
                    },
3703
                ),
3704
        )
3705
    }
3706
}
3707
3708
/// A wrapper for [`Span`] that implements the `Hash`, `Eq` and `PartialEq`
3709
/// traits.
3710
///
3711
/// A `SpanFieldwise` is meant to make it easy to compare two spans in a "dumb"
3712
/// way based purely on its unit values, while still providing a speed bump
3713
/// to avoid accidentally doing this comparison on `Span` directly. This is
3714
/// distinct from something like [`Span::compare`] that performs a comparison
3715
/// on the actual elapsed time of two spans.
3716
///
3717
/// It is generally discouraged to use `SpanFieldwise` since spans that
3718
/// represent an equivalent elapsed amount of time may compare unequal.
3719
/// However, in some cases, it is useful to be able to assert precise field
3720
/// values. For example, Jiff itself makes heavy use of fieldwise comparisons
3721
/// for tests.
3722
///
3723
/// # Construction
3724
///
3725
/// While callers may use `SpanFieldwise(span)` (where `span` has type [`Span`])
3726
/// to construct a value of this type, callers may find [`Span::fieldwise`]
3727
/// more convenient. Namely, `Span::fieldwise` may avoid the need to explicitly
3728
/// import `SpanFieldwise`.
3729
///
3730
/// # Trait implementations
3731
///
3732
/// In addition to implementing the `Hash`, `Eq` and `PartialEq` traits, this
3733
/// type also provides `PartialEq` impls for comparing a `Span` with a
3734
/// `SpanFieldwise`. This simplifies comparisons somewhat while still requiring
3735
/// that at least one of the values has an explicit fieldwise comparison type.
3736
///
3737
/// # Safety
3738
///
3739
/// This type is guaranteed to have the same layout in memory as [`Span`].
3740
///
3741
/// # Example: the difference between `SpanFieldwise` and [`Span::compare`]
3742
///
3743
/// In short, `SpanFieldwise` considers `2 hours` and `120 minutes` to be
3744
/// distinct values, but `Span::compare` considers them to be equivalent:
3745
///
3746
/// ```
3747
/// use std::cmp::Ordering;
3748
/// use jiff::ToSpan;
3749
///
3750
/// assert_ne!(120.minutes().fieldwise(), 2.hours().fieldwise());
3751
/// assert_eq!(120.minutes().compare(2.hours())?, Ordering::Equal);
3752
///
3753
/// // These comparisons are allowed between a `Span` and a `SpanFieldwise`.
3754
/// // Namely, as long as one value is "fieldwise," then the comparison is OK.
3755
/// assert_ne!(120.minutes().fieldwise(), 2.hours());
3756
/// assert_ne!(120.minutes(), 2.hours().fieldwise());
3757
///
3758
/// # Ok::<(), Box<dyn std::error::Error>>(())
3759
/// ```
3760
#[derive(Clone, Copy, Debug, Default)]
3761
#[repr(transparent)]
3762
pub struct SpanFieldwise(pub Span);
3763
3764
// Exists so that things like `-1.day().fieldwise()` works as expected.
3765
impl core::ops::Neg for SpanFieldwise {
3766
    type Output = SpanFieldwise;
3767
3768
    #[inline]
3769
0
    fn neg(self) -> SpanFieldwise {
3770
0
        SpanFieldwise(self.0.negate())
3771
0
    }
3772
}
3773
3774
impl Eq for SpanFieldwise {}
3775
3776
impl PartialEq for SpanFieldwise {
3777
0
    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3778
0
        self.0.sign == rhs.0.sign
3779
0
            && self.0.years == rhs.0.years
3780
0
            && self.0.months == rhs.0.months
3781
0
            && self.0.weeks == rhs.0.weeks
3782
0
            && self.0.days == rhs.0.days
3783
0
            && self.0.hours == rhs.0.hours
3784
0
            && self.0.minutes == rhs.0.minutes
3785
0
            && self.0.seconds == rhs.0.seconds
3786
0
            && self.0.milliseconds == rhs.0.milliseconds
3787
0
            && self.0.microseconds == rhs.0.microseconds
3788
0
            && self.0.nanoseconds == rhs.0.nanoseconds
3789
0
    }
3790
}
3791
3792
impl<'a> PartialEq<SpanFieldwise> for &'a SpanFieldwise {
3793
0
    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3794
0
        *self == rhs
3795
0
    }
3796
}
3797
3798
impl PartialEq<Span> for SpanFieldwise {
3799
0
    fn eq(&self, rhs: &Span) -> bool {
3800
0
        self == rhs.fieldwise()
3801
0
    }
3802
}
3803
3804
impl PartialEq<SpanFieldwise> for Span {
3805
0
    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3806
0
        self.fieldwise() == *rhs
3807
0
    }
3808
}
3809
3810
impl<'a> PartialEq<SpanFieldwise> for &'a Span {
3811
0
    fn eq(&self, rhs: &SpanFieldwise) -> bool {
3812
0
        self.fieldwise() == *rhs
3813
0
    }
3814
}
3815
3816
impl core::hash::Hash for SpanFieldwise {
3817
0
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
3818
0
        self.0.sign.hash(state);
3819
0
        self.0.years.hash(state);
3820
0
        self.0.months.hash(state);
3821
0
        self.0.weeks.hash(state);
3822
0
        self.0.days.hash(state);
3823
0
        self.0.hours.hash(state);
3824
0
        self.0.minutes.hash(state);
3825
0
        self.0.seconds.hash(state);
3826
0
        self.0.milliseconds.hash(state);
3827
0
        self.0.microseconds.hash(state);
3828
0
        self.0.nanoseconds.hash(state);
3829
0
    }
3830
}
3831
3832
impl From<Span> for SpanFieldwise {
3833
0
    fn from(span: Span) -> SpanFieldwise {
3834
0
        SpanFieldwise(span)
3835
0
    }
3836
}
3837
3838
impl From<SpanFieldwise> for Span {
3839
0
    fn from(span: SpanFieldwise) -> Span {
3840
0
        span.0
3841
0
    }
3842
}
3843
3844
/// A trait for enabling concise literals for creating [`Span`] values.
3845
///
3846
/// In short, this trait lets you write something like `5.seconds()` or
3847
/// `1.day()` to create a [`Span`]. Once a `Span` has been created, you can
3848
/// use its mutator methods to add more fields. For example,
3849
/// `1.day().hours(10)` is equivalent to `Span::new().days(1).hours(10)`.
3850
///
3851
/// This trait is implemented for the following integer types: `i8`, `i16`,
3852
/// `i32` and `i64`.
3853
///
3854
/// Note that this trait is provided as a convenience and should generally
3855
/// only be used for literals in your source code. You should not use this
3856
/// trait on numbers provided by end users. Namely, if the number provided
3857
/// is not within Jiff's span limits, then these trait methods will panic.
3858
/// Instead, use fallible mutator constructors like [`Span::try_days`]
3859
/// or [`Span::try_seconds`].
3860
///
3861
/// # Example
3862
///
3863
/// ```
3864
/// use jiff::ToSpan;
3865
///
3866
/// assert_eq!(5.days().to_string(), "P5D");
3867
/// assert_eq!(5.days().hours(10).to_string(), "P5DT10H");
3868
///
3869
/// // Negation works and it doesn't matter where the sign goes. It can be
3870
/// // applied to the span itself or to the integer.
3871
/// assert_eq!((-5.days()).to_string(), "-P5D");
3872
/// assert_eq!((-5).days().to_string(), "-P5D");
3873
/// ```
3874
///
3875
/// # Example: alternative via span parsing
3876
///
3877
/// Another way of tersely building a `Span` value is by parsing a ISO 8601
3878
/// duration string:
3879
///
3880
/// ```
3881
/// use jiff::Span;
3882
///
3883
/// let span = "P5y2m15dT23h30m10s".parse::<Span>()?;
3884
/// assert_eq!(
3885
///     span.fieldwise(),
3886
///     Span::new().years(5).months(2).days(15).hours(23).minutes(30).seconds(10),
3887
/// );
3888
///
3889
/// # Ok::<(), Box<dyn std::error::Error>>(())
3890
/// ```
3891
pub trait ToSpan: Sized {
3892
    /// Create a new span from this integer in units of years.
3893
    ///
3894
    /// # Panics
3895
    ///
3896
    /// When `Span::new().years(self)` would panic.
3897
    fn years(self) -> Span;
3898
3899
    /// Create a new span from this integer in units of months.
3900
    ///
3901
    /// # Panics
3902
    ///
3903
    /// When `Span::new().months(self)` would panic.
3904
    fn months(self) -> Span;
3905
3906
    /// Create a new span from this integer in units of weeks.
3907
    ///
3908
    /// # Panics
3909
    ///
3910
    /// When `Span::new().weeks(self)` would panic.
3911
    fn weeks(self) -> Span;
3912
3913
    /// Create a new span from this integer in units of days.
3914
    ///
3915
    /// # Panics
3916
    ///
3917
    /// When `Span::new().days(self)` would panic.
3918
    fn days(self) -> Span;
3919
3920
    /// Create a new span from this integer in units of hours.
3921
    ///
3922
    /// # Panics
3923
    ///
3924
    /// When `Span::new().hours(self)` would panic.
3925
    fn hours(self) -> Span;
3926
3927
    /// Create a new span from this integer in units of minutes.
3928
    ///
3929
    /// # Panics
3930
    ///
3931
    /// When `Span::new().minutes(self)` would panic.
3932
    fn minutes(self) -> Span;
3933
3934
    /// Create a new span from this integer in units of seconds.
3935
    ///
3936
    /// # Panics
3937
    ///
3938
    /// When `Span::new().seconds(self)` would panic.
3939
    fn seconds(self) -> Span;
3940
3941
    /// Create a new span from this integer in units of milliseconds.
3942
    ///
3943
    /// # Panics
3944
    ///
3945
    /// When `Span::new().milliseconds(self)` would panic.
3946
    fn milliseconds(self) -> Span;
3947
3948
    /// Create a new span from this integer in units of microseconds.
3949
    ///
3950
    /// # Panics
3951
    ///
3952
    /// When `Span::new().microseconds(self)` would panic.
3953
    fn microseconds(self) -> Span;
3954
3955
    /// Create a new span from this integer in units of nanoseconds.
3956
    ///
3957
    /// # Panics
3958
    ///
3959
    /// When `Span::new().nanoseconds(self)` would panic.
3960
    fn nanoseconds(self) -> Span;
3961
3962
    /// Equivalent to `years()`, but reads better for singular units.
3963
    #[inline]
3964
0
    fn year(self) -> Span {
3965
0
        self.years()
3966
0
    }
3967
3968
    /// Equivalent to `months()`, but reads better for singular units.
3969
    #[inline]
3970
0
    fn month(self) -> Span {
3971
0
        self.months()
3972
0
    }
3973
3974
    /// Equivalent to `weeks()`, but reads better for singular units.
3975
    #[inline]
3976
0
    fn week(self) -> Span {
3977
0
        self.weeks()
3978
0
    }
3979
3980
    /// Equivalent to `days()`, but reads better for singular units.
3981
    #[inline]
3982
0
    fn day(self) -> Span {
3983
0
        self.days()
3984
0
    }
3985
3986
    /// Equivalent to `hours()`, but reads better for singular units.
3987
    #[inline]
3988
0
    fn hour(self) -> Span {
3989
0
        self.hours()
3990
0
    }
3991
3992
    /// Equivalent to `minutes()`, but reads better for singular units.
3993
    #[inline]
3994
0
    fn minute(self) -> Span {
3995
0
        self.minutes()
3996
0
    }
3997
3998
    /// Equivalent to `seconds()`, but reads better for singular units.
3999
    #[inline]
4000
0
    fn second(self) -> Span {
4001
0
        self.seconds()
4002
0
    }
4003
4004
    /// Equivalent to `milliseconds()`, but reads better for singular units.
4005
    #[inline]
4006
0
    fn millisecond(self) -> Span {
4007
0
        self.milliseconds()
4008
0
    }
4009
4010
    /// Equivalent to `microseconds()`, but reads better for singular units.
4011
    #[inline]
4012
0
    fn microsecond(self) -> Span {
4013
0
        self.microseconds()
4014
0
    }
4015
4016
    /// Equivalent to `nanoseconds()`, but reads better for singular units.
4017
    #[inline]
4018
0
    fn nanosecond(self) -> Span {
4019
0
        self.nanoseconds()
4020
0
    }
4021
}
4022
4023
macro_rules! impl_to_span {
4024
    ($ty:ty) => {
4025
        impl ToSpan for $ty {
4026
            #[inline]
4027
0
            fn years(self) -> Span {
4028
0
                Span::new().years(self)
4029
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::years
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::years
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::years
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::years
4030
            #[inline]
4031
0
            fn months(self) -> Span {
4032
0
                Span::new().months(self)
4033
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::months
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::months
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::months
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::months
4034
            #[inline]
4035
0
            fn weeks(self) -> Span {
4036
0
                Span::new().weeks(self)
4037
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::weeks
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::weeks
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::weeks
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::weeks
4038
            #[inline]
4039
0
            fn days(self) -> Span {
4040
0
                Span::new().days(self)
4041
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::days
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::days
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::days
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::days
4042
            #[inline]
4043
0
            fn hours(self) -> Span {
4044
0
                Span::new().hours(self)
4045
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::hours
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::hours
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::hours
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::hours
4046
            #[inline]
4047
0
            fn minutes(self) -> Span {
4048
0
                Span::new().minutes(self)
4049
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::minutes
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::minutes
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::minutes
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::minutes
4050
            #[inline]
4051
0
            fn seconds(self) -> Span {
4052
0
                Span::new().seconds(self)
4053
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::seconds
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::seconds
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::seconds
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::seconds
4054
            #[inline]
4055
0
            fn milliseconds(self) -> Span {
4056
0
                Span::new().milliseconds(self)
4057
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::milliseconds
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::milliseconds
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::milliseconds
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::milliseconds
4058
            #[inline]
4059
0
            fn microseconds(self) -> Span {
4060
0
                Span::new().microseconds(self)
4061
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::microseconds
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::microseconds
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::microseconds
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::microseconds
4062
            #[inline]
4063
0
            fn nanoseconds(self) -> Span {
4064
0
                Span::new().nanoseconds(self)
4065
0
            }
Unexecuted instantiation: <i8 as jiff::span::ToSpan>::nanoseconds
Unexecuted instantiation: <i16 as jiff::span::ToSpan>::nanoseconds
Unexecuted instantiation: <i32 as jiff::span::ToSpan>::nanoseconds
Unexecuted instantiation: <i64 as jiff::span::ToSpan>::nanoseconds
4066
        }
4067
    };
4068
}
4069
4070
impl_to_span!(i8);
4071
impl_to_span!(i16);
4072
impl_to_span!(i32);
4073
impl_to_span!(i64);
4074
4075
/// A way to refer to a single calendar or clock unit.
4076
///
4077
/// This type is principally used in APIs involving a [`Span`], which is a
4078
/// duration of time. For example, routines like [`Zoned::until`] permit
4079
/// specifying the largest unit of the span returned:
4080
///
4081
/// ```
4082
/// use jiff::{Unit, Zoned};
4083
///
4084
/// let zdt1: Zoned = "2024-07-06 17:40-04[America/New_York]".parse()?;
4085
/// let zdt2: Zoned = "2024-11-05 08:00-05[America/New_York]".parse()?;
4086
/// let span = zdt1.until((Unit::Year, &zdt2))?;
4087
/// assert_eq!(format!("{span:#}"), "3mo 29d 14h 20m");
4088
///
4089
/// # Ok::<(), Box<dyn std::error::Error>>(())
4090
/// ```
4091
///
4092
/// But a `Unit` is also used in APIs for rounding datetimes themselves:
4093
///
4094
/// ```
4095
/// use jiff::{Unit, Zoned};
4096
///
4097
/// let zdt: Zoned = "2024-07-06 17:44:22.158-04[America/New_York]".parse()?;
4098
/// let nearest_minute = zdt.round(Unit::Minute)?;
4099
/// assert_eq!(
4100
///     nearest_minute.to_string(),
4101
///     "2024-07-06T17:44:00-04:00[America/New_York]",
4102
/// );
4103
///
4104
/// # Ok::<(), Box<dyn std::error::Error>>(())
4105
/// ```
4106
///
4107
/// # Example: ordering
4108
///
4109
/// This example demonstrates that `Unit` has an ordering defined such that
4110
/// bigger units compare greater than smaller units.
4111
///
4112
/// ```
4113
/// use jiff::Unit;
4114
///
4115
/// assert!(Unit::Year > Unit::Nanosecond);
4116
/// assert!(Unit::Day > Unit::Hour);
4117
/// assert!(Unit::Hour > Unit::Minute);
4118
/// assert!(Unit::Hour > Unit::Minute);
4119
/// assert_eq!(Unit::Hour, Unit::Hour);
4120
/// ```
4121
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
4122
pub enum Unit {
4123
    /// A Gregorian calendar year. It usually has 365 days for non-leap years,
4124
    /// and 366 days for leap years.
4125
    Year = 9,
4126
    /// A Gregorian calendar month. It usually has one of 28, 29, 30 or 31
4127
    /// days.
4128
    Month = 8,
4129
    /// A week is 7 days that either begins on Sunday or Monday.
4130
    Week = 7,
4131
    /// A day is usually 24 hours, but some days may have different lengths
4132
    /// due to time zone transitions.
4133
    Day = 6,
4134
    /// An hour is always 60 minutes.
4135
    Hour = 5,
4136
    /// A minute is always 60 seconds. (Jiff behaves as if leap seconds do not
4137
    /// exist.)
4138
    Minute = 4,
4139
    /// A second is always 1,000 milliseconds.
4140
    Second = 3,
4141
    /// A millisecond is always 1,000 microseconds.
4142
    Millisecond = 2,
4143
    /// A microsecond is always 1,000 nanoseconds.
4144
    Microsecond = 1,
4145
    /// A nanosecond is the smallest granularity of time supported by Jiff.
4146
    Nanosecond = 0,
4147
}
4148
4149
impl Unit {
4150
    /// Returns the next biggest unit, if one exists.
4151
0
    pub(crate) fn next(&self) -> Option<Unit> {
4152
0
        match *self {
4153
0
            Unit::Year => None,
4154
0
            Unit::Month => Some(Unit::Year),
4155
0
            Unit::Week => Some(Unit::Month),
4156
0
            Unit::Day => Some(Unit::Week),
4157
0
            Unit::Hour => Some(Unit::Day),
4158
0
            Unit::Minute => Some(Unit::Hour),
4159
0
            Unit::Second => Some(Unit::Minute),
4160
0
            Unit::Millisecond => Some(Unit::Second),
4161
0
            Unit::Microsecond => Some(Unit::Millisecond),
4162
0
            Unit::Nanosecond => Some(Unit::Microsecond),
4163
        }
4164
0
    }
4165
4166
    /// Returns the number of nanoseconds in this unit as a 128-bit integer.
4167
    ///
4168
    /// # Panics
4169
    ///
4170
    /// When this unit is always variable. That is, years or months.
4171
0
    pub(crate) fn nanoseconds(self) -> NoUnits128 {
4172
0
        match self {
4173
0
            Unit::Nanosecond => Constant(1),
4174
0
            Unit::Microsecond => t::NANOS_PER_MICRO,
4175
0
            Unit::Millisecond => t::NANOS_PER_MILLI,
4176
0
            Unit::Second => t::NANOS_PER_SECOND,
4177
0
            Unit::Minute => t::NANOS_PER_MINUTE,
4178
0
            Unit::Hour => t::NANOS_PER_HOUR,
4179
0
            Unit::Day => t::NANOS_PER_CIVIL_DAY,
4180
0
            Unit::Week => t::NANOS_PER_CIVIL_WEEK,
4181
0
            unit => unreachable!("{unit:?} has no definitive time interval"),
4182
        }
4183
0
        .rinto()
4184
0
    }
4185
4186
    /// Returns true when this unit is definitively variable.
4187
    ///
4188
    /// In effect, this is any unit bigger than 'day', because any such unit
4189
    /// can vary in time depending on its reference point. A 'day' can as well,
4190
    /// but we sorta special case 'day' to mean '24 hours' for cases where
4191
    /// the user is dealing with civil time.
4192
0
    fn is_variable(self) -> bool {
4193
0
        matches!(self, Unit::Year | Unit::Month | Unit::Week | Unit::Day)
4194
0
    }
4195
4196
    /// A human readable singular description of this unit of time.
4197
0
    pub(crate) fn singular(&self) -> &'static str {
4198
0
        match *self {
4199
0
            Unit::Year => "year",
4200
0
            Unit::Month => "month",
4201
0
            Unit::Week => "week",
4202
0
            Unit::Day => "day",
4203
0
            Unit::Hour => "hour",
4204
0
            Unit::Minute => "minute",
4205
0
            Unit::Second => "second",
4206
0
            Unit::Millisecond => "millisecond",
4207
0
            Unit::Microsecond => "microsecond",
4208
0
            Unit::Nanosecond => "nanosecond",
4209
        }
4210
0
    }
4211
4212
    /// A human readable plural description of this unit of time.
4213
0
    pub(crate) fn plural(&self) -> &'static str {
4214
0
        match *self {
4215
0
            Unit::Year => "years",
4216
0
            Unit::Month => "months",
4217
0
            Unit::Week => "weeks",
4218
0
            Unit::Day => "days",
4219
0
            Unit::Hour => "hours",
4220
0
            Unit::Minute => "minutes",
4221
0
            Unit::Second => "seconds",
4222
0
            Unit::Millisecond => "milliseconds",
4223
0
            Unit::Microsecond => "microseconds",
4224
0
            Unit::Nanosecond => "nanoseconds",
4225
        }
4226
0
    }
4227
4228
    /// A very succinct label corresponding to this unit.
4229
0
    pub(crate) fn compact(&self) -> &'static str {
4230
0
        match *self {
4231
0
            Unit::Year => "y",
4232
0
            Unit::Month => "mo",
4233
0
            Unit::Week => "w",
4234
0
            Unit::Day => "d",
4235
0
            Unit::Hour => "h",
4236
0
            Unit::Minute => "m",
4237
0
            Unit::Second => "s",
4238
0
            Unit::Millisecond => "ms",
4239
0
            Unit::Microsecond => "µs",
4240
0
            Unit::Nanosecond => "ns",
4241
        }
4242
0
    }
4243
4244
    /// The inverse of `unit as usize`.
4245
0
    fn from_usize(n: usize) -> Option<Unit> {
4246
0
        match n {
4247
0
            0 => Some(Unit::Nanosecond),
4248
0
            1 => Some(Unit::Microsecond),
4249
0
            2 => Some(Unit::Millisecond),
4250
0
            3 => Some(Unit::Second),
4251
0
            4 => Some(Unit::Minute),
4252
0
            5 => Some(Unit::Hour),
4253
0
            6 => Some(Unit::Day),
4254
0
            7 => Some(Unit::Week),
4255
0
            8 => Some(Unit::Month),
4256
0
            9 => Some(Unit::Year),
4257
0
            _ => None,
4258
        }
4259
0
    }
4260
}
4261
4262
#[cfg(test)]
4263
impl quickcheck::Arbitrary for Unit {
4264
    fn arbitrary(g: &mut quickcheck::Gen) -> Unit {
4265
        Unit::from_usize(usize::arbitrary(g) % 10).unwrap()
4266
    }
4267
4268
    fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
4269
        alloc::boxed::Box::new(
4270
            (*self as usize)
4271
                .shrink()
4272
                .map(|n| Unit::from_usize(n % 10).unwrap()),
4273
        )
4274
    }
4275
}
4276
4277
/// Options for [`Span::checked_add`] and [`Span::checked_sub`].
4278
///
4279
/// This type provides a way to ergonomically add two spans with an optional
4280
/// relative datetime. Namely, a relative datetime is only needed when at least
4281
/// one of the two spans being added (or subtracted) has a non-zero calendar
4282
/// unit (years, months, weeks or days). Otherwise, an error will be returned.
4283
///
4284
/// Callers may use [`SpanArithmetic::days_are_24_hours`] to opt into 24-hour
4285
/// invariant days (and 7-day weeks) without providing a relative datetime.
4286
///
4287
/// The main way to construct values of this type is with its `From` trait
4288
/// implementations:
4289
///
4290
/// * `From<Span> for SpanArithmetic` adds (or subtracts) the given span to the
4291
/// receiver in [`Span::checked_add`] (or [`Span::checked_sub`]).
4292
/// * `From<(Span, civil::Date)> for SpanArithmetic` adds (or subtracts)
4293
/// the given span to the receiver in [`Span::checked_add`] (or
4294
/// [`Span::checked_sub`]), relative to the given date. There are also `From`
4295
/// implementations for `civil::DateTime`, `Zoned` and [`SpanRelativeTo`].
4296
///
4297
/// # Example
4298
///
4299
/// ```
4300
/// use jiff::ToSpan;
4301
///
4302
/// assert_eq!(
4303
///     1.hour().checked_add(30.minutes())?,
4304
///     1.hour().minutes(30).fieldwise(),
4305
/// );
4306
///
4307
/// # Ok::<(), Box<dyn std::error::Error>>(())
4308
/// ```
4309
#[derive(Clone, Copy, Debug)]
4310
pub struct SpanArithmetic<'a> {
4311
    duration: Duration,
4312
    relative: Option<SpanRelativeTo<'a>>,
4313
}
4314
4315
impl<'a> SpanArithmetic<'a> {
4316
    /// This is a convenience function for setting the relative option on
4317
    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4318
    ///
4319
    /// # Example
4320
    ///
4321
    /// When doing arithmetic on spans involving days, either a relative
4322
    /// datetime must be provided, or a special assertion opting into 24-hour
4323
    /// days is required. Otherwise, you get an error.
4324
    ///
4325
    /// ```
4326
    /// use jiff::{SpanArithmetic, ToSpan};
4327
    ///
4328
    /// let span1 = 2.days().hours(12);
4329
    /// let span2 = 12.hours();
4330
    /// // No relative date provided, which results in an error.
4331
    /// assert_eq!(
4332
    ///     span1.checked_add(span2).unwrap_err().to_string(),
4333
    ///     "using unit 'day' in a span or configuration requires that \
4334
    ///      either a relative reference time be given or \
4335
    ///      `SpanRelativeTo::days_are_24_hours()` is used to indicate \
4336
    ///      invariant 24-hour days, but neither were provided",
4337
    /// );
4338
    /// let sum = span1.checked_add(
4339
    ///     SpanArithmetic::from(span2).days_are_24_hours(),
4340
    /// )?;
4341
    /// assert_eq!(sum, 3.days().fieldwise());
4342
    ///
4343
    /// # Ok::<(), Box<dyn std::error::Error>>(())
4344
    /// ```
4345
    #[inline]
4346
0
    pub fn days_are_24_hours(self) -> SpanArithmetic<'a> {
4347
0
        self.relative(SpanRelativeTo::days_are_24_hours())
4348
0
    }
4349
}
4350
4351
impl<'a> SpanArithmetic<'a> {
4352
    #[inline]
4353
0
    fn relative<R: Into<SpanRelativeTo<'a>>>(
4354
0
        self,
4355
0
        relative: R,
4356
0
    ) -> SpanArithmetic<'a> {
4357
0
        SpanArithmetic { relative: Some(relative.into()), ..self }
4358
0
    }
4359
4360
    #[inline]
4361
0
    fn checked_add(self, span1: Span) -> Result<Span, Error> {
4362
0
        match self.duration.to_signed()? {
4363
0
            SDuration::Span(span2) => {
4364
0
                span1.checked_add_span(self.relative, &span2)
4365
            }
4366
0
            SDuration::Absolute(dur2) => {
4367
0
                span1.checked_add_duration(self.relative, dur2)
4368
            }
4369
        }
4370
0
    }
4371
}
4372
4373
impl From<Span> for SpanArithmetic<'static> {
4374
0
    fn from(span: Span) -> SpanArithmetic<'static> {
4375
0
        let duration = Duration::from(span);
4376
0
        SpanArithmetic { duration, relative: None }
4377
0
    }
4378
}
4379
4380
impl<'a> From<&'a Span> for SpanArithmetic<'static> {
4381
0
    fn from(span: &'a Span) -> SpanArithmetic<'static> {
4382
0
        let duration = Duration::from(*span);
4383
0
        SpanArithmetic { duration, relative: None }
4384
0
    }
4385
}
4386
4387
impl From<(Span, Date)> for SpanArithmetic<'static> {
4388
    #[inline]
4389
0
    fn from((span, date): (Span, Date)) -> SpanArithmetic<'static> {
4390
0
        SpanArithmetic::from(span).relative(date)
4391
0
    }
4392
}
4393
4394
impl From<(Span, DateTime)> for SpanArithmetic<'static> {
4395
    #[inline]
4396
0
    fn from((span, datetime): (Span, DateTime)) -> SpanArithmetic<'static> {
4397
0
        SpanArithmetic::from(span).relative(datetime)
4398
0
    }
4399
}
4400
4401
impl<'a> From<(Span, &'a Zoned)> for SpanArithmetic<'a> {
4402
    #[inline]
4403
0
    fn from((span, zoned): (Span, &'a Zoned)) -> SpanArithmetic<'a> {
4404
0
        SpanArithmetic::from(span).relative(zoned)
4405
0
    }
4406
}
4407
4408
impl<'a> From<(Span, SpanRelativeTo<'a>)> for SpanArithmetic<'a> {
4409
    #[inline]
4410
0
    fn from(
4411
0
        (span, relative): (Span, SpanRelativeTo<'a>),
4412
0
    ) -> SpanArithmetic<'a> {
4413
0
        SpanArithmetic::from(span).relative(relative)
4414
0
    }
4415
}
4416
4417
impl<'a> From<(&'a Span, Date)> for SpanArithmetic<'static> {
4418
    #[inline]
4419
0
    fn from((span, date): (&'a Span, Date)) -> SpanArithmetic<'static> {
4420
0
        SpanArithmetic::from(span).relative(date)
4421
0
    }
4422
}
4423
4424
impl<'a> From<(&'a Span, DateTime)> for SpanArithmetic<'static> {
4425
    #[inline]
4426
0
    fn from(
4427
0
        (span, datetime): (&'a Span, DateTime),
4428
0
    ) -> SpanArithmetic<'static> {
4429
0
        SpanArithmetic::from(span).relative(datetime)
4430
0
    }
4431
}
4432
4433
impl<'a, 'b> From<(&'a Span, &'b Zoned)> for SpanArithmetic<'b> {
4434
    #[inline]
4435
0
    fn from((span, zoned): (&'a Span, &'b Zoned)) -> SpanArithmetic<'b> {
4436
0
        SpanArithmetic::from(span).relative(zoned)
4437
0
    }
4438
}
4439
4440
impl<'a, 'b> From<(&'a Span, SpanRelativeTo<'b>)> for SpanArithmetic<'b> {
4441
    #[inline]
4442
0
    fn from(
4443
0
        (span, relative): (&'a Span, SpanRelativeTo<'b>),
4444
0
    ) -> SpanArithmetic<'b> {
4445
0
        SpanArithmetic::from(span).relative(relative)
4446
0
    }
4447
}
4448
4449
impl From<SignedDuration> for SpanArithmetic<'static> {
4450
0
    fn from(duration: SignedDuration) -> SpanArithmetic<'static> {
4451
0
        let duration = Duration::from(duration);
4452
0
        SpanArithmetic { duration, relative: None }
4453
0
    }
4454
}
4455
4456
impl From<(SignedDuration, Date)> for SpanArithmetic<'static> {
4457
    #[inline]
4458
0
    fn from(
4459
0
        (duration, date): (SignedDuration, Date),
4460
0
    ) -> SpanArithmetic<'static> {
4461
0
        SpanArithmetic::from(duration).relative(date)
4462
0
    }
4463
}
4464
4465
impl From<(SignedDuration, DateTime)> for SpanArithmetic<'static> {
4466
    #[inline]
4467
0
    fn from(
4468
0
        (duration, datetime): (SignedDuration, DateTime),
4469
0
    ) -> SpanArithmetic<'static> {
4470
0
        SpanArithmetic::from(duration).relative(datetime)
4471
0
    }
4472
}
4473
4474
impl<'a> From<(SignedDuration, &'a Zoned)> for SpanArithmetic<'a> {
4475
    #[inline]
4476
0
    fn from(
4477
0
        (duration, zoned): (SignedDuration, &'a Zoned),
4478
0
    ) -> SpanArithmetic<'a> {
4479
0
        SpanArithmetic::from(duration).relative(zoned)
4480
0
    }
4481
}
4482
4483
impl From<UnsignedDuration> for SpanArithmetic<'static> {
4484
0
    fn from(duration: UnsignedDuration) -> SpanArithmetic<'static> {
4485
0
        let duration = Duration::from(duration);
4486
0
        SpanArithmetic { duration, relative: None }
4487
0
    }
4488
}
4489
4490
impl From<(UnsignedDuration, Date)> for SpanArithmetic<'static> {
4491
    #[inline]
4492
0
    fn from(
4493
0
        (duration, date): (UnsignedDuration, Date),
4494
0
    ) -> SpanArithmetic<'static> {
4495
0
        SpanArithmetic::from(duration).relative(date)
4496
0
    }
4497
}
4498
4499
impl From<(UnsignedDuration, DateTime)> for SpanArithmetic<'static> {
4500
    #[inline]
4501
0
    fn from(
4502
0
        (duration, datetime): (UnsignedDuration, DateTime),
4503
0
    ) -> SpanArithmetic<'static> {
4504
0
        SpanArithmetic::from(duration).relative(datetime)
4505
0
    }
4506
}
4507
4508
impl<'a> From<(UnsignedDuration, &'a Zoned)> for SpanArithmetic<'a> {
4509
    #[inline]
4510
0
    fn from(
4511
0
        (duration, zoned): (UnsignedDuration, &'a Zoned),
4512
0
    ) -> SpanArithmetic<'a> {
4513
0
        SpanArithmetic::from(duration).relative(zoned)
4514
0
    }
4515
}
4516
4517
/// Options for [`Span::compare`].
4518
///
4519
/// This type provides a way to ergonomically compare two spans with an
4520
/// optional relative datetime. Namely, a relative datetime is only needed when
4521
/// at least one of the two spans being compared has a non-zero calendar unit
4522
/// (years, months, weeks or days). Otherwise, an error will be returned.
4523
///
4524
/// Callers may use [`SpanCompare::days_are_24_hours`] to opt into 24-hour
4525
/// invariant days (and 7-day weeks) without providing a relative datetime.
4526
///
4527
/// The main way to construct values of this type is with its `From` trait
4528
/// implementations:
4529
///
4530
/// * `From<Span> for SpanCompare` compares the given span to the receiver
4531
/// in [`Span::compare`].
4532
/// * `From<(Span, civil::Date)> for SpanCompare` compares the given span
4533
/// to the receiver in [`Span::compare`], relative to the given date. There
4534
/// are also `From` implementations for `civil::DateTime`, `Zoned` and
4535
/// [`SpanRelativeTo`].
4536
///
4537
/// # Example
4538
///
4539
/// ```
4540
/// use jiff::ToSpan;
4541
///
4542
/// let span1 = 3.hours();
4543
/// let span2 = 180.minutes();
4544
/// assert_eq!(span1.compare(span2)?, std::cmp::Ordering::Equal);
4545
///
4546
/// # Ok::<(), Box<dyn std::error::Error>>(())
4547
/// ```
4548
#[derive(Clone, Copy, Debug)]
4549
pub struct SpanCompare<'a> {
4550
    span: Span,
4551
    relative: Option<SpanRelativeTo<'a>>,
4552
}
4553
4554
impl<'a> SpanCompare<'a> {
4555
    /// This is a convenience function for setting the relative option on
4556
    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4557
    ///
4558
    /// # Example
4559
    ///
4560
    /// When comparing spans involving days, either a relative datetime must be
4561
    /// provided, or a special assertion opting into 24-hour days is
4562
    /// required. Otherwise, you get an error.
4563
    ///
4564
    /// ```
4565
    /// use jiff::{SpanCompare, ToSpan, Unit};
4566
    ///
4567
    /// let span1 = 2.days().hours(12);
4568
    /// let span2 = 60.hours();
4569
    /// // No relative date provided, which results in an error.
4570
    /// assert_eq!(
4571
    ///     span1.compare(span2).unwrap_err().to_string(),
4572
    ///     "using unit 'day' in a span or configuration requires that \
4573
    ///      either a relative reference time be given or \
4574
    ///      `SpanRelativeTo::days_are_24_hours()` is used to indicate \
4575
    ///      invariant 24-hour days, but neither were provided",
4576
    /// );
4577
    /// let ordering = span1.compare(
4578
    ///     SpanCompare::from(span2).days_are_24_hours(),
4579
    /// )?;
4580
    /// assert_eq!(ordering, std::cmp::Ordering::Equal);
4581
    ///
4582
    /// # Ok::<(), Box<dyn std::error::Error>>(())
4583
    /// ```
4584
    #[inline]
4585
0
    pub fn days_are_24_hours(self) -> SpanCompare<'a> {
4586
0
        self.relative(SpanRelativeTo::days_are_24_hours())
4587
0
    }
4588
}
4589
4590
impl<'a> SpanCompare<'a> {
4591
    #[inline]
4592
0
    fn new(span: Span) -> SpanCompare<'static> {
4593
0
        SpanCompare { span, relative: None }
4594
0
    }
4595
4596
    #[inline]
4597
0
    fn relative<R: Into<SpanRelativeTo<'a>>>(
4598
0
        self,
4599
0
        relative: R,
4600
0
    ) -> SpanCompare<'a> {
4601
0
        SpanCompare { relative: Some(relative.into()), ..self }
4602
0
    }
4603
4604
0
    fn compare(self, span: Span) -> Result<Ordering, Error> {
4605
0
        let (span1, span2) = (span, self.span);
4606
0
        let unit = span1.largest_unit().max(span2.largest_unit());
4607
0
        let start = match self.relative {
4608
0
            Some(r) => match r.to_relative(unit)? {
4609
0
                Some(r) => r,
4610
                None => {
4611
0
                    let nanos1 = span1.to_invariant_nanoseconds();
4612
0
                    let nanos2 = span2.to_invariant_nanoseconds();
4613
0
                    return Ok(nanos1.cmp(&nanos2));
4614
                }
4615
            },
4616
            None => {
4617
0
                requires_relative_date_err(unit)?;
4618
0
                let nanos1 = span1.to_invariant_nanoseconds();
4619
0
                let nanos2 = span2.to_invariant_nanoseconds();
4620
0
                return Ok(nanos1.cmp(&nanos2));
4621
            }
4622
        };
4623
0
        let end1 = start.checked_add(span1)?.to_nanosecond();
4624
0
        let end2 = start.checked_add(span2)?.to_nanosecond();
4625
0
        Ok(end1.cmp(&end2))
4626
0
    }
4627
}
4628
4629
impl From<Span> for SpanCompare<'static> {
4630
0
    fn from(span: Span) -> SpanCompare<'static> {
4631
0
        SpanCompare::new(span)
4632
0
    }
4633
}
4634
4635
impl<'a> From<&'a Span> for SpanCompare<'static> {
4636
0
    fn from(span: &'a Span) -> SpanCompare<'static> {
4637
0
        SpanCompare::new(*span)
4638
0
    }
4639
}
4640
4641
impl From<(Span, Date)> for SpanCompare<'static> {
4642
    #[inline]
4643
0
    fn from((span, date): (Span, Date)) -> SpanCompare<'static> {
4644
0
        SpanCompare::from(span).relative(date)
4645
0
    }
4646
}
4647
4648
impl From<(Span, DateTime)> for SpanCompare<'static> {
4649
    #[inline]
4650
0
    fn from((span, datetime): (Span, DateTime)) -> SpanCompare<'static> {
4651
0
        SpanCompare::from(span).relative(datetime)
4652
0
    }
4653
}
4654
4655
impl<'a> From<(Span, &'a Zoned)> for SpanCompare<'a> {
4656
    #[inline]
4657
0
    fn from((span, zoned): (Span, &'a Zoned)) -> SpanCompare<'a> {
4658
0
        SpanCompare::from(span).relative(zoned)
4659
0
    }
4660
}
4661
4662
impl<'a> From<(Span, SpanRelativeTo<'a>)> for SpanCompare<'a> {
4663
    #[inline]
4664
0
    fn from((span, relative): (Span, SpanRelativeTo<'a>)) -> SpanCompare<'a> {
4665
0
        SpanCompare::from(span).relative(relative)
4666
0
    }
4667
}
4668
4669
impl<'a> From<(&'a Span, Date)> for SpanCompare<'static> {
4670
    #[inline]
4671
0
    fn from((span, date): (&'a Span, Date)) -> SpanCompare<'static> {
4672
0
        SpanCompare::from(span).relative(date)
4673
0
    }
4674
}
4675
4676
impl<'a> From<(&'a Span, DateTime)> for SpanCompare<'static> {
4677
    #[inline]
4678
0
    fn from((span, datetime): (&'a Span, DateTime)) -> SpanCompare<'static> {
4679
0
        SpanCompare::from(span).relative(datetime)
4680
0
    }
4681
}
4682
4683
impl<'a, 'b> From<(&'a Span, &'b Zoned)> for SpanCompare<'b> {
4684
    #[inline]
4685
0
    fn from((span, zoned): (&'a Span, &'b Zoned)) -> SpanCompare<'b> {
4686
0
        SpanCompare::from(span).relative(zoned)
4687
0
    }
4688
}
4689
4690
impl<'a, 'b> From<(&'a Span, SpanRelativeTo<'b>)> for SpanCompare<'b> {
4691
    #[inline]
4692
0
    fn from(
4693
0
        (span, relative): (&'a Span, SpanRelativeTo<'b>),
4694
0
    ) -> SpanCompare<'b> {
4695
0
        SpanCompare::from(span).relative(relative)
4696
0
    }
4697
}
4698
4699
/// Options for [`Span::total`].
4700
///
4701
/// This type provides a way to ergonomically determine the number of a
4702
/// particular unit in a span, with a potentially fractional component, with
4703
/// an optional relative datetime. Namely, a relative datetime is only needed
4704
/// when the span has a non-zero calendar unit (years, months, weeks or days).
4705
/// Otherwise, an error will be returned.
4706
///
4707
/// Callers may use [`SpanTotal::days_are_24_hours`] to opt into 24-hour
4708
/// invariant days (and 7-day weeks) without providing a relative datetime.
4709
///
4710
/// The main way to construct values of this type is with its `From` trait
4711
/// implementations:
4712
///
4713
/// * `From<Unit> for SpanTotal` computes a total for the given unit in the
4714
/// receiver span for [`Span::total`].
4715
/// * `From<(Unit, civil::Date)> for SpanTotal` computes a total for the given
4716
/// unit in the receiver span for [`Span::total`], relative to the given date.
4717
/// There are also `From` implementations for `civil::DateTime`, `Zoned` and
4718
/// [`SpanRelativeTo`].
4719
///
4720
/// # Example
4721
///
4722
/// This example shows how to find the number of seconds in a particular span:
4723
///
4724
/// ```
4725
/// use jiff::{ToSpan, Unit};
4726
///
4727
/// let span = 3.hours().minutes(10);
4728
/// assert_eq!(span.total(Unit::Second)?, 11_400.0);
4729
///
4730
/// # Ok::<(), Box<dyn std::error::Error>>(())
4731
/// ```
4732
///
4733
/// # Example: 24 hour days
4734
///
4735
/// This shows how to find the total number of 24 hour days in `123,456,789`
4736
/// seconds.
4737
///
4738
/// ```
4739
/// use jiff::{SpanTotal, ToSpan, Unit};
4740
///
4741
/// let span = 123_456_789.seconds();
4742
/// assert_eq!(
4743
///     span.total(SpanTotal::from(Unit::Day).days_are_24_hours())?,
4744
///     1428.8980208333332,
4745
/// );
4746
///
4747
/// # Ok::<(), Box<dyn std::error::Error>>(())
4748
/// ```
4749
///
4750
/// # Example: DST is taken into account
4751
///
4752
/// The month of March 2024 in `America/New_York` had 31 days, but one of those
4753
/// days was 23 hours long due a transition into daylight saving time:
4754
///
4755
/// ```
4756
/// use jiff::{civil::date, ToSpan, Unit};
4757
///
4758
/// let span = 744.hours();
4759
/// let relative = date(2024, 3, 1).in_tz("America/New_York")?;
4760
/// // Because of the short day, 744 hours is actually a little *more* than
4761
/// // 1 month starting from 2024-03-01.
4762
/// assert_eq!(span.total((Unit::Month, &relative))?, 1.0013888888888889);
4763
///
4764
/// # Ok::<(), Box<dyn std::error::Error>>(())
4765
/// ```
4766
///
4767
/// Now compare what happens when the relative datetime is civil and not
4768
/// time zone aware:
4769
///
4770
/// ```
4771
/// use jiff::{civil::date, ToSpan, Unit};
4772
///
4773
/// let span = 744.hours();
4774
/// let relative = date(2024, 3, 1);
4775
/// assert_eq!(span.total((Unit::Month, relative))?, 1.0);
4776
///
4777
/// # Ok::<(), Box<dyn std::error::Error>>(())
4778
/// ```
4779
#[derive(Clone, Copy, Debug)]
4780
pub struct SpanTotal<'a> {
4781
    unit: Unit,
4782
    relative: Option<SpanRelativeTo<'a>>,
4783
}
4784
4785
impl<'a> SpanTotal<'a> {
4786
    /// This is a convenience function for setting the relative option on
4787
    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
4788
    ///
4789
    /// # Example
4790
    ///
4791
    /// When computing the total duration for spans involving days, either a
4792
    /// relative datetime must be provided, or a special assertion opting into
4793
    /// 24-hour days is required. Otherwise, you get an error.
4794
    ///
4795
    /// ```
4796
    /// use jiff::{civil::date, SpanTotal, ToSpan, Unit};
4797
    ///
4798
    /// let span = 2.days().hours(12);
4799
    ///
4800
    /// // No relative date provided, which results in an error.
4801
    /// assert_eq!(
4802
    ///     span.total(Unit::Hour).unwrap_err().to_string(),
4803
    ///     "using unit 'day' in a span or configuration requires that either \
4804
    ///      a relative reference time be given or \
4805
    ///      `SpanRelativeTo::days_are_24_hours()` is used to indicate \
4806
    ///      invariant 24-hour days, but neither were provided",
4807
    /// );
4808
    ///
4809
    /// // If we can assume all days are 24 hours, then we can assert it:
4810
    /// let total = span.total(
4811
    ///     SpanTotal::from(Unit::Hour).days_are_24_hours(),
4812
    /// )?;
4813
    /// assert_eq!(total, 60.0);
4814
    ///
4815
    /// // Or provide a relative datetime, which is preferred if possible:
4816
    /// let total = span.total((Unit::Hour, date(2025, 1, 26)))?;
4817
    /// assert_eq!(total, 60.0);
4818
    ///
4819
    /// # Ok::<(), Box<dyn std::error::Error>>(())
4820
    /// ```
4821
    #[inline]
4822
0
    pub fn days_are_24_hours(self) -> SpanTotal<'a> {
4823
0
        self.relative(SpanRelativeTo::days_are_24_hours())
4824
0
    }
4825
}
4826
4827
impl<'a> SpanTotal<'a> {
4828
    #[inline]
4829
0
    fn new(unit: Unit) -> SpanTotal<'static> {
4830
0
        SpanTotal { unit, relative: None }
4831
0
    }
4832
4833
    #[inline]
4834
0
    fn relative<R: Into<SpanRelativeTo<'a>>>(
4835
0
        self,
4836
0
        relative: R,
4837
0
    ) -> SpanTotal<'a> {
4838
0
        SpanTotal { relative: Some(relative.into()), ..self }
4839
0
    }
4840
4841
0
    fn total(self, span: Span) -> Result<f64, Error> {
4842
0
        let max_unit = self.unit.max(span.largest_unit());
4843
0
        let relative = match self.relative {
4844
0
            Some(r) => match r.to_relative(max_unit)? {
4845
0
                Some(r) => r,
4846
                None => {
4847
0
                    return Ok(self.total_invariant(span));
4848
                }
4849
            },
4850
            None => {
4851
0
                requires_relative_date_err(max_unit)?;
4852
0
                return Ok(self.total_invariant(span));
4853
            }
4854
        };
4855
0
        let relspan = relative.into_relative_span(self.unit, span)?;
4856
0
        if !self.unit.is_variable() {
4857
0
            return Ok(self.total_invariant(relspan.span));
4858
0
        }
4859
4860
0
        assert!(self.unit >= Unit::Day);
4861
0
        let sign = relspan.span.get_sign_ranged();
4862
0
        let (relative_start, relative_end) = match relspan.kind {
4863
0
            RelativeSpanKind::Civil { start, end } => {
4864
0
                let start = Relative::Civil(start);
4865
0
                let end = Relative::Civil(end);
4866
0
                (start, end)
4867
            }
4868
0
            RelativeSpanKind::Zoned { start, end } => {
4869
0
                let start = Relative::Zoned(start);
4870
0
                let end = Relative::Zoned(end);
4871
0
                (start, end)
4872
            }
4873
        };
4874
0
        let (relative0, relative1) = clamp_relative_span(
4875
0
            &relative_start,
4876
0
            relspan.span.without_lower(self.unit),
4877
0
            self.unit,
4878
0
            sign.rinto(),
4879
0
        )?;
4880
0
        let denom = (relative1 - relative0).get() as f64;
4881
0
        let numer = (relative_end.to_nanosecond() - relative0).get() as f64;
4882
0
        let unit_val = relspan.span.get_units_ranged(self.unit).get() as f64;
4883
0
        Ok(unit_val + (numer / denom) * (sign.get() as f64))
4884
0
    }
4885
4886
    #[inline]
4887
0
    fn total_invariant(&self, span: Span) -> f64 {
4888
0
        assert!(self.unit <= Unit::Week);
4889
0
        let nanos = span.to_invariant_nanoseconds();
4890
0
        (nanos.get() as f64) / (self.unit.nanoseconds().get() as f64)
4891
0
    }
4892
}
4893
4894
impl From<Unit> for SpanTotal<'static> {
4895
    #[inline]
4896
0
    fn from(unit: Unit) -> SpanTotal<'static> {
4897
0
        SpanTotal::new(unit)
4898
0
    }
4899
}
4900
4901
impl From<(Unit, Date)> for SpanTotal<'static> {
4902
    #[inline]
4903
0
    fn from((unit, date): (Unit, Date)) -> SpanTotal<'static> {
4904
0
        SpanTotal::from(unit).relative(date)
4905
0
    }
4906
}
4907
4908
impl From<(Unit, DateTime)> for SpanTotal<'static> {
4909
    #[inline]
4910
0
    fn from((unit, datetime): (Unit, DateTime)) -> SpanTotal<'static> {
4911
0
        SpanTotal::from(unit).relative(datetime)
4912
0
    }
4913
}
4914
4915
impl<'a> From<(Unit, &'a Zoned)> for SpanTotal<'a> {
4916
    #[inline]
4917
0
    fn from((unit, zoned): (Unit, &'a Zoned)) -> SpanTotal<'a> {
4918
0
        SpanTotal::from(unit).relative(zoned)
4919
0
    }
4920
}
4921
4922
impl<'a> From<(Unit, SpanRelativeTo<'a>)> for SpanTotal<'a> {
4923
    #[inline]
4924
0
    fn from((unit, relative): (Unit, SpanRelativeTo<'a>)) -> SpanTotal<'a> {
4925
0
        SpanTotal::from(unit).relative(relative)
4926
0
    }
4927
}
4928
4929
/// Options for [`Span::round`].
4930
///
4931
/// This type provides a way to configure the rounding of a span. This
4932
/// includes setting the smallest unit (i.e., the unit to round), the
4933
/// largest unit, the rounding increment, the rounding mode (e.g., "ceil" or
4934
/// "truncate") and the datetime that the span is relative to.
4935
///
4936
/// `Span::round` accepts anything that implements `Into<SpanRound>`. There are
4937
/// a few key trait implementations that make this convenient:
4938
///
4939
/// * `From<Unit> for SpanRound` will construct a rounding configuration where
4940
/// the smallest unit is set to the one given.
4941
/// * `From<(Unit, i64)> for SpanRound` will construct a rounding configuration
4942
/// where the smallest unit and the rounding increment are set to the ones
4943
/// given.
4944
///
4945
/// In order to set other options (like the largest unit, the rounding mode
4946
/// and the relative datetime), one must explicitly create a `SpanRound` and
4947
/// pass it to `Span::round`.
4948
///
4949
/// # Example
4950
///
4951
/// This example shows how to find how many full 3 month quarters are in a
4952
/// particular span of time.
4953
///
4954
/// ```
4955
/// use jiff::{civil::date, RoundMode, SpanRound, ToSpan, Unit};
4956
///
4957
/// let span1 = 10.months().days(15);
4958
/// let round = SpanRound::new()
4959
///     .smallest(Unit::Month)
4960
///     .increment(3)
4961
///     .mode(RoundMode::Trunc)
4962
///     // A relative datetime must be provided when
4963
///     // rounding involves calendar units.
4964
///     .relative(date(2024, 1, 1));
4965
/// let span2 = span1.round(round)?;
4966
/// assert_eq!(span2.get_months() / 3, 3);
4967
///
4968
/// # Ok::<(), Box<dyn std::error::Error>>(())
4969
/// ```
4970
#[derive(Clone, Copy, Debug)]
4971
pub struct SpanRound<'a> {
4972
    largest: Option<Unit>,
4973
    smallest: Unit,
4974
    mode: RoundMode,
4975
    increment: i64,
4976
    relative: Option<SpanRelativeTo<'a>>,
4977
}
4978
4979
impl<'a> SpanRound<'a> {
4980
    /// Create a new default configuration for rounding a span via
4981
    /// [`Span::round`].
4982
    ///
4983
    /// The default configuration does no rounding.
4984
    #[inline]
4985
0
    pub fn new() -> SpanRound<'static> {
4986
0
        SpanRound {
4987
0
            largest: None,
4988
0
            smallest: Unit::Nanosecond,
4989
0
            mode: RoundMode::HalfExpand,
4990
0
            increment: 1,
4991
0
            relative: None,
4992
0
        }
4993
0
    }
4994
4995
    /// Set the smallest units allowed in the span returned. These are the
4996
    /// units that the span is rounded to.
4997
    ///
4998
    /// # Errors
4999
    ///
5000
    /// The smallest units must be no greater than the largest units. If this
5001
    /// is violated, then rounding a span with this configuration will result
5002
    /// in an error.
5003
    ///
5004
    /// If a smallest unit bigger than days is selected without a relative
5005
    /// datetime reference point, then an error is returned when using this
5006
    /// configuration with [`Span::round`].
5007
    ///
5008
    /// # Example
5009
    ///
5010
    /// A basic example that rounds to the nearest minute:
5011
    ///
5012
    /// ```
5013
    /// use jiff::{ToSpan, Unit};
5014
    ///
5015
    /// let span = 15.minutes().seconds(46);
5016
    /// assert_eq!(span.round(Unit::Minute)?, 16.minutes().fieldwise());
5017
    ///
5018
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5019
    /// ```
5020
    #[inline]
5021
0
    pub fn smallest(self, unit: Unit) -> SpanRound<'a> {
5022
0
        SpanRound { smallest: unit, ..self }
5023
0
    }
5024
5025
    /// Set the largest units allowed in the span returned.
5026
    ///
5027
    /// When a largest unit is not specified, then it defaults to the largest
5028
    /// non-zero unit that is at least as big as the configured smallest
5029
    /// unit. For example, given a span of `2 months 17 hours`, the default
5030
    /// largest unit would be `Unit::Month`. The default implies that a span's
5031
    /// units do not get "bigger" than what was given.
5032
    ///
5033
    /// Once a largest unit is set, there is no way to change this rounding
5034
    /// configuration back to using the "automatic" default. Instead, callers
5035
    /// must create a new configuration.
5036
    ///
5037
    /// If a largest unit is set and no other options are set, then the
5038
    /// rounding operation can be said to be a "re-balancing." That is, the
5039
    /// span won't lose precision, but the way in which it is expressed may
5040
    /// change.
5041
    ///
5042
    /// # Errors
5043
    ///
5044
    /// The largest units, when set, must be at least as big as the smallest
5045
    /// units (which defaults to [`Unit::Nanosecond`]). If this is violated,
5046
    /// then rounding a span with this configuration will result in an error.
5047
    ///
5048
    /// If a largest unit bigger than days is selected without a relative
5049
    /// datetime reference point, then an error is returned when using this
5050
    /// configuration with [`Span::round`].
5051
    ///
5052
    /// # Example: re-balancing
5053
    ///
5054
    /// This shows how a span can be re-balanced without losing precision:
5055
    ///
5056
    /// ```
5057
    /// use jiff::{SpanRound, ToSpan, Unit};
5058
    ///
5059
    /// let span = 86_401_123_456_789i64.nanoseconds();
5060
    /// assert_eq!(
5061
    ///     span.round(SpanRound::new().largest(Unit::Hour))?.fieldwise(),
5062
    ///     24.hours().seconds(1).milliseconds(123).microseconds(456).nanoseconds(789),
5063
    /// );
5064
    ///
5065
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5066
    /// ```
5067
    ///
5068
    /// If you need to use a largest unit bigger than hours, then you must
5069
    /// provide a relative datetime as a reference point (otherwise an error
5070
    /// will occur):
5071
    ///
5072
    /// ```
5073
    /// use jiff::{civil::date, SpanRound, ToSpan, Unit};
5074
    ///
5075
    /// let span = 3_968_000.seconds();
5076
    /// let round = SpanRound::new()
5077
    ///     .largest(Unit::Day)
5078
    ///     .relative(date(2024, 7, 1));
5079
    /// assert_eq!(
5080
    ///     span.round(round)?,
5081
    ///     45.days().hours(22).minutes(13).seconds(20).fieldwise(),
5082
    /// );
5083
    ///
5084
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5085
    /// ```
5086
    ///
5087
    /// As a special case for days, one can instead opt into invariant 24-hour
5088
    /// days (and 7-day weeks) without providing an explicit relative date:
5089
    ///
5090
    /// ```
5091
    /// use jiff::{SpanRound, ToSpan, Unit};
5092
    ///
5093
    /// let span = 86_401_123_456_789i64.nanoseconds();
5094
    /// assert_eq!(
5095
    ///     span.round(
5096
    ///         SpanRound::new().largest(Unit::Day).days_are_24_hours(),
5097
    ///     )?.fieldwise(),
5098
    ///     1.day().seconds(1).milliseconds(123).microseconds(456).nanoseconds(789),
5099
    /// );
5100
    ///
5101
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5102
    /// ```
5103
    ///
5104
    /// # Example: re-balancing while taking DST into account
5105
    ///
5106
    /// When given a zone aware relative datetime, rounding will even take
5107
    /// DST into account:
5108
    ///
5109
    /// ```
5110
    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5111
    ///
5112
    /// let span = 2756.hours();
5113
    /// let zdt = "2020-01-01T00:00+01:00[Europe/Rome]".parse::<Zoned>()?;
5114
    /// let round = SpanRound::new().largest(Unit::Year).relative(&zdt);
5115
    /// assert_eq!(
5116
    ///     span.round(round)?,
5117
    ///     3.months().days(23).hours(21).fieldwise(),
5118
    /// );
5119
    ///
5120
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5121
    /// ```
5122
    ///
5123
    /// Now compare with the same operation, but on a civil datetime (which is
5124
    /// not aware of time zone):
5125
    ///
5126
    /// ```
5127
    /// use jiff::{civil::DateTime, SpanRound, ToSpan, Unit};
5128
    ///
5129
    /// let span = 2756.hours();
5130
    /// let dt = "2020-01-01T00:00".parse::<DateTime>()?;
5131
    /// let round = SpanRound::new().largest(Unit::Year).relative(dt);
5132
    /// assert_eq!(
5133
    ///     span.round(round)?,
5134
    ///     3.months().days(23).hours(20).fieldwise(),
5135
    /// );
5136
    ///
5137
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5138
    /// ```
5139
    ///
5140
    /// The result is 1 hour shorter. This is because, in the zone
5141
    /// aware re-balancing, it accounts for the transition into DST at
5142
    /// `2020-03-29T01:00Z`, which skips an hour. This makes the span one hour
5143
    /// longer because one of the days in the span is actually only 23 hours
5144
    /// long instead of 24 hours.
5145
    #[inline]
5146
0
    pub fn largest(self, unit: Unit) -> SpanRound<'a> {
5147
0
        SpanRound { largest: Some(unit), ..self }
5148
0
    }
5149
5150
    /// Set the rounding mode.
5151
    ///
5152
    /// This defaults to [`RoundMode::HalfExpand`], which makes rounding work
5153
    /// like how you were taught in school.
5154
    ///
5155
    /// # Example
5156
    ///
5157
    /// A basic example that rounds to the nearest minute, but changing its
5158
    /// rounding mode to truncation:
5159
    ///
5160
    /// ```
5161
    /// use jiff::{RoundMode, SpanRound, ToSpan, Unit};
5162
    ///
5163
    /// let span = 15.minutes().seconds(46);
5164
    /// assert_eq!(
5165
    ///     span.round(SpanRound::new()
5166
    ///         .smallest(Unit::Minute)
5167
    ///         .mode(RoundMode::Trunc),
5168
    ///     )?,
5169
    ///     // The default round mode does rounding like
5170
    ///     // how you probably learned in school, and would
5171
    ///     // result in rounding up to 16 minutes. But we
5172
    ///     // change it to truncation here, which makes it
5173
    ///     // round down.
5174
    ///     15.minutes().fieldwise(),
5175
    /// );
5176
    ///
5177
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5178
    /// ```
5179
    #[inline]
5180
0
    pub fn mode(self, mode: RoundMode) -> SpanRound<'a> {
5181
0
        SpanRound { mode, ..self }
5182
0
    }
5183
5184
    /// Set the rounding increment for the smallest unit.
5185
    ///
5186
    /// The default value is `1`. Other values permit rounding the smallest
5187
    /// unit to the nearest integer increment specified. For example, if the
5188
    /// smallest unit is set to [`Unit::Minute`], then a rounding increment of
5189
    /// `30` would result in rounding in increments of a half hour. That is,
5190
    /// the only minute value that could result would be `0` or `30`.
5191
    ///
5192
    /// # Errors
5193
    ///
5194
    /// When the smallest unit is less than days, the rounding increment must
5195
    /// divide evenly into the next highest unit after the smallest unit
5196
    /// configured (and must not be equivalent to it). For example, if the
5197
    /// smallest unit is [`Unit::Nanosecond`], then *some* of the valid values
5198
    /// for the rounding increment are `1`, `2`, `4`, `5`, `100` and `500`.
5199
    /// Namely, any integer that divides evenly into `1,000` nanoseconds since
5200
    /// there are `1,000` nanoseconds in the next highest unit (microseconds).
5201
    ///
5202
    /// The error will occur when computing the span, and not when setting
5203
    /// the increment here.
5204
    ///
5205
    /// # Example
5206
    ///
5207
    /// This shows how to round a span to the nearest 5 minute increment:
5208
    ///
5209
    /// ```
5210
    /// use jiff::{ToSpan, Unit};
5211
    ///
5212
    /// let span = 4.hours().minutes(2).seconds(30);
5213
    /// assert_eq!(
5214
    ///     span.round((Unit::Minute, 5))?,
5215
    ///     4.hours().minutes(5).fieldwise(),
5216
    /// );
5217
    ///
5218
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5219
    /// ```
5220
    #[inline]
5221
0
    pub fn increment(self, increment: i64) -> SpanRound<'a> {
5222
0
        SpanRound { increment, ..self }
5223
0
    }
5224
5225
    /// Set the relative datetime to use when rounding a span.
5226
    ///
5227
    /// A relative datetime is only required when calendar units (units greater
5228
    /// than days) are involved. This includes having calendar units in the
5229
    /// original span, or calendar units in the configured smallest or largest
5230
    /// unit. A relative datetime is required when calendar units are used
5231
    /// because the duration of a particular calendar unit (like 1 month or 1
5232
    /// year) is variable and depends on the date. For example, 1 month from
5233
    /// 2024-01-01 is 31 days, but 1 month from 2024-02-01 is 29 days.
5234
    ///
5235
    /// A relative datetime is provided by anything that implements
5236
    /// `Into<SpanRelativeTo>`. There are a few convenience trait
5237
    /// implementations provided:
5238
    ///
5239
    /// * `From<&Zoned> for SpanRelativeTo` uses a zone aware datetime to do
5240
    /// rounding. In this case, rounding will take time zone transitions into
5241
    /// account. In particular, when using a zoned relative datetime, not all
5242
    /// days are necessarily 24 hours.
5243
    /// * `From<civil::DateTime> for SpanRelativeTo` uses a civil datetime. In
5244
    /// this case, all days will be considered 24 hours long.
5245
    /// * `From<civil::Date> for SpanRelativeTo` uses a civil date. In this
5246
    /// case, all days will be considered 24 hours long.
5247
    ///
5248
    /// Note that one can impose 24-hour days without providing a reference
5249
    /// date via [`SpanRelativeTo::days_are_24_hours`].
5250
    ///
5251
    /// # Errors
5252
    ///
5253
    /// If rounding involves a calendar unit (units bigger than hours) and no
5254
    /// relative datetime is provided, then this configuration will lead to
5255
    /// an error when used with [`Span::round`].
5256
    ///
5257
    /// # Example
5258
    ///
5259
    /// This example shows very precisely how a DST transition can impact
5260
    /// rounding and re-balancing. For example, consider the day `2024-11-03`
5261
    /// in `America/New_York`. On this day, the 1 o'clock hour was repeated,
5262
    /// making the day 24 hours long. This will be taken into account when
5263
    /// rounding if a zoned datetime is provided as a reference point:
5264
    ///
5265
    /// ```
5266
    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5267
    ///
5268
    /// let zdt = "2024-11-03T00-04[America/New_York]".parse::<Zoned>()?;
5269
    /// let round = SpanRound::new().largest(Unit::Hour).relative(&zdt);
5270
    /// assert_eq!(1.day().round(round)?, 25.hours().fieldwise());
5271
    ///
5272
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5273
    /// ```
5274
    ///
5275
    /// And similarly for `2024-03-10`, where the 2 o'clock hour was skipped
5276
    /// entirely:
5277
    ///
5278
    /// ```
5279
    /// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5280
    ///
5281
    /// let zdt = "2024-03-10T00-05[America/New_York]".parse::<Zoned>()?;
5282
    /// let round = SpanRound::new().largest(Unit::Hour).relative(&zdt);
5283
    /// assert_eq!(1.day().round(round)?, 23.hours().fieldwise());
5284
    ///
5285
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5286
    /// ```
5287
    #[inline]
5288
0
    pub fn relative<R: Into<SpanRelativeTo<'a>>>(
5289
0
        self,
5290
0
        relative: R,
5291
0
    ) -> SpanRound<'a> {
5292
0
        SpanRound { relative: Some(relative.into()), ..self }
5293
0
    }
Unexecuted instantiation: <jiff::span::SpanRound>::relative::<jiff::civil::date::Date>
Unexecuted instantiation: <jiff::span::SpanRound>::relative::<jiff::civil::datetime::DateTime>
Unexecuted instantiation: <jiff::span::SpanRound>::relative::<&jiff::zoned::Zoned>
5294
5295
    /// This is a convenience function for setting the relative option on
5296
    /// this configuration to [`SpanRelativeTo::days_are_24_hours`].
5297
    ///
5298
    /// # Example
5299
    ///
5300
    /// When rounding spans involving days, either a relative datetime must be
5301
    /// provided, or a special assertion opting into 24-hour days is
5302
    /// required. Otherwise, you get an error.
5303
    ///
5304
    /// ```
5305
    /// use jiff::{SpanRound, ToSpan, Unit};
5306
    ///
5307
    /// let span = 2.days().hours(12);
5308
    /// // No relative date provided, which results in an error.
5309
    /// assert_eq!(
5310
    ///     span.round(Unit::Day).unwrap_err().to_string(),
5311
    ///     "error with `smallest` rounding option: using unit 'day' in a \
5312
    ///      span or configuration requires that either a relative reference \
5313
    ///      time be given or `SpanRelativeTo::days_are_24_hours()` is used \
5314
    ///      to indicate invariant 24-hour days, but neither were provided",
5315
    /// );
5316
    /// let rounded = span.round(
5317
    ///     SpanRound::new().smallest(Unit::Day).days_are_24_hours(),
5318
    /// )?;
5319
    /// assert_eq!(rounded, 3.days().fieldwise());
5320
    ///
5321
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5322
    /// ```
5323
    #[inline]
5324
0
    pub fn days_are_24_hours(self) -> SpanRound<'a> {
5325
0
        self.relative(SpanRelativeTo::days_are_24_hours())
5326
0
    }
5327
5328
    /// Returns the configured smallest unit on this round configuration.
5329
    #[inline]
5330
0
    pub(crate) fn get_smallest(&self) -> Unit {
5331
0
        self.smallest
5332
0
    }
5333
5334
    /// Returns the configured largest unit on this round configuration.
5335
    #[inline]
5336
0
    pub(crate) fn get_largest(&self) -> Option<Unit> {
5337
0
        self.largest
5338
0
    }
5339
5340
    /// Returns true only when rounding a span *may* change it. When it
5341
    /// returns false, and if the span is already balanced according to
5342
    /// the largest unit in this round configuration, then it is guaranteed
5343
    /// that rounding is a no-op.
5344
    ///
5345
    /// This is useful to avoid rounding calls after doing span arithmetic
5346
    /// on datetime types. This works because the "largest" unit is used to
5347
    /// construct a balanced span for the difference between two datetimes.
5348
    /// So we already know the span has been balanced. If this weren't the
5349
    /// case, then the largest unit being different from the one in the span
5350
    /// could result in rounding making a change. (And indeed, in the general
5351
    /// case of span rounding below, we do a more involved check for this.)
5352
    #[inline]
5353
0
    pub(crate) fn rounding_may_change_span_ignore_largest(&self) -> bool {
5354
0
        self.smallest > Unit::Nanosecond || self.increment > 1
5355
0
    }
5356
5357
    /// Does the actual span rounding.
5358
0
    fn round(&self, span: Span) -> Result<Span, Error> {
5359
0
        let existing_largest = span.largest_unit();
5360
0
        let mode = self.mode;
5361
0
        let smallest = self.smallest;
5362
0
        let largest =
5363
0
            self.largest.unwrap_or_else(|| smallest.max(existing_largest));
5364
0
        let max = existing_largest.max(largest);
5365
0
        let increment = increment::for_span(smallest, self.increment)?;
5366
0
        if largest < smallest {
5367
0
            return Err(err!(
5368
0
                "largest unit ('{largest}') cannot be smaller than \
5369
0
                 smallest unit ('{smallest}')",
5370
0
                largest = largest.singular(),
5371
0
                smallest = smallest.singular(),
5372
0
            ));
5373
0
        }
5374
0
        let relative = match self.relative {
5375
0
            Some(ref r) => {
5376
0
                match r.to_relative(max)? {
5377
0
                    Some(r) => r,
5378
                    None => {
5379
                        // If our reference point is civil time, then its units
5380
                        // are invariant as long as we are using day-or-lower
5381
                        // everywhere. That is, the length of the duration is
5382
                        // independent of the reference point. In which case,
5383
                        // rounding is a simple matter of converting the span
5384
                        // to a number of nanoseconds and then rounding that.
5385
0
                        return Ok(round_span_invariant(
5386
0
                            span, smallest, largest, increment, mode,
5387
0
                        )?);
5388
                    }
5389
                }
5390
            }
5391
            None => {
5392
                // This is only okay if none of our units are above 'day'.
5393
                // That is, a reference point is only necessary when there is
5394
                // no reasonable invariant interpretation of the span. And this
5395
                // is only true when everything is less than 'day'.
5396
0
                requires_relative_date_err(smallest)
5397
0
                    .context("error with `smallest` rounding option")?;
5398
0
                if let Some(largest) = self.largest {
5399
0
                    requires_relative_date_err(largest)
5400
0
                        .context("error with `largest` rounding option")?;
5401
0
                }
5402
0
                requires_relative_date_err(existing_largest).context(
5403
                    "error with largest unit in span to be rounded",
5404
0
                )?;
5405
0
                assert!(max <= Unit::Week);
5406
0
                return Ok(round_span_invariant(
5407
0
                    span, smallest, largest, increment, mode,
5408
0
                )?);
5409
            }
5410
        };
5411
0
        relative.round(span, smallest, largest, increment, mode)
5412
0
    }
5413
}
5414
5415
impl Default for SpanRound<'static> {
5416
0
    fn default() -> SpanRound<'static> {
5417
0
        SpanRound::new()
5418
0
    }
5419
}
5420
5421
impl From<Unit> for SpanRound<'static> {
5422
0
    fn from(unit: Unit) -> SpanRound<'static> {
5423
0
        SpanRound::default().smallest(unit)
5424
0
    }
5425
}
5426
5427
impl From<(Unit, i64)> for SpanRound<'static> {
5428
0
    fn from((unit, increment): (Unit, i64)) -> SpanRound<'static> {
5429
0
        SpanRound::default().smallest(unit).increment(increment)
5430
0
    }
5431
}
5432
5433
/// A relative datetime for use with [`Span`] APIs.
5434
///
5435
/// A relative datetime can be one of the following: [`civil::Date`](Date),
5436
/// [`civil::DateTime`](DateTime) or [`Zoned`]. It can be constructed from any
5437
/// of the preceding types via `From` trait implementations.
5438
///
5439
/// A relative datetime is used to indicate how the calendar units of a `Span`
5440
/// should be interpreted. For example, the span "1 month" does not have a
5441
/// fixed meaning. One month from `2024-03-01` is 31 days, but one month from
5442
/// `2024-04-01` is 30 days. Similar for years.
5443
///
5444
/// When a relative datetime in time zone aware (i.e., it is a `Zoned`), then
5445
/// a `Span` will also consider its day units to be variable in length. For
5446
/// example, `2024-03-10` in `America/New_York` was only 23 hours long, where
5447
/// as `2024-11-03` in `America/New_York` was 25 hours long. When a relative
5448
/// datetime is civil, then days are considered to always be of a fixed 24
5449
/// hour length.
5450
///
5451
/// This type is principally used as an input to one of several different
5452
/// [`Span`] APIs:
5453
///
5454
/// * [`Span::round`] rounds spans. A relative datetime is necessary when
5455
/// dealing with calendar units. (But spans without calendar units can be
5456
/// rounded without providing a relative datetime.)
5457
/// * Span arithmetic via [`Span::checked_add`] and [`Span::checked_sub`].
5458
/// A relative datetime is needed when adding or subtracting spans with
5459
/// calendar units.
5460
/// * Span comarisons via [`Span::compare`] require a relative datetime when
5461
/// comparing spans with calendar units.
5462
/// * Computing the "total" duration as a single floating point number via
5463
/// [`Span::total`] also requires a relative datetime when dealing with
5464
/// calendar units.
5465
///
5466
/// # Example
5467
///
5468
/// This example shows how to round a span with larger calendar units to
5469
/// smaller units:
5470
///
5471
/// ```
5472
/// use jiff::{SpanRound, ToSpan, Unit, Zoned};
5473
///
5474
/// let zdt: Zoned = "2012-01-01[Antarctica/Troll]".parse()?;
5475
/// let round = SpanRound::new().largest(Unit::Day).relative(&zdt);
5476
/// assert_eq!(1.year().round(round)?, 366.days().fieldwise());
5477
///
5478
/// // If you tried this without a relative datetime, it would fail:
5479
/// let round = SpanRound::new().largest(Unit::Day);
5480
/// assert!(1.year().round(round).is_err());
5481
///
5482
/// # Ok::<(), Box<dyn std::error::Error>>(())
5483
/// ```
5484
#[derive(Clone, Copy, Debug)]
5485
pub struct SpanRelativeTo<'a> {
5486
    kind: SpanRelativeToKind<'a>,
5487
}
5488
5489
impl<'a> SpanRelativeTo<'a> {
5490
    /// Creates a special marker that indicates all days ought to be assumed
5491
    /// to be 24 hours without providing a relative reference time.
5492
    ///
5493
    /// This is relevant to the following APIs:
5494
    ///
5495
    /// * [`Span::checked_add`]
5496
    /// * [`Span::checked_sub`]
5497
    /// * [`Span::compare`]
5498
    /// * [`Span::total`]
5499
    /// * [`Span::round`]
5500
    /// * [`Span::to_duration`]
5501
    ///
5502
    /// Specifically, in a previous version of Jiff, the above APIs permitted
5503
    /// _silently_ assuming that days are always 24 hours when a relative
5504
    /// reference date wasn't provided. In the current version of Jiff, this
5505
    /// silent interpretation no longer happens and instead an error will
5506
    /// occur.
5507
    ///
5508
    /// If you need to use these APIs with spans that contain non-zero units
5509
    /// of days or weeks but without a relative reference date, then you may
5510
    /// use this routine to create a special marker for `SpanRelativeTo` that
5511
    /// permits the APIs above to assume days are always 24 hours.
5512
    ///
5513
    /// # Motivation
5514
    ///
5515
    /// The purpose of the marker is two-fold:
5516
    ///
5517
    /// * Requiring the marker is important for improving the consistency of
5518
    /// `Span` APIs. Previously, some APIs (like [`Timestamp::checked_add`])
5519
    /// would always return an error if the `Span` given had non-zero
5520
    /// units of days or greater. On the other hand, other APIs (like
5521
    /// [`Span::checked_add`]) would autoamtically assume days were always
5522
    /// 24 hours if no relative reference time was given and either span had
5523
    /// non-zero units of days. With this marker, APIs _never_ assume days are
5524
    /// always 24 hours automatically.
5525
    /// * When it _is_ appropriate to assume all days are 24 hours
5526
    /// (for example, when only dealing with spans derived from
5527
    /// [`civil`](crate::civil) datetimes) and where providing a relative
5528
    /// reference datetime doesn't make sense. In this case, one _could_
5529
    /// provide a "dummy" reference date since the precise date in civil time
5530
    /// doesn't impact the length of a day. But a marker like the one returned
5531
    /// here is more explicit for the purpose of assuming days are always 24
5532
    /// hours.
5533
    ///
5534
    /// With that said, ideally, callers should provide a relative reference
5535
    /// datetime if possible.
5536
    ///
5537
    /// See [Issue #48] for more discussion on this topic.
5538
    ///
5539
    /// # Example: different interpretations of "1 day"
5540
    ///
5541
    /// This example shows how "1 day" can be interpreted differently via the
5542
    /// [`Span::total`] API:
5543
    ///
5544
    /// ```
5545
    /// use jiff::{SpanRelativeTo, ToSpan, Unit, Zoned};
5546
    ///
5547
    /// let span = 1.day();
5548
    ///
5549
    /// // An error because days aren't always 24 hours:
5550
    /// assert_eq!(
5551
    ///     span.total(Unit::Hour).unwrap_err().to_string(),
5552
    ///     "using unit 'day' in a span or configuration requires that either \
5553
    ///      a relative reference time be given or \
5554
    ///      `SpanRelativeTo::days_are_24_hours()` is used to indicate \
5555
    ///      invariant 24-hour days, but neither were provided",
5556
    /// );
5557
    /// // Opt into invariant 24 hour days without a relative date:
5558
    /// let marker = SpanRelativeTo::days_are_24_hours();
5559
    /// let hours = span.total((Unit::Hour, marker))?;
5560
    /// assert_eq!(hours, 24.0);
5561
    /// // Days can be shorter than 24 hours:
5562
    /// let zdt: Zoned = "2024-03-10[America/New_York]".parse()?;
5563
    /// let hours = span.total((Unit::Hour, &zdt))?;
5564
    /// assert_eq!(hours, 23.0);
5565
    /// // Days can be longer than 24 hours:
5566
    /// let zdt: Zoned = "2024-11-03[America/New_York]".parse()?;
5567
    /// let hours = span.total((Unit::Hour, &zdt))?;
5568
    /// assert_eq!(hours, 25.0);
5569
    ///
5570
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5571
    /// ```
5572
    ///
5573
    /// Similar behavior applies to the other APIs listed above.
5574
    ///
5575
    /// # Example: different interpretations of "1 week"
5576
    ///
5577
    /// This example shows how "1 week" can be interpreted differently via the
5578
    /// [`Span::total`] API:
5579
    ///
5580
    /// ```
5581
    /// use jiff::{SpanRelativeTo, ToSpan, Unit, Zoned};
5582
    ///
5583
    /// let span = 1.week();
5584
    ///
5585
    /// // An error because days aren't always 24 hours:
5586
    /// assert_eq!(
5587
    ///     span.total(Unit::Hour).unwrap_err().to_string(),
5588
    ///     "using unit 'week' in a span or configuration requires that either \
5589
    ///      a relative reference time be given or \
5590
    ///      `SpanRelativeTo::days_are_24_hours()` is used to indicate \
5591
    ///      invariant 24-hour days, but neither were provided",
5592
    /// );
5593
    /// // Opt into invariant 24 hour days without a relative date:
5594
    /// let marker = SpanRelativeTo::days_are_24_hours();
5595
    /// let hours = span.total((Unit::Hour, marker))?;
5596
    /// assert_eq!(hours, 168.0);
5597
    /// // Weeks can be shorter than 24*7 hours:
5598
    /// let zdt: Zoned = "2024-03-10[America/New_York]".parse()?;
5599
    /// let hours = span.total((Unit::Hour, &zdt))?;
5600
    /// assert_eq!(hours, 167.0);
5601
    /// // Weeks can be longer than 24*7 hours:
5602
    /// let zdt: Zoned = "2024-11-03[America/New_York]".parse()?;
5603
    /// let hours = span.total((Unit::Hour, &zdt))?;
5604
    /// assert_eq!(hours, 169.0);
5605
    ///
5606
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5607
    /// ```
5608
    ///
5609
    /// # Example: working with [`civil::Date`](crate::civil::Date)
5610
    ///
5611
    /// A `Span` returned by computing the difference in time between two
5612
    /// [`civil::Date`](crate::civil::Date)s will have a non-zero number of
5613
    /// days. In older versions of Jiff, if one wanted to add spans returned by
5614
    /// these APIs, you could do so without futzing with relative dates. But
5615
    /// now you either need to provide a relative date:
5616
    ///
5617
    /// ```
5618
    /// use jiff::{civil::date, ToSpan};
5619
    ///
5620
    /// let d1 = date(2025, 1, 18);
5621
    /// let d2 = date(2025, 1, 26);
5622
    /// let d3 = date(2025, 2, 14);
5623
    ///
5624
    /// let span1 = d2 - d1;
5625
    /// let span2 = d3 - d2;
5626
    /// let total = span1.checked_add((span2, d1))?;
5627
    /// assert_eq!(total, 27.days().fieldwise());
5628
    ///
5629
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5630
    /// ```
5631
    ///
5632
    /// Or you can provide a marker indicating that days are always 24 hours.
5633
    /// This is fine for this use case since one is only doing civil calendar
5634
    /// arithmetic and not working with time zones:
5635
    ///
5636
    /// ```
5637
    /// use jiff::{civil::date, SpanRelativeTo, ToSpan};
5638
    ///
5639
    /// let d1 = date(2025, 1, 18);
5640
    /// let d2 = date(2025, 1, 26);
5641
    /// let d3 = date(2025, 2, 14);
5642
    ///
5643
    /// let span1 = d2 - d1;
5644
    /// let span2 = d3 - d2;
5645
    /// let total = span1.checked_add(
5646
    ///     (span2, SpanRelativeTo::days_are_24_hours()),
5647
    /// )?;
5648
    /// assert_eq!(total, 27.days().fieldwise());
5649
    ///
5650
    /// # Ok::<(), Box<dyn std::error::Error>>(())
5651
    /// ```
5652
    ///
5653
    /// [Issue #48]: https://github.com/BurntSushi/jiff/issues/48
5654
    #[inline]
5655
0
    pub const fn days_are_24_hours() -> SpanRelativeTo<'static> {
5656
0
        let kind = SpanRelativeToKind::DaysAre24Hours;
5657
0
        SpanRelativeTo { kind }
5658
0
    }
5659
5660
    /// Converts this public API relative datetime into a more versatile
5661
    /// internal representation of the same concept.
5662
    ///
5663
    /// Basically, the internal `Relative` type is `Cow` which means it isn't
5664
    /// `Copy`. But it can present a more uniform API. The public API type
5665
    /// doesn't have `Cow` so that it can be `Copy`.
5666
    ///
5667
    /// We also take this opportunity to attach some convenient data, such
5668
    /// as a timestamp when the relative datetime type is civil.
5669
    ///
5670
    /// This can return `None` if this `SpanRelativeTo` isn't actually a
5671
    /// datetime but a "marker" indicating some unit (like days) should be
5672
    /// treated as invariant. Or `None` is returned when the given unit is
5673
    /// always invariant (hours or smaller).
5674
    ///
5675
    /// # Errors
5676
    ///
5677
    /// If there was a problem doing this conversion, then an error is
5678
    /// returned. In practice, this only occurs for a civil datetime near the
5679
    /// civil datetime minimum and maximum values.
5680
0
    fn to_relative(&self, unit: Unit) -> Result<Option<Relative<'a>>, Error> {
5681
0
        if !unit.is_variable() {
5682
0
            return Ok(None);
5683
0
        }
5684
0
        match self.kind {
5685
0
            SpanRelativeToKind::Civil(dt) => {
5686
0
                Ok(Some(Relative::Civil(RelativeCivil::new(dt)?)))
5687
            }
5688
0
            SpanRelativeToKind::Zoned(zdt) => {
5689
0
                Ok(Some(Relative::Zoned(RelativeZoned {
5690
0
                    zoned: DumbCow::Borrowed(zdt),
5691
0
                })))
5692
            }
5693
            SpanRelativeToKind::DaysAre24Hours => {
5694
0
                if matches!(unit, Unit::Year | Unit::Month) {
5695
0
                    return Err(err!(
5696
0
                        "using unit '{unit}' in span or configuration \
5697
0
                         requires that a relative reference time be given \
5698
0
                         (`SpanRelativeTo::days_are_24_hours()` was given \
5699
0
                         but this only permits using days and weeks \
5700
0
                         without a relative reference time)",
5701
0
                        unit = unit.singular(),
5702
0
                    ));
5703
0
                }
5704
0
                Ok(None)
5705
            }
5706
        }
5707
0
    }
5708
}
5709
5710
#[derive(Clone, Copy, Debug)]
5711
enum SpanRelativeToKind<'a> {
5712
    Civil(DateTime),
5713
    Zoned(&'a Zoned),
5714
    DaysAre24Hours,
5715
}
5716
5717
impl<'a> From<&'a Zoned> for SpanRelativeTo<'a> {
5718
0
    fn from(zdt: &'a Zoned) -> SpanRelativeTo<'a> {
5719
0
        SpanRelativeTo { kind: SpanRelativeToKind::Zoned(zdt) }
5720
0
    }
5721
}
5722
5723
impl From<DateTime> for SpanRelativeTo<'static> {
5724
0
    fn from(dt: DateTime) -> SpanRelativeTo<'static> {
5725
0
        SpanRelativeTo { kind: SpanRelativeToKind::Civil(dt) }
5726
0
    }
5727
}
5728
5729
impl From<Date> for SpanRelativeTo<'static> {
5730
0
    fn from(date: Date) -> SpanRelativeTo<'static> {
5731
0
        let dt = DateTime::from_parts(date, Time::midnight());
5732
0
        SpanRelativeTo { kind: SpanRelativeToKind::Civil(dt) }
5733
0
    }
5734
}
5735
5736
/// A bit set that keeps track of all non-zero units on a `Span`.
5737
///
5738
/// Because of alignment, adding this to a `Span` does not make it any bigger.
5739
///
5740
/// The benefit of this bit set is to make it extremely cheap to enable fast
5741
/// paths in various places. For example, doing arithmetic on a `Date` with an
5742
/// arbitrary `Span` is pretty involved. But if you know the `Span` only
5743
/// consists of non-zero units of days (and zero for all other units), then you
5744
/// can take a much cheaper path.
5745
#[derive(Clone, Copy)]
5746
pub(crate) struct UnitSet(u16);
5747
5748
impl UnitSet {
5749
    /// Return a bit set representing all units as zero.
5750
    #[inline]
5751
5.20k
    fn empty() -> UnitSet {
5752
5.20k
        UnitSet(0)
5753
5.20k
    }
5754
5755
    /// Set the given `unit` to `is_zero` status in this set.
5756
    ///
5757
    /// When `is_zero` is false, the unit is added to this set. Otherwise,
5758
    /// the unit is removed from this set.
5759
    #[inline]
5760
3.38k
    fn set(self, unit: Unit, is_zero: bool) -> UnitSet {
5761
3.38k
        let bit = 1 << unit as usize;
5762
3.38k
        if is_zero {
5763
118
            UnitSet(self.0 & !bit)
5764
        } else {
5765
3.26k
            UnitSet(self.0 | bit)
5766
        }
5767
3.38k
    }
5768
5769
    /// Returns true if and only if no units are in this set.
5770
    #[inline]
5771
1.49k
    pub(crate) fn is_empty(&self) -> bool {
5772
1.49k
        self.0 == 0
5773
1.49k
    }
5774
5775
    /// Returns true if and only if this `Span` contains precisely one
5776
    /// non-zero unit corresponding to the unit given.
5777
    #[inline]
5778
629
    pub(crate) fn contains_only(self, unit: Unit) -> bool {
5779
629
        self.0 == (1 << unit as usize)
5780
629
    }
5781
5782
    /// Returns this set, but with only calendar units.
5783
    #[inline]
5784
3.90k
    pub(crate) fn only_calendar(self) -> UnitSet {
5785
3.90k
        UnitSet(self.0 & 0b0000_0011_1100_0000)
5786
3.90k
    }
5787
5788
    /// Returns this set, but with only time units.
5789
    #[inline]
5790
1.49k
    pub(crate) fn only_time(self) -> UnitSet {
5791
1.49k
        UnitSet(self.0 & 0b0000_0000_0011_1111)
5792
1.49k
    }
5793
5794
    /// Returns the largest unit in this set, or `None` if none are present.
5795
    #[inline]
5796
1.49k
    pub(crate) fn largest_unit(self) -> Option<Unit> {
5797
1.49k
        let zeros = usize::try_from(self.0.leading_zeros()).ok()?;
5798
1.49k
        15usize.checked_sub(zeros).and_then(Unit::from_usize)
5799
1.49k
    }
5800
}
5801
5802
// N.B. This `Debug` impl isn't typically used.
5803
//
5804
// This is because the `Debug` impl for `Span` just emits itself in the
5805
// friendly duration format, which doesn't include internal representation
5806
// details like this set. It is included in `Span::debug`, but this isn't
5807
// part of the public crate API.
5808
impl core::fmt::Debug for UnitSet {
5809
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
5810
0
        write!(f, "{{")?;
5811
0
        let mut units = *self;
5812
0
        let mut i = 0;
5813
0
        while let Some(unit) = units.largest_unit() {
5814
0
            if i > 0 {
5815
0
                write!(f, ", ")?;
5816
0
            }
5817
0
            i += 1;
5818
0
            write!(f, "{}", unit.compact())?;
5819
0
            units = units.set(unit, false);
5820
        }
5821
0
        if i == 0 {
5822
0
            write!(f, "∅")?;
5823
0
        }
5824
0
        write!(f, "}}")
5825
0
    }
5826
}
5827
5828
/// An internal abstraction for managing a relative datetime for use in some
5829
/// `Span` APIs.
5830
///
5831
/// This is effectively the same as a `SpanRelativeTo`, but uses a `Cow<Zoned>`
5832
/// instead of a `&Zoned`. This makes it non-`Copy`, but allows us to craft a
5833
/// more uniform API. (i.e., `relative + span = relative` instead of `relative
5834
/// + span = owned_relative` or whatever.) Note that the `Copy` impl on
5835
/// `SpanRelativeTo` means it has to accept a `&Zoned`. It can't ever take a
5836
/// `Zoned` since it is non-Copy.
5837
///
5838
/// NOTE: Separately from above, I think it's plausible that this type could be
5839
/// designed a bit differently. Namely, something like this:
5840
///
5841
/// ```text
5842
/// struct Relative<'a> {
5843
///     tz: Option<&'a TimeZone>,
5844
///     dt: DateTime,
5845
///     ts: Timestamp,
5846
/// }
5847
/// ```
5848
///
5849
/// That is, we do zone aware stuff but without an actual `Zoned` type. But I
5850
/// think in order to make that work, we would need to expose most of the
5851
/// `Zoned` API as functions on its component types (DateTime, Timestamp and
5852
/// TimeZone). I think we are likely to want to do that for public API reasons,
5853
/// but I'd like to resist it since I think it will add a lot of complexity.
5854
/// Or maybe we need a `Unzoned` type that is `DateTime` and `Timestamp`, but
5855
/// requires passing the time zone in to each of its methods. That might work
5856
/// quite well, even if it was just an internal type.
5857
///
5858
/// Anyway, I'm not 100% sure the above would work, but I think it would. It
5859
/// would be nicer because everything would be `Copy` all the time. We'd never
5860
/// need a `Cow<TimeZone>` for example, because we never need to change or
5861
/// create a new time zone.
5862
#[derive(Clone, Debug)]
5863
enum Relative<'a> {
5864
    Civil(RelativeCivil),
5865
    Zoned(RelativeZoned<'a>),
5866
}
5867
5868
impl<'a> Relative<'a> {
5869
    /// Adds the given span to this relative datetime.
5870
    ///
5871
    /// This defers to either [`DateTime::checked_add`] or
5872
    /// [`Zoned::checked_add`], depending on the type of relative datetime.
5873
    ///
5874
    /// The `Relative` datetime returned is guaranteed to have the same
5875
    /// internal datetie type as `self`.
5876
    ///
5877
    /// # Errors
5878
    ///
5879
    /// This returns an error in the same cases as the underlying checked
5880
    /// arithmetic APIs. In general, this occurs when adding the given `span`
5881
    /// would result in overflow.
5882
0
    fn checked_add(&self, span: Span) -> Result<Relative, Error> {
5883
0
        match *self {
5884
0
            Relative::Civil(dt) => Ok(Relative::Civil(dt.checked_add(span)?)),
5885
0
            Relative::Zoned(ref zdt) => {
5886
0
                Ok(Relative::Zoned(zdt.checked_add(span)?))
5887
            }
5888
        }
5889
0
    }
5890
5891
0
    fn checked_add_duration(
5892
0
        &self,
5893
0
        duration: SignedDuration,
5894
0
    ) -> Result<Relative, Error> {
5895
0
        match *self {
5896
0
            Relative::Civil(dt) => {
5897
0
                Ok(Relative::Civil(dt.checked_add_duration(duration)?))
5898
            }
5899
0
            Relative::Zoned(ref zdt) => {
5900
0
                Ok(Relative::Zoned(zdt.checked_add_duration(duration)?))
5901
            }
5902
        }
5903
0
    }
5904
5905
    /// Returns the span of time from this relative datetime to the one given,
5906
    /// with units as large as `largest`.
5907
    ///
5908
    /// # Errors
5909
    ///
5910
    /// This returns an error in the same cases as when the underlying
5911
    /// [`DateTime::until`] or [`Zoned::until`] fail. Because this doesn't
5912
    /// set or expose any rounding configuration, this can generally only
5913
    /// occur when `largest` is `Unit::Nanosecond` and the span of time
5914
    /// between `self` and `other` is too big to represent as a 64-bit integer
5915
    /// nanosecond count.
5916
    ///
5917
    /// # Panics
5918
    ///
5919
    /// This panics if `self` and `other` are different internal datetime
5920
    /// types. For example, if `self` was a civil datetime and `other` were
5921
    /// a zoned datetime.
5922
0
    fn until(&self, largest: Unit, other: &Relative) -> Result<Span, Error> {
5923
0
        match (self, other) {
5924
0
            (&Relative::Civil(ref dt1), &Relative::Civil(ref dt2)) => {
5925
0
                dt1.until(largest, dt2)
5926
            }
5927
0
            (&Relative::Zoned(ref zdt1), &Relative::Zoned(ref zdt2)) => {
5928
0
                zdt1.until(largest, zdt2)
5929
            }
5930
            // This would be bad if `Relative` were a public API, but in
5931
            // practice, this case never occurs because we don't mixup our
5932
            // `Relative` datetime types.
5933
0
            _ => unreachable!(),
5934
        }
5935
0
    }
5936
5937
    /// Converts this relative datetime to a nanosecond in UTC time.
5938
    ///
5939
    /// # Errors
5940
    ///
5941
    /// If there was a problem doing this conversion, then an error is
5942
    /// returned. In practice, this only occurs for a civil datetime near the
5943
    /// civil datetime minimum and maximum values.
5944
0
    fn to_nanosecond(&self) -> NoUnits128 {
5945
0
        match *self {
5946
0
            Relative::Civil(dt) => dt.timestamp.as_nanosecond_ranged().rinto(),
5947
0
            Relative::Zoned(ref zdt) => {
5948
0
                zdt.zoned.timestamp().as_nanosecond_ranged().rinto()
5949
            }
5950
        }
5951
0
    }
5952
5953
    /// Create a balanced span of time relative to this datetime.
5954
    ///
5955
    /// The relative span returned has the same internal datetime type
5956
    /// (civil or zoned) as this relative datetime.
5957
    ///
5958
    /// # Errors
5959
    ///
5960
    /// This returns an error when the span in this range cannot be
5961
    /// represented. In general, this only occurs when asking for largest units
5962
    /// of `Unit::Nanosecond` *and* when the span is too big to fit into a
5963
    /// 64-bit nanosecond count.
5964
    ///
5965
    /// This can also return an error in other extreme cases, such as when
5966
    /// adding the given span to this relative datetime results in overflow,
5967
    /// or if this relative datetime is a civil datetime and it couldn't be
5968
    /// converted to a timestamp in UTC.
5969
0
    fn into_relative_span(
5970
0
        self,
5971
0
        largest: Unit,
5972
0
        span: Span,
5973
0
    ) -> Result<RelativeSpan<'a>, Error> {
5974
0
        let kind = match self {
5975
0
            Relative::Civil(start) => {
5976
0
                let end = start.checked_add(span)?;
5977
0
                RelativeSpanKind::Civil { start, end }
5978
            }
5979
0
            Relative::Zoned(start) => {
5980
0
                let end = start.checked_add(span)?;
5981
0
                RelativeSpanKind::Zoned { start, end }
5982
            }
5983
        };
5984
0
        let relspan = kind.into_relative_span(largest)?;
5985
0
        if span.get_sign_ranged() != C(0)
5986
0
            && relspan.span.get_sign_ranged() != C(0)
5987
0
            && span.get_sign_ranged() != relspan.span.get_sign_ranged()
5988
        {
5989
            // I haven't quite figured out when this case is hit. I think it's
5990
            // actually impossible right? Balancing a duration should not flip
5991
            // the sign.
5992
            //
5993
            // ref: https://github.com/fullcalendar/temporal-polyfill/blob/9e001042864394247181d1a5d591c18057ce32d2/packages/temporal-polyfill/src/internal/durationMath.ts#L236-L238
5994
0
            unreachable!(
5995
                "balanced span should have same sign as original span"
5996
            )
5997
0
        }
5998
0
        Ok(relspan)
5999
0
    }
6000
6001
    /// Rounds the given span using the given rounding configuration.
6002
0
    fn round(
6003
0
        self,
6004
0
        span: Span,
6005
0
        smallest: Unit,
6006
0
        largest: Unit,
6007
0
        increment: NoUnits128,
6008
0
        mode: RoundMode,
6009
0
    ) -> Result<Span, Error> {
6010
0
        let relspan = self.into_relative_span(largest, span)?;
6011
0
        if relspan.span.get_sign_ranged() == C(0) {
6012
0
            return Ok(relspan.span);
6013
0
        }
6014
0
        let nudge = match relspan.kind {
6015
0
            RelativeSpanKind::Civil { start, end } => {
6016
0
                if smallest > Unit::Day {
6017
0
                    Nudge::relative_calendar(
6018
0
                        relspan.span,
6019
0
                        &Relative::Civil(start),
6020
0
                        &Relative::Civil(end),
6021
0
                        smallest,
6022
0
                        increment,
6023
0
                        mode,
6024
0
                    )?
6025
                } else {
6026
0
                    let relative_end = end.timestamp.as_nanosecond_ranged();
6027
0
                    Nudge::relative_invariant(
6028
0
                        relspan.span,
6029
0
                        relative_end.rinto(),
6030
0
                        smallest,
6031
0
                        largest,
6032
0
                        increment,
6033
0
                        mode,
6034
0
                    )?
6035
                }
6036
            }
6037
0
            RelativeSpanKind::Zoned { ref start, ref end } => {
6038
0
                if smallest >= Unit::Day {
6039
0
                    Nudge::relative_calendar(
6040
0
                        relspan.span,
6041
0
                        &Relative::Zoned(start.borrowed()),
6042
0
                        &Relative::Zoned(end.borrowed()),
6043
0
                        smallest,
6044
0
                        increment,
6045
0
                        mode,
6046
0
                    )?
6047
0
                } else if largest >= Unit::Day {
6048
                    // This is a special case for zoned datetimes when rounding
6049
                    // could bleed into variable units.
6050
0
                    Nudge::relative_zoned_time(
6051
0
                        relspan.span,
6052
0
                        start,
6053
0
                        smallest,
6054
0
                        increment,
6055
0
                        mode,
6056
0
                    )?
6057
                } else {
6058
                    // Otherwise, rounding is the same as civil datetime.
6059
0
                    let relative_end =
6060
0
                        end.zoned.timestamp().as_nanosecond_ranged();
6061
0
                    Nudge::relative_invariant(
6062
0
                        relspan.span,
6063
0
                        relative_end.rinto(),
6064
0
                        smallest,
6065
0
                        largest,
6066
0
                        increment,
6067
0
                        mode,
6068
0
                    )?
6069
                }
6070
            }
6071
        };
6072
0
        nudge.bubble(&relspan, smallest, largest)
6073
0
    }
6074
}
6075
6076
/// A balanced span between a range of civil or zoned datetimes.
6077
///
6078
/// The span is always balanced up to a certain unit as given to
6079
/// `RelativeSpanKind::into_relative_span`.
6080
#[derive(Clone, Debug)]
6081
struct RelativeSpan<'a> {
6082
    span: Span,
6083
    kind: RelativeSpanKind<'a>,
6084
}
6085
6086
/// A civil or zoned datetime range of time.
6087
#[derive(Clone, Debug)]
6088
enum RelativeSpanKind<'a> {
6089
    Civil { start: RelativeCivil, end: RelativeCivil },
6090
    Zoned { start: RelativeZoned<'a>, end: RelativeZoned<'a> },
6091
}
6092
6093
impl<'a> RelativeSpanKind<'a> {
6094
    /// Create a balanced `RelativeSpan` from this range of time.
6095
    ///
6096
    /// # Errors
6097
    ///
6098
    /// This returns an error when the span in this range cannot be
6099
    /// represented. In general, this only occurs when asking for largest units
6100
    /// of `Unit::Nanosecond` *and* when the span is too big to fit into a
6101
    /// 64-bit nanosecond count.
6102
0
    fn into_relative_span(
6103
0
        self,
6104
0
        largest: Unit,
6105
0
    ) -> Result<RelativeSpan<'a>, Error> {
6106
0
        let span = match self {
6107
0
            RelativeSpanKind::Civil { ref start, ref end } => start
6108
0
                .datetime
6109
0
                .until((largest, end.datetime))
6110
0
                .with_context(|| {
6111
0
                    err!(
6112
0
                        "failed to get span between {start} and {end} \
6113
0
                         with largest unit as {unit}",
6114
                        start = start.datetime,
6115
                        end = end.datetime,
6116
0
                        unit = largest.plural(),
6117
                    )
6118
0
                })?,
6119
0
            RelativeSpanKind::Zoned { ref start, ref end } => start
6120
0
                .zoned
6121
0
                .until((largest, &*end.zoned))
6122
0
                .with_context(|| {
6123
0
                    err!(
6124
0
                        "failed to get span between {start} and {end} \
6125
0
                         with largest unit as {unit}",
6126
                        start = start.zoned,
6127
                        end = end.zoned,
6128
0
                        unit = largest.plural(),
6129
                    )
6130
0
                })?,
6131
        };
6132
0
        Ok(RelativeSpan { span, kind: self })
6133
0
    }
6134
}
6135
6136
/// A wrapper around a civil datetime and a timestamp corresponding to that
6137
/// civil datetime in UTC.
6138
///
6139
/// Haphazardly interpreting a civil datetime in UTC is an odd and *usually*
6140
/// incorrect thing to do. But the way we use it here is basically just to give
6141
/// it an "anchoring" point such that we can represent it using a single
6142
/// integer for rounding purposes. It is only used in a context *relative* to
6143
/// another civil datetime interpreted in UTC. In this fashion, the selection
6144
/// of UTC specifically doesn't really matter. We could use any time zone.
6145
/// (Although, it must be a time zone without any transitions, otherwise we
6146
/// could wind up with time zone aware results in a context where that would
6147
/// be unexpected since this is civil time.)
6148
#[derive(Clone, Copy, Debug)]
6149
struct RelativeCivil {
6150
    datetime: DateTime,
6151
    timestamp: Timestamp,
6152
}
6153
6154
impl RelativeCivil {
6155
    /// Creates a new relative wrapper around the given civil datetime.
6156
    ///
6157
    /// This wrapper bundles a timestamp for the given datetime by interpreting
6158
    /// it as being in UTC. This is an "odd" thing to do, but it's only used
6159
    /// in the context of determining the length of time between two civil
6160
    /// datetimes. So technically, any time zone without transitions could be
6161
    /// used.
6162
    ///
6163
    /// # Errors
6164
    ///
6165
    /// This returns an error if the datetime could not be converted to a
6166
    /// timestamp. This only occurs near the minimum and maximum civil datetime
6167
    /// values.
6168
0
    fn new(datetime: DateTime) -> Result<RelativeCivil, Error> {
6169
0
        let timestamp = datetime
6170
0
            .to_zoned(TimeZone::UTC)
6171
0
            .with_context(|| {
6172
0
                err!("failed to convert {datetime} to timestamp")
6173
0
            })?
6174
0
            .timestamp();
6175
0
        Ok(RelativeCivil { datetime, timestamp })
6176
0
    }
6177
6178
    /// Returns the result of [`DateTime::checked_add`].
6179
    ///
6180
    /// # Errors
6181
    ///
6182
    /// Returns an error in the same cases as `DateTime::checked_add`. That is,
6183
    /// when adding the span to this zoned datetime would overflow.
6184
    ///
6185
    /// This also returns an error if the resulting datetime could not be
6186
    /// converted to a timestamp in UTC. This only occurs near the minimum and
6187
    /// maximum datetime values.
6188
0
    fn checked_add(&self, span: Span) -> Result<RelativeCivil, Error> {
6189
0
        let datetime = self.datetime.checked_add(span).with_context(|| {
6190
0
            err!("failed to add {span} to {dt}", dt = self.datetime)
6191
0
        })?;
6192
0
        let timestamp = datetime
6193
0
            .to_zoned(TimeZone::UTC)
6194
0
            .with_context(|| {
6195
0
                err!("failed to convert {datetime} to timestamp")
6196
0
            })?
6197
0
            .timestamp();
6198
0
        Ok(RelativeCivil { datetime, timestamp })
6199
0
    }
6200
6201
    /// Returns the result of [`DateTime::checked_add`] with an absolute
6202
    /// duration.
6203
    ///
6204
    /// # Errors
6205
    ///
6206
    /// Returns an error in the same cases as `DateTime::checked_add`. That is,
6207
    /// when adding the span to this zoned datetime would overflow.
6208
    ///
6209
    /// This also returns an error if the resulting datetime could not be
6210
    /// converted to a timestamp in UTC. This only occurs near the minimum and
6211
    /// maximum datetime values.
6212
0
    fn checked_add_duration(
6213
0
        &self,
6214
0
        duration: SignedDuration,
6215
0
    ) -> Result<RelativeCivil, Error> {
6216
0
        let datetime =
6217
0
            self.datetime.checked_add(duration).with_context(|| {
6218
0
                err!("failed to add {duration:?} to {dt}", dt = self.datetime)
6219
0
            })?;
6220
0
        let timestamp = datetime
6221
0
            .to_zoned(TimeZone::UTC)
6222
0
            .with_context(|| {
6223
0
                err!("failed to convert {datetime} to timestamp")
6224
0
            })?
6225
0
            .timestamp();
6226
0
        Ok(RelativeCivil { datetime, timestamp })
6227
0
    }
6228
6229
    /// Returns the result of [`DateTime::until`].
6230
    ///
6231
    /// # Errors
6232
    ///
6233
    /// Returns an error in the same cases as `DateTime::until`. That is, when
6234
    /// the span for the given largest unit cannot be represented. This can
6235
    /// generally only happen when `largest` is `Unit::Nanosecond` and the span
6236
    /// cannot be represented as a 64-bit integer of nanoseconds.
6237
0
    fn until(
6238
0
        &self,
6239
0
        largest: Unit,
6240
0
        other: &RelativeCivil,
6241
0
    ) -> Result<Span, Error> {
6242
0
        self.datetime.until((largest, other.datetime)).with_context(|| {
6243
0
            err!(
6244
0
                "failed to get span between {dt1} and {dt2} \
6245
0
                 with largest unit as {unit}",
6246
0
                unit = largest.plural(),
6247
                dt1 = self.datetime,
6248
                dt2 = other.datetime,
6249
            )
6250
0
        })
6251
0
    }
6252
}
6253
6254
/// A simple wrapper around a possibly borrowed `Zoned`.
6255
#[derive(Clone, Debug)]
6256
struct RelativeZoned<'a> {
6257
    zoned: DumbCow<'a, Zoned>,
6258
}
6259
6260
impl<'a> RelativeZoned<'a> {
6261
    /// Returns the result of [`Zoned::checked_add`].
6262
    ///
6263
    /// # Errors
6264
    ///
6265
    /// Returns an error in the same cases as `Zoned::checked_add`. That is,
6266
    /// when adding the span to this zoned datetime would overflow.
6267
0
    fn checked_add(
6268
0
        &self,
6269
0
        span: Span,
6270
0
    ) -> Result<RelativeZoned<'static>, Error> {
6271
0
        let zoned = self.zoned.checked_add(span).with_context(|| {
6272
0
            err!("failed to add {span} to {zoned}", zoned = self.zoned)
6273
0
        })?;
6274
0
        Ok(RelativeZoned { zoned: DumbCow::Owned(zoned) })
6275
0
    }
6276
6277
    /// Returns the result of [`Zoned::checked_add`] with an absolute duration.
6278
    ///
6279
    /// # Errors
6280
    ///
6281
    /// Returns an error in the same cases as `Zoned::checked_add`. That is,
6282
    /// when adding the span to this zoned datetime would overflow.
6283
0
    fn checked_add_duration(
6284
0
        &self,
6285
0
        duration: SignedDuration,
6286
0
    ) -> Result<RelativeZoned<'static>, Error> {
6287
0
        let zoned = self.zoned.checked_add(duration).with_context(|| {
6288
0
            err!("failed to add {duration:?} to {zoned}", zoned = self.zoned)
6289
0
        })?;
6290
0
        Ok(RelativeZoned { zoned: DumbCow::Owned(zoned) })
6291
0
    }
6292
6293
    /// Returns the result of [`Zoned::until`].
6294
    ///
6295
    /// # Errors
6296
    ///
6297
    /// Returns an error in the same cases as `Zoned::until`. That is, when
6298
    /// the span for the given largest unit cannot be represented. This can
6299
    /// generally only happen when `largest` is `Unit::Nanosecond` and the span
6300
    /// cannot be represented as a 64-bit integer of nanoseconds.
6301
0
    fn until(
6302
0
        &self,
6303
0
        largest: Unit,
6304
0
        other: &RelativeZoned<'a>,
6305
0
    ) -> Result<Span, Error> {
6306
0
        self.zoned.until((largest, &*other.zoned)).with_context(|| {
6307
0
            err!(
6308
0
                "failed to get span between {zdt1} and {zdt2} \
6309
0
                 with largest unit as {unit}",
6310
0
                unit = largest.plural(),
6311
                zdt1 = self.zoned,
6312
                zdt2 = other.zoned,
6313
            )
6314
0
        })
6315
0
    }
6316
6317
    /// Returns the borrowed version of self; useful when you need to convert
6318
    /// `&RelativeZoned` into `RelativeZoned` without cloning anything.
6319
0
    fn borrowed(&self) -> RelativeZoned {
6320
0
        RelativeZoned { zoned: self.zoned.borrowed() }
6321
0
    }
6322
}
6323
6324
// The code below is the "core" rounding logic for spans. It was greatly
6325
// inspired by this gist[1] and the fullcalendar Temporal polyfill[2]. In
6326
// particular, the algorithm implemented below is a major simplification from
6327
// how Temporal used to work[3]. Parts of it are still in rough and unclear
6328
// shape IMO.
6329
//
6330
// [1]: https://gist.github.com/arshaw/36d3152c21482bcb78ea2c69591b20e0
6331
// [2]: https://github.com/fullcalendar/temporal-polyfill
6332
// [3]: https://github.com/tc39/proposal-temporal/issues/2792
6333
6334
/// The result of a span rounding strategy. There are three:
6335
///
6336
/// * Rounding spans relative to civil datetimes using only invariant
6337
/// units (days or less). This is achieved by converting the span to a simple
6338
/// integer number of nanoseconds and then rounding that.
6339
/// * Rounding spans relative to either a civil datetime or a zoned datetime
6340
/// where rounding might involve changing non-uniform units. That is, when
6341
/// the smallest unit is greater than days for civil datetimes and greater
6342
/// than hours for zoned datetimes.
6343
/// * Rounding spans relative to a zoned datetime whose smallest unit is
6344
/// less than days.
6345
///
6346
/// Each of these might produce a bottom heavy span that needs to be
6347
/// re-balanced. This type represents that result via one of three constructors
6348
/// corresponding to each of the above strategies, and then provides a routine
6349
/// for rebalancing via "bubbling."
6350
#[derive(Debug)]
6351
struct Nudge {
6352
    /// A possibly bottom heavy rounded span.
6353
    span: Span,
6354
    /// The nanosecond timestamp corresponding to `relative + span`, where
6355
    /// `span` is the (possibly bottom heavy) rounded span.
6356
    rounded_relative_end: NoUnits128,
6357
    /// Whether rounding may have created a bottom heavy span such that a
6358
    /// calendar unit might need to be incremented after re-balancing smaller
6359
    /// units.
6360
    grew_big_unit: bool,
6361
}
6362
6363
impl Nudge {
6364
    /// Performs rounding on the given span limited to invariant units.
6365
    ///
6366
    /// For civil datetimes, this means the smallest unit must be days or less,
6367
    /// but the largest unit can be bigger. For zoned datetimes, this means
6368
    /// that *both* the largest and smallest unit must be hours or less. This
6369
    /// is because zoned datetimes with rounding that can spill up to days
6370
    /// requires special handling.
6371
    ///
6372
    /// It works by converting the span to a single integer number of
6373
    /// nanoseconds, rounding it and then converting back to a span.
6374
0
    fn relative_invariant(
6375
0
        balanced: Span,
6376
0
        relative_end: NoUnits128,
6377
0
        smallest: Unit,
6378
0
        largest: Unit,
6379
0
        increment: NoUnits128,
6380
0
        mode: RoundMode,
6381
0
    ) -> Result<Nudge, Error> {
6382
        // Ensures this is only called when rounding invariant units.
6383
0
        assert!(smallest <= Unit::Week);
6384
6385
0
        let sign = balanced.get_sign_ranged();
6386
0
        let balanced_nanos = balanced.to_invariant_nanoseconds();
6387
0
        let rounded_nanos = mode.round_by_unit_in_nanoseconds(
6388
0
            balanced_nanos,
6389
0
            smallest,
6390
0
            increment,
6391
        );
6392
0
        let span = Span::from_invariant_nanoseconds(largest, rounded_nanos)
6393
0
            .with_context(|| {
6394
0
                err!(
6395
0
                    "failed to convert rounded nanoseconds {rounded_nanos} \
6396
0
                     to span for largest unit as {unit}",
6397
0
                    unit = largest.plural(),
6398
                )
6399
0
            })?
6400
0
            .years_ranged(balanced.get_years_ranged())
6401
0
            .months_ranged(balanced.get_months_ranged())
6402
0
            .weeks_ranged(balanced.get_weeks_ranged());
6403
6404
0
        let diff_nanos = rounded_nanos - balanced_nanos;
6405
0
        let diff_days = rounded_nanos.div_ceil(t::NANOS_PER_CIVIL_DAY)
6406
0
            - balanced_nanos.div_ceil(t::NANOS_PER_CIVIL_DAY);
6407
0
        let grew_big_unit = diff_days.signum() == sign;
6408
0
        let rounded_relative_end = relative_end + diff_nanos;
6409
0
        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6410
0
    }
6411
6412
    /// Performs rounding on the given span where the smallest unit configured
6413
    /// implies that rounding will cover calendar or "non-uniform" units. (That
6414
    /// is, units whose length can change based on the relative datetime.)
6415
0
    fn relative_calendar(
6416
0
        balanced: Span,
6417
0
        relative_start: &Relative<'_>,
6418
0
        relative_end: &Relative<'_>,
6419
0
        smallest: Unit,
6420
0
        increment: NoUnits128,
6421
0
        mode: RoundMode,
6422
0
    ) -> Result<Nudge, Error> {
6423
        #[cfg(not(feature = "std"))]
6424
        use crate::util::libm::Float;
6425
6426
0
        assert!(smallest >= Unit::Day);
6427
0
        let sign = balanced.get_sign_ranged();
6428
0
        let truncated = increment
6429
0
            * balanced.get_units_ranged(smallest).div_ceil(increment);
6430
0
        let span = balanced
6431
0
            .without_lower(smallest)
6432
0
            .try_units_ranged(smallest, truncated.rinto())
6433
0
            .with_context(|| {
6434
0
                err!(
6435
0
                    "failed to set {unit} to {truncated} on span {balanced}",
6436
0
                    unit = smallest.singular()
6437
                )
6438
0
            })?;
6439
0
        let (relative0, relative1) = clamp_relative_span(
6440
0
            relative_start,
6441
0
            span,
6442
0
            smallest,
6443
0
            NoUnits::try_rfrom("increment", increment)?
6444
0
                .try_checked_mul("signed increment", sign)?,
6445
0
        )?;
6446
6447
        // FIXME: This is brutal. This is the only non-optional floating point
6448
        // used so far in Jiff. We do expose floating point for things like
6449
        // `Span::total`, but that's optional and not a core part of Jiff's
6450
        // functionality. This is in the core part of Jiff's span rounding...
6451
0
        let denom = (relative1 - relative0).get() as f64;
6452
0
        let numer = (relative_end.to_nanosecond() - relative0).get() as f64;
6453
0
        let exact = (truncated.get() as f64)
6454
0
            + (numer / denom) * (sign.get() as f64) * (increment.get() as f64);
6455
0
        let rounded = mode.round_float(exact, increment);
6456
0
        let grew_big_unit =
6457
0
            ((rounded.get() as f64) - exact).signum() == (sign.get() as f64);
6458
6459
0
        let span = span
6460
0
            .try_units_ranged(smallest, rounded.rinto())
6461
0
            .with_context(|| {
6462
0
                err!(
6463
0
                    "failed to set {unit} to {truncated} on span {span}",
6464
0
                    unit = smallest.singular()
6465
                )
6466
0
            })?;
6467
0
        let rounded_relative_end =
6468
0
            if grew_big_unit { relative1 } else { relative0 };
6469
0
        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6470
0
    }
6471
6472
    /// Performs rounding on the given span where the smallest unit is hours
6473
    /// or less *and* the relative datetime is time zone aware.
6474
0
    fn relative_zoned_time(
6475
0
        balanced: Span,
6476
0
        relative_start: &RelativeZoned<'_>,
6477
0
        smallest: Unit,
6478
0
        increment: NoUnits128,
6479
0
        mode: RoundMode,
6480
0
    ) -> Result<Nudge, Error> {
6481
0
        let sign = balanced.get_sign_ranged();
6482
0
        let time_nanos =
6483
0
            balanced.only_lower(Unit::Day).to_invariant_nanoseconds();
6484
0
        let mut rounded_time_nanos =
6485
0
            mode.round_by_unit_in_nanoseconds(time_nanos, smallest, increment);
6486
0
        let (relative0, relative1) = clamp_relative_span(
6487
            // FIXME: Find a way to drop this clone.
6488
0
            &Relative::Zoned(relative_start.clone()),
6489
0
            balanced.without_lower(Unit::Day),
6490
0
            Unit::Day,
6491
0
            sign.rinto(),
6492
0
        )?;
6493
0
        let day_nanos = relative1 - relative0;
6494
0
        let beyond_day_nanos = rounded_time_nanos - day_nanos;
6495
6496
0
        let mut day_delta = NoUnits::N::<0>();
6497
0
        let rounded_relative_end =
6498
0
            if beyond_day_nanos == C(0) || beyond_day_nanos.signum() == sign {
6499
0
                day_delta += C(1);
6500
0
                rounded_time_nanos = mode.round_by_unit_in_nanoseconds(
6501
0
                    beyond_day_nanos,
6502
0
                    smallest,
6503
0
                    increment,
6504
0
                );
6505
0
                relative1 + rounded_time_nanos
6506
            } else {
6507
0
                relative0 + rounded_time_nanos
6508
            };
6509
6510
0
        let span =
6511
0
            Span::from_invariant_nanoseconds(Unit::Hour, rounded_time_nanos)
6512
0
                .with_context(|| {
6513
0
                    err!(
6514
0
                        "failed to convert rounded nanoseconds \
6515
0
                     {rounded_time_nanos} to span for largest unit as {unit}",
6516
0
                        unit = Unit::Hour.plural(),
6517
                    )
6518
0
                })?
6519
0
                .years_ranged(balanced.get_years_ranged())
6520
0
                .months_ranged(balanced.get_months_ranged())
6521
0
                .weeks_ranged(balanced.get_weeks_ranged())
6522
0
                .days_ranged(balanced.get_days_ranged() + day_delta);
6523
0
        let grew_big_unit = day_delta != C(0);
6524
0
        Ok(Nudge { span, rounded_relative_end, grew_big_unit })
6525
0
    }
6526
6527
    /// This "bubbles" up the units in a potentially "bottom heavy" span to
6528
    /// larger units. For example, P1m50d relative to March 1 is bottom heavy.
6529
    /// This routine will bubble the days up to months to get P2m19d.
6530
    ///
6531
    /// # Errors
6532
    ///
6533
    /// This routine fails if any arithmetic on the individual units fails, or
6534
    /// when span arithmetic on the relative datetime given fails.
6535
0
    fn bubble(
6536
0
        &self,
6537
0
        relative: &RelativeSpan,
6538
0
        smallest: Unit,
6539
0
        largest: Unit,
6540
0
    ) -> Result<Span, Error> {
6541
0
        if !self.grew_big_unit || smallest == Unit::Week {
6542
0
            return Ok(self.span);
6543
0
        }
6544
6545
0
        let smallest = smallest.max(Unit::Day);
6546
0
        let mut balanced = self.span;
6547
0
        let sign = balanced.get_sign_ranged();
6548
0
        let mut unit = smallest;
6549
0
        while let Some(u) = unit.next() {
6550
0
            unit = u;
6551
0
            if unit > largest {
6552
0
                break;
6553
0
            }
6554
            // We only bubble smaller units up into weeks when the largest unit
6555
            // is explicitly set to weeks. Otherwise, we leave it as-is.
6556
0
            if unit == Unit::Week && largest != Unit::Week {
6557
0
                continue;
6558
0
            }
6559
6560
0
            let span_start = balanced.without_lower(unit);
6561
0
            let new_units = span_start
6562
0
                .get_units_ranged(unit)
6563
0
                .try_checked_add("bubble-units", sign)
6564
0
                .with_context(|| {
6565
0
                    err!(
6566
0
                        "failed to add sign {sign} to {unit} value {value}",
6567
0
                        unit = unit.plural(),
6568
0
                        value = span_start.get_units_ranged(unit),
6569
                    )
6570
0
                })?;
6571
0
            let span_end = span_start
6572
0
                .try_units_ranged(unit, new_units)
6573
0
                .with_context(|| {
6574
0
                    err!(
6575
0
                        "failed to set {unit} to value \
6576
0
                         {new_units} on span {span_start}",
6577
0
                        unit = unit.plural(),
6578
                    )
6579
0
                })?;
6580
0
            let threshold = match relative.kind {
6581
0
                RelativeSpanKind::Civil { ref start, .. } => {
6582
0
                    start.checked_add(span_end)?.timestamp
6583
                }
6584
0
                RelativeSpanKind::Zoned { ref start, .. } => {
6585
0
                    start.checked_add(span_end)?.zoned.timestamp()
6586
                }
6587
            };
6588
0
            let beyond =
6589
0
                self.rounded_relative_end - threshold.as_nanosecond_ranged();
6590
0
            if beyond == C(0) || beyond.signum() == sign {
6591
0
                balanced = span_end;
6592
0
            } else {
6593
0
                break;
6594
            }
6595
        }
6596
0
        Ok(balanced)
6597
0
    }
6598
}
6599
6600
/// Rounds a span consisting of only invariant units.
6601
///
6602
/// This only applies when the max of the units in the span being rounded,
6603
/// the largest configured unit and the smallest configured unit are all
6604
/// invariant. That is, days or lower for spans without a relative datetime or
6605
/// a relative civil datetime, and hours or lower for spans with a relative
6606
/// zoned datetime.
6607
///
6608
/// All we do here is convert the span to an integer number of nanoseconds,
6609
/// round that and then convert back. There aren't any tricky corner cases to
6610
/// consider here.
6611
0
fn round_span_invariant(
6612
0
    span: Span,
6613
0
    smallest: Unit,
6614
0
    largest: Unit,
6615
0
    increment: NoUnits128,
6616
0
    mode: RoundMode,
6617
0
) -> Result<Span, Error> {
6618
0
    assert!(smallest <= Unit::Week);
6619
0
    assert!(largest <= Unit::Week);
6620
0
    let nanos = span.to_invariant_nanoseconds();
6621
0
    let rounded =
6622
0
        mode.round_by_unit_in_nanoseconds(nanos, smallest, increment);
6623
0
    Span::from_invariant_nanoseconds(largest, rounded).with_context(|| {
6624
0
        err!(
6625
0
            "failed to convert rounded nanoseconds {rounded} \
6626
0
             to span for largest unit as {unit}",
6627
0
            unit = largest.plural(),
6628
        )
6629
0
    })
6630
0
}
6631
6632
/// Returns the nanosecond timestamps of `relative + span` and `relative +
6633
/// {amount of unit} + span`.
6634
///
6635
/// This is useful for determining the actual length, in nanoseconds, of some
6636
/// unit amount (usually a single unit). Usually, this is called with a span
6637
/// whose units lower than `unit` are zeroed out and with an `amount` that
6638
/// is `-1` or `1` or `0`. So for example, if `unit` were `Unit::Day`, then
6639
/// you'd get back two nanosecond timestamps relative to the relative datetime
6640
/// given that start exactly "one day" apart. (Which might be different than 24
6641
/// hours, depending on the time zone.)
6642
///
6643
/// # Errors
6644
///
6645
/// This returns an error if adding the units overflows, or if doing the span
6646
/// arithmetic on `relative` overflows.
6647
0
fn clamp_relative_span(
6648
0
    relative: &Relative<'_>,
6649
0
    span: Span,
6650
0
    unit: Unit,
6651
0
    amount: NoUnits,
6652
0
) -> Result<(NoUnits128, NoUnits128), Error> {
6653
0
    let amount = span
6654
0
        .get_units_ranged(unit)
6655
0
        .try_checked_add("clamp-units", amount)
6656
0
        .with_context(|| {
6657
0
            err!(
6658
0
                "failed to add {amount} to {unit} \
6659
0
                 value {value} on span {span}",
6660
0
                unit = unit.plural(),
6661
0
                value = span.get_units_ranged(unit),
6662
            )
6663
0
        })?;
6664
0
    let span_amount =
6665
0
        span.try_units_ranged(unit, amount).with_context(|| {
6666
0
            err!(
6667
0
                "failed to set {unit} unit to {amount} on span {span}",
6668
0
                unit = unit.plural(),
6669
            )
6670
0
        })?;
6671
0
    let relative0 = relative.checked_add(span)?.to_nanosecond();
6672
0
    let relative1 = relative.checked_add(span_amount)?.to_nanosecond();
6673
0
    Ok((relative0, relative1))
6674
0
}
6675
6676
/// A common parsing function that works in bytes.
6677
///
6678
/// Specifically, this parses either an ISO 8601 duration into a `Span` or
6679
/// a "friendly" duration into a `Span`. It also tries to give decent error
6680
/// messages.
6681
///
6682
/// This works because the friendly and ISO 8601 formats have non-overlapping
6683
/// prefixes. Both can start with a `+` or `-`, but aside from that, an ISO
6684
/// 8601 duration _always_ has to start with a `P` or `p`. We can utilize this
6685
/// property to very quickly determine how to parse the input. We just need to
6686
/// handle the possibly ambiguous case with a leading sign a little carefully
6687
/// in order to ensure good error messages.
6688
///
6689
/// (We do the same thing for `SignedDuration`.)
6690
#[cfg_attr(feature = "perf-inline", inline(always))]
6691
0
fn parse_iso_or_friendly(bytes: &[u8]) -> Result<Span, Error> {
6692
0
    if bytes.is_empty() {
6693
0
        return Err(err!(
6694
0
            "an empty string is not a valid `Span`, \
6695
0
             expected either a ISO 8601 or Jiff's 'friendly' \
6696
0
             format",
6697
0
        ));
6698
0
    }
6699
0
    let mut first = bytes[0];
6700
0
    if first == b'+' || first == b'-' {
6701
0
        if bytes.len() == 1 {
6702
0
            return Err(err!(
6703
0
                "found nothing after sign `{sign}`, \
6704
0
                 which is not a valid `Span`, \
6705
0
                 expected either a ISO 8601 or Jiff's 'friendly' \
6706
0
                 format",
6707
0
                sign = escape::Byte(first),
6708
0
            ));
6709
0
        }
6710
0
        first = bytes[1];
6711
0
    }
6712
0
    if first == b'P' || first == b'p' {
6713
0
        temporal::DEFAULT_SPAN_PARSER.parse_span(bytes)
6714
    } else {
6715
0
        friendly::DEFAULT_SPAN_PARSER.parse_span(bytes)
6716
    }
6717
0
}
6718
6719
0
fn requires_relative_date_err(unit: Unit) -> Result<(), Error> {
6720
0
    if unit.is_variable() {
6721
0
        return Err(if matches!(unit, Unit::Week | Unit::Day) {
6722
0
            err!(
6723
0
                "using unit '{unit}' in a span or configuration \
6724
0
                 requires that either a relative reference time be given \
6725
0
                 or `SpanRelativeTo::days_are_24_hours()` is used to \
6726
0
                 indicate invariant 24-hour days, \
6727
0
                 but neither were provided",
6728
0
                unit = unit.singular(),
6729
            )
6730
        } else {
6731
0
            err!(
6732
0
                "using unit '{unit}' in a span or configuration \
6733
0
                 requires that a relative reference time be given, \
6734
0
                 but none was provided",
6735
0
                unit = unit.singular(),
6736
            )
6737
        });
6738
0
    }
6739
0
    Ok(())
6740
0
}
6741
6742
#[cfg(test)]
6743
mod tests {
6744
    use std::io::Cursor;
6745
6746
    use alloc::string::ToString;
6747
6748
    use crate::{civil::date, RoundMode};
6749
6750
    use super::*;
6751
6752
    #[test]
6753
    fn test_total() {
6754
        if crate::tz::db().is_definitively_empty() {
6755
            return;
6756
        }
6757
6758
        let span = 130.hours().minutes(20);
6759
        let total = span.total(Unit::Second).unwrap();
6760
        assert_eq!(total, 469200.0);
6761
6762
        let span = 123456789.seconds();
6763
        let total = span
6764
            .total(SpanTotal::from(Unit::Day).days_are_24_hours())
6765
            .unwrap();
6766
        assert_eq!(total, 1428.8980208333332);
6767
6768
        let span = 2756.hours();
6769
        let dt = date(2020, 1, 1).at(0, 0, 0, 0);
6770
        let zdt = dt.in_tz("Europe/Rome").unwrap();
6771
        let total = span.total((Unit::Month, &zdt)).unwrap();
6772
        assert_eq!(total, 3.7958333333333334);
6773
        let total = span.total((Unit::Month, dt)).unwrap();
6774
        assert_eq!(total, 3.7944444444444443);
6775
    }
6776
6777
    #[test]
6778
    fn test_compare() {
6779
        if crate::tz::db().is_definitively_empty() {
6780
            return;
6781
        }
6782
6783
        let span1 = 79.hours().minutes(10);
6784
        let span2 = 79.hours().seconds(630);
6785
        let span3 = 78.hours().minutes(50);
6786
        let mut array = [span1, span2, span3];
6787
        array.sort_by(|sp1, sp2| sp1.compare(sp2).unwrap());
6788
        assert_eq!(array, [span3, span1, span2].map(SpanFieldwise));
6789
6790
        let day24 = SpanRelativeTo::days_are_24_hours();
6791
        let span1 = 79.hours().minutes(10);
6792
        let span2 = 3.days().hours(7).seconds(630);
6793
        let span3 = 3.days().hours(6).minutes(50);
6794
        let mut array = [span1, span2, span3];
6795
        array.sort_by(|sp1, sp2| sp1.compare((sp2, day24)).unwrap());
6796
        assert_eq!(array, [span3, span1, span2].map(SpanFieldwise));
6797
6798
        let dt = date(2020, 11, 1).at(0, 0, 0, 0);
6799
        let zdt = dt.in_tz("America/Los_Angeles").unwrap();
6800
        array.sort_by(|sp1, sp2| sp1.compare((sp2, &zdt)).unwrap());
6801
        assert_eq!(array, [span1, span3, span2].map(SpanFieldwise));
6802
    }
6803
6804
    #[test]
6805
    fn test_checked_add() {
6806
        let span1 = 1.hour();
6807
        let span2 = 30.minutes();
6808
        let sum = span1.checked_add(span2).unwrap();
6809
        span_eq!(sum, 1.hour().minutes(30));
6810
6811
        let span1 = 1.hour().minutes(30);
6812
        let span2 = 2.hours().minutes(45);
6813
        let sum = span1.checked_add(span2).unwrap();
6814
        span_eq!(sum, 4.hours().minutes(15));
6815
6816
        let span = 50
6817
            .years()
6818
            .months(50)
6819
            .days(50)
6820
            .hours(50)
6821
            .minutes(50)
6822
            .seconds(50)
6823
            .milliseconds(500)
6824
            .microseconds(500)
6825
            .nanoseconds(500);
6826
        let relative = date(1900, 1, 1).at(0, 0, 0, 0);
6827
        let sum = span.checked_add((span, relative)).unwrap();
6828
        let expected = 108
6829
            .years()
6830
            .months(7)
6831
            .days(12)
6832
            .hours(5)
6833
            .minutes(41)
6834
            .seconds(41)
6835
            .milliseconds(1)
6836
            .microseconds(1)
6837
            .nanoseconds(0);
6838
        span_eq!(sum, expected);
6839
6840
        let span = 1.month().days(15);
6841
        let relative = date(2000, 2, 1).at(0, 0, 0, 0);
6842
        let sum = span.checked_add((span, relative)).unwrap();
6843
        span_eq!(sum, 3.months());
6844
        let relative = date(2000, 3, 1).at(0, 0, 0, 0);
6845
        let sum = span.checked_add((span, relative)).unwrap();
6846
        span_eq!(sum, 2.months().days(30));
6847
    }
6848
6849
    #[test]
6850
    fn test_round_day_time() {
6851
        let span = 29.seconds();
6852
        let rounded = span.round(Unit::Minute).unwrap();
6853
        span_eq!(rounded, 0.minute());
6854
6855
        let span = 30.seconds();
6856
        let rounded = span.round(Unit::Minute).unwrap();
6857
        span_eq!(rounded, 1.minute());
6858
6859
        let span = 8.seconds();
6860
        let rounded = span
6861
            .round(
6862
                SpanRound::new()
6863
                    .smallest(Unit::Nanosecond)
6864
                    .largest(Unit::Microsecond),
6865
            )
6866
            .unwrap();
6867
        span_eq!(rounded, 8_000_000.microseconds());
6868
6869
        let span = 130.minutes();
6870
        let rounded = span
6871
            .round(SpanRound::new().largest(Unit::Day).days_are_24_hours())
6872
            .unwrap();
6873
        span_eq!(rounded, 2.hours().minutes(10));
6874
6875
        let span = 10.minutes().seconds(52);
6876
        let rounded = span.round(Unit::Minute).unwrap();
6877
        span_eq!(rounded, 11.minutes());
6878
6879
        let span = 10.minutes().seconds(52);
6880
        let rounded = span
6881
            .round(
6882
                SpanRound::new().smallest(Unit::Minute).mode(RoundMode::Trunc),
6883
            )
6884
            .unwrap();
6885
        span_eq!(rounded, 10.minutes());
6886
6887
        let span = 2.hours().minutes(34).seconds(18);
6888
        let rounded =
6889
            span.round(SpanRound::new().largest(Unit::Second)).unwrap();
6890
        span_eq!(rounded, 9258.seconds());
6891
6892
        let span = 6.minutes();
6893
        let rounded = span
6894
            .round(
6895
                SpanRound::new()
6896
                    .smallest(Unit::Minute)
6897
                    .increment(5)
6898
                    .mode(RoundMode::Ceil),
6899
            )
6900
            .unwrap();
6901
        span_eq!(rounded, 10.minutes());
6902
    }
6903
6904
    #[test]
6905
    fn test_round_relative_zoned_calendar() {
6906
        if crate::tz::db().is_definitively_empty() {
6907
            return;
6908
        }
6909
6910
        let span = 2756.hours();
6911
        let relative =
6912
            date(2020, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6913
        let options = SpanRound::new()
6914
            .largest(Unit::Year)
6915
            .smallest(Unit::Day)
6916
            .relative(&relative);
6917
        let rounded = span.round(options).unwrap();
6918
        span_eq!(rounded, 3.months().days(24));
6919
6920
        let span = 24.hours().nanoseconds(5);
6921
        let relative = date(2000, 10, 29)
6922
            .at(0, 0, 0, 0)
6923
            .in_tz("America/Vancouver")
6924
            .unwrap();
6925
        let options = SpanRound::new()
6926
            .largest(Unit::Day)
6927
            .smallest(Unit::Minute)
6928
            .relative(&relative)
6929
            .mode(RoundMode::Expand)
6930
            .increment(30);
6931
        let rounded = span.round(options).unwrap();
6932
        // It seems like this is the correct answer, although it apparently
6933
        // differs from Temporal and the FullCalendar polyfill. I'm not sure
6934
        // what accounts for the difference in the implementation.
6935
        //
6936
        // See: https://github.com/tc39/proposal-temporal/pull/2758#discussion_r1597255245
6937
        span_eq!(rounded, 24.hours().minutes(30));
6938
6939
        // Ref: https://github.com/tc39/proposal-temporal/issues/2816#issuecomment-2115608460
6940
        let span = -1.month().hours(24);
6941
        let relative: crate::Zoned = date(2024, 4, 11)
6942
            .at(2, 0, 0, 0)
6943
            .in_tz("America/New_York")
6944
            .unwrap();
6945
        let options =
6946
            SpanRound::new().smallest(Unit::Millisecond).relative(&relative);
6947
        let rounded = span.round(options).unwrap();
6948
        span_eq!(rounded, -1.month().days(1).hours(1));
6949
        let dt = relative.checked_add(span).unwrap();
6950
        let diff = relative.until((Unit::Month, &dt)).unwrap();
6951
        span_eq!(diff, -1.month().days(1).hours(1));
6952
6953
        // Like the above, but don't use a datetime near a DST transition. In
6954
        // this case, a day is a normal 24 hours. (Unlike above, where the
6955
        // duration includes a 23 hour day, and so an additional hour has to be
6956
        // added to the span to account for that.)
6957
        let span = -1.month().hours(24);
6958
        let relative = date(2024, 6, 11)
6959
            .at(2, 0, 0, 0)
6960
            .in_tz("America/New_York")
6961
            .unwrap();
6962
        let options =
6963
            SpanRound::new().smallest(Unit::Millisecond).relative(&relative);
6964
        let rounded = span.round(options).unwrap();
6965
        span_eq!(rounded, -1.month().days(1));
6966
    }
6967
6968
    #[test]
6969
    fn test_round_relative_zoned_time() {
6970
        if crate::tz::db().is_definitively_empty() {
6971
            return;
6972
        }
6973
6974
        let span = 2756.hours();
6975
        let relative =
6976
            date(2020, 1, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6977
        let options = SpanRound::new().largest(Unit::Year).relative(&relative);
6978
        let rounded = span.round(options).unwrap();
6979
        span_eq!(rounded, 3.months().days(23).hours(21));
6980
6981
        let span = 2756.hours();
6982
        let relative =
6983
            date(2020, 9, 1).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6984
        let options = SpanRound::new().largest(Unit::Year).relative(&relative);
6985
        let rounded = span.round(options).unwrap();
6986
        span_eq!(rounded, 3.months().days(23).hours(19));
6987
6988
        let span = 3.hours();
6989
        let relative =
6990
            date(2020, 3, 8).at(0, 0, 0, 0).in_tz("America/New_York").unwrap();
6991
        let options = SpanRound::new().largest(Unit::Year).relative(&relative);
6992
        let rounded = span.round(options).unwrap();
6993
        span_eq!(rounded, 3.hours());
6994
    }
6995
6996
    #[test]
6997
    fn test_round_relative_day_time() {
6998
        let span = 2756.hours();
6999
        let options =
7000
            SpanRound::new().largest(Unit::Year).relative(date(2020, 1, 1));
7001
        let rounded = span.round(options).unwrap();
7002
        span_eq!(rounded, 3.months().days(23).hours(20));
7003
7004
        let span = 2756.hours();
7005
        let options =
7006
            SpanRound::new().largest(Unit::Year).relative(date(2020, 9, 1));
7007
        let rounded = span.round(options).unwrap();
7008
        span_eq!(rounded, 3.months().days(23).hours(20));
7009
7010
        let span = 190.days();
7011
        let options =
7012
            SpanRound::new().largest(Unit::Year).relative(date(2020, 1, 1));
7013
        let rounded = span.round(options).unwrap();
7014
        span_eq!(rounded, 6.months().days(8));
7015
7016
        let span = 30
7017
            .days()
7018
            .hours(23)
7019
            .minutes(59)
7020
            .seconds(59)
7021
            .milliseconds(999)
7022
            .microseconds(999)
7023
            .nanoseconds(999);
7024
        let options = SpanRound::new()
7025
            .smallest(Unit::Microsecond)
7026
            .largest(Unit::Year)
7027
            .relative(date(2024, 5, 1));
7028
        let rounded = span.round(options).unwrap();
7029
        span_eq!(rounded, 1.month());
7030
7031
        let span = 364
7032
            .days()
7033
            .hours(23)
7034
            .minutes(59)
7035
            .seconds(59)
7036
            .milliseconds(999)
7037
            .microseconds(999)
7038
            .nanoseconds(999);
7039
        let options = SpanRound::new()
7040
            .smallest(Unit::Microsecond)
7041
            .largest(Unit::Year)
7042
            .relative(date(2023, 1, 1));
7043
        let rounded = span.round(options).unwrap();
7044
        span_eq!(rounded, 1.year());
7045
7046
        let span = 365
7047
            .days()
7048
            .hours(23)
7049
            .minutes(59)
7050
            .seconds(59)
7051
            .milliseconds(999)
7052
            .microseconds(999)
7053
            .nanoseconds(999);
7054
        let options = SpanRound::new()
7055
            .smallest(Unit::Microsecond)
7056
            .largest(Unit::Year)
7057
            .relative(date(2023, 1, 1));
7058
        let rounded = span.round(options).unwrap();
7059
        span_eq!(rounded, 1.year().days(1));
7060
7061
        let span = 365
7062
            .days()
7063
            .hours(23)
7064
            .minutes(59)
7065
            .seconds(59)
7066
            .milliseconds(999)
7067
            .microseconds(999)
7068
            .nanoseconds(999);
7069
        let options = SpanRound::new()
7070
            .smallest(Unit::Microsecond)
7071
            .largest(Unit::Year)
7072
            .relative(date(2024, 1, 1));
7073
        let rounded = span.round(options).unwrap();
7074
        span_eq!(rounded, 1.year());
7075
7076
        let span = 3.hours();
7077
        let options =
7078
            SpanRound::new().largest(Unit::Year).relative(date(2020, 3, 8));
7079
        let rounded = span.round(options).unwrap();
7080
        span_eq!(rounded, 3.hours());
7081
    }
7082
7083
    #[test]
7084
    fn span_sign() {
7085
        assert_eq!(Span::new().get_sign_ranged(), C(0));
7086
        assert_eq!(Span::new().days(1).get_sign_ranged(), C(1));
7087
        assert_eq!(Span::new().days(-1).get_sign_ranged(), C(-1));
7088
        assert_eq!(Span::new().days(1).days(0).get_sign_ranged(), C(0));
7089
        assert_eq!(Span::new().days(-1).days(0).get_sign_ranged(), C(0));
7090
        assert_eq!(
7091
            Span::new().years(1).days(1).days(0).get_sign_ranged(),
7092
            C(1)
7093
        );
7094
        assert_eq!(
7095
            Span::new().years(-1).days(-1).days(0).get_sign_ranged(),
7096
            C(-1)
7097
        );
7098
    }
7099
7100
    #[test]
7101
    fn span_size() {
7102
        #[cfg(target_pointer_width = "64")]
7103
        {
7104
            #[cfg(debug_assertions)]
7105
            {
7106
                assert_eq!(core::mem::align_of::<Span>(), 8);
7107
                assert_eq!(core::mem::size_of::<Span>(), 184);
7108
            }
7109
            #[cfg(not(debug_assertions))]
7110
            {
7111
                assert_eq!(core::mem::align_of::<Span>(), 8);
7112
                assert_eq!(core::mem::size_of::<Span>(), 64);
7113
            }
7114
        }
7115
    }
7116
7117
    quickcheck::quickcheck! {
7118
        fn prop_roundtrip_span_nanoseconds(span: Span) -> quickcheck::TestResult {
7119
            let largest = span.largest_unit();
7120
            if largest > Unit::Day {
7121
                return quickcheck::TestResult::discard();
7122
            }
7123
            let nanos = span.to_invariant_nanoseconds();
7124
            let got = Span::from_invariant_nanoseconds(largest, nanos).unwrap();
7125
            quickcheck::TestResult::from_bool(nanos == got.to_invariant_nanoseconds())
7126
        }
7127
    }
7128
7129
    /// # `serde` deserializer compatibility test
7130
    ///
7131
    /// Serde YAML used to be unable to deserialize `jiff` types,
7132
    /// as deserializing from bytes is not supported by the deserializer.
7133
    ///
7134
    /// - <https://github.com/BurntSushi/jiff/issues/138>
7135
    /// - <https://github.com/BurntSushi/jiff/discussions/148>
7136
    #[test]
7137
    fn span_deserialize_yaml() {
7138
        let expected = Span::new()
7139
            .years(1)
7140
            .months(2)
7141
            .weeks(3)
7142
            .days(4)
7143
            .hours(5)
7144
            .minutes(6)
7145
            .seconds(7);
7146
7147
        let deserialized: Span =
7148
            serde_yaml::from_str("P1y2m3w4dT5h6m7s").unwrap();
7149
7150
        span_eq!(deserialized, expected);
7151
7152
        let deserialized: Span =
7153
            serde_yaml::from_slice("P1y2m3w4dT5h6m7s".as_bytes()).unwrap();
7154
7155
        span_eq!(deserialized, expected);
7156
7157
        let cursor = Cursor::new(b"P1y2m3w4dT5h6m7s");
7158
        let deserialized: Span = serde_yaml::from_reader(cursor).unwrap();
7159
7160
        span_eq!(deserialized, expected);
7161
    }
7162
7163
    #[test]
7164
    fn display() {
7165
        let span = Span::new()
7166
            .years(1)
7167
            .months(2)
7168
            .weeks(3)
7169
            .days(4)
7170
            .hours(5)
7171
            .minutes(6)
7172
            .seconds(7)
7173
            .milliseconds(8)
7174
            .microseconds(9)
7175
            .nanoseconds(10);
7176
        insta::assert_snapshot!(
7177
            span,
7178
            @"P1Y2M3W4DT5H6M7.00800901S",
7179
        );
7180
        insta::assert_snapshot!(
7181
            alloc::format!("{span:#}"),
7182
            @"1y 2mo 3w 4d 5h 6m 7s 8ms 9µs 10ns",
7183
        );
7184
    }
7185
7186
    /// This test ensures that we can parse `humantime` formatted durations.
7187
    #[test]
7188
    fn humantime_compatibility_parse() {
7189
        let dur = std::time::Duration::new(60 * 60 * 24 * 411, 123_456_789);
7190
        let formatted = humantime::format_duration(dur).to_string();
7191
        assert_eq!(
7192
            formatted,
7193
            "1year 1month 15days 7h 26m 24s 123ms 456us 789ns"
7194
        );
7195
        let expected = 1
7196
            .year()
7197
            .months(1)
7198
            .days(15)
7199
            .hours(7)
7200
            .minutes(26)
7201
            .seconds(24)
7202
            .milliseconds(123)
7203
            .microseconds(456)
7204
            .nanoseconds(789);
7205
        span_eq!(formatted.parse::<Span>().unwrap(), expected);
7206
    }
7207
7208
    /// This test ensures that we can print a `Span` that `humantime` can
7209
    /// parse.
7210
    ///
7211
    /// Note that this isn't the default since `humantime`'s parser is
7212
    /// pretty limited. e.g., It doesn't support things like `nsecs`
7213
    /// despite supporting `secs`. And other reasons. See the docs on
7214
    /// `Designator::HumanTime` for why we sadly provide a custom variant for
7215
    /// it.
7216
    #[test]
7217
    fn humantime_compatibility_print() {
7218
        static PRINTER: friendly::SpanPrinter = friendly::SpanPrinter::new()
7219
            .designator(friendly::Designator::HumanTime);
7220
7221
        let span = 1
7222
            .year()
7223
            .months(1)
7224
            .days(15)
7225
            .hours(7)
7226
            .minutes(26)
7227
            .seconds(24)
7228
            .milliseconds(123)
7229
            .microseconds(456)
7230
            .nanoseconds(789);
7231
        let formatted = PRINTER.span_to_string(&span);
7232
        assert_eq!(formatted, "1y 1month 15d 7h 26m 24s 123ms 456us 789ns");
7233
7234
        let dur = humantime::parse_duration(&formatted).unwrap();
7235
        let expected =
7236
            std::time::Duration::new(60 * 60 * 24 * 411, 123_456_789);
7237
        assert_eq!(dur, expected);
7238
    }
7239
7240
    #[test]
7241
    fn from_str() {
7242
        let p = |s: &str| -> Result<Span, Error> { s.parse() };
7243
7244
        insta::assert_snapshot!(
7245
            p("1 day").unwrap(),
7246
            @"P1D",
7247
        );
7248
        insta::assert_snapshot!(
7249
            p("+1 day").unwrap(),
7250
            @"P1D",
7251
        );
7252
        insta::assert_snapshot!(
7253
            p("-1 day").unwrap(),
7254
            @"-P1D",
7255
        );
7256
        insta::assert_snapshot!(
7257
            p("P1d").unwrap(),
7258
            @"P1D",
7259
        );
7260
        insta::assert_snapshot!(
7261
            p("+P1d").unwrap(),
7262
            @"P1D",
7263
        );
7264
        insta::assert_snapshot!(
7265
            p("-P1d").unwrap(),
7266
            @"-P1D",
7267
        );
7268
7269
        insta::assert_snapshot!(
7270
            p("").unwrap_err(),
7271
            @"an empty string is not a valid `Span`, expected either a ISO 8601 or Jiff's 'friendly' format",
7272
        );
7273
        insta::assert_snapshot!(
7274
            p("+").unwrap_err(),
7275
            @"found nothing after sign `+`, which is not a valid `Span`, expected either a ISO 8601 or Jiff's 'friendly' format",
7276
        );
7277
        insta::assert_snapshot!(
7278
            p("-").unwrap_err(),
7279
            @"found nothing after sign `-`, which is not a valid `Span`, expected either a ISO 8601 or Jiff's 'friendly' format",
7280
        );
7281
    }
7282
7283
    #[test]
7284
    fn serde_deserialize() {
7285
        let p = |s: &str| -> Result<Span, serde_json::Error> {
7286
            serde_json::from_str(&alloc::format!("\"{s}\""))
7287
        };
7288
7289
        insta::assert_snapshot!(
7290
            p("1 day").unwrap(),
7291
            @"P1D",
7292
        );
7293
        insta::assert_snapshot!(
7294
            p("+1 day").unwrap(),
7295
            @"P1D",
7296
        );
7297
        insta::assert_snapshot!(
7298
            p("-1 day").unwrap(),
7299
            @"-P1D",
7300
        );
7301
        insta::assert_snapshot!(
7302
            p("P1d").unwrap(),
7303
            @"P1D",
7304
        );
7305
        insta::assert_snapshot!(
7306
            p("+P1d").unwrap(),
7307
            @"P1D",
7308
        );
7309
        insta::assert_snapshot!(
7310
            p("-P1d").unwrap(),
7311
            @"-P1D",
7312
        );
7313
7314
        insta::assert_snapshot!(
7315
            p("").unwrap_err(),
7316
            @"an empty string is not a valid `Span`, expected either a ISO 8601 or Jiff's 'friendly' format at line 1 column 2",
7317
        );
7318
        insta::assert_snapshot!(
7319
            p("+").unwrap_err(),
7320
            @"found nothing after sign `+`, which is not a valid `Span`, expected either a ISO 8601 or Jiff's 'friendly' format at line 1 column 3",
7321
        );
7322
        insta::assert_snapshot!(
7323
            p("-").unwrap_err(),
7324
            @"found nothing after sign `-`, which is not a valid `Span`, expected either a ISO 8601 or Jiff's 'friendly' format at line 1 column 3",
7325
        );
7326
    }
7327
}