Skip to main content

jiff/fmt/friendly/
printer.rs

1use crate::{
2    fmt::{
3        buffer::{ArrayBuffer, BorrowedBuffer},
4        Write,
5    },
6    Error, SignedDuration, Span, Unit,
7};
8
9const SECS_PER_HOUR: u64 = MINS_PER_HOUR * SECS_PER_MIN;
10const SECS_PER_MIN: u64 = 60;
11const MINS_PER_HOUR: u64 = 60;
12const NANOS_PER_HOUR: u128 =
13    (SECS_PER_MIN * MINS_PER_HOUR * NANOS_PER_SEC) as u128;
14const NANOS_PER_MIN: u128 = (SECS_PER_MIN * NANOS_PER_SEC) as u128;
15const NANOS_PER_SEC: u64 = 1_000_000_000;
16const NANOS_PER_MILLI: u32 = 1_000_000;
17const NANOS_PER_MICRO: u32 = 1_000;
18
19/// Defines the maximum possible length (in bytes) of a `Span` printed in the
20/// friendly format.
21///
22/// The way I computed this length was by using settings that would produce the
23/// maximal amount of text and using a negative `Span` with each unit set to
24/// its minimum value:
25///
26/// ```ignore
27/// SpanPrinter::new()
28///     .padding(u8::MAX)
29///     .designator(friendly::Designator::Verbose)
30///     .spacing(friendly::Spacing::BetweenUnitsAndDesignators)
31///     .comma_after_designator(true);
32/// ```
33const MAX_SPAN_LEN: usize = 306;
34
35/// Defines the maximum possible length (in bytes) of a `SignedDuration`
36/// printed in the friendly format.
37///
38/// See `MAX_SPAN_LEN` for how this was computed. In this case,
39/// `SignedDuration::MIN` was used.
40const MAX_SIGNED_DURATION_LEN: usize = 194;
41
42/// Defines the maximum possible length (in bytes) of a `std::time::Duration`
43/// printed in the friendly format.
44///
45/// See `MAX_SPAN_LEN` for how this was computed. In this case,
46/// `std::time::Duration::MAX - std::time::Duration::from_secs(16)` was used
47/// to find the maximal length. This was a little tricky because the maximal
48/// unsigned duration has minute units equivalent to `0`, and is thus quite a
49/// bit shorter than the maximum possible length.
50///
51/// Note that this is shorter than `MAX_SIGNED_DURATION` because one cannot
52/// get a negative formatted duration from a `std::time::Duration`. So there's
53/// no way to get an ` ago` suffix (or a `-` prefix).
54const MAX_UNSIGNED_DURATION_LEN: usize = 190;
55
56/// Configuration for [`SpanPrinter::designator`].
57///
58/// This controls which kinds of designators to use when formatting a
59/// "friendly" duration. Generally, this only provides one axis of control:
60/// the length of each designator.
61///
62/// # Example
63///
64/// ```
65/// use jiff::{fmt::friendly::{Designator, SpanPrinter}, ToSpan};
66///
67/// let span = 1.year().months(2);
68///
69/// let printer = SpanPrinter::new();
70/// assert_eq!(printer.span_to_string(&span), "1y 2mo");
71///
72/// let printer = SpanPrinter::new().designator(Designator::Short);
73/// assert_eq!(printer.span_to_string(&span), "1yr 2mos");
74///
75/// let printer = SpanPrinter::new().designator(Designator::Verbose);
76/// assert_eq!(printer.span_to_string(&span), "1year 2months");
77///
78/// let printer = SpanPrinter::new().designator(Designator::HumanTime);
79/// assert_eq!(printer.span_to_string(&span), "1y 2months");
80/// ```
81#[derive(Clone, Copy, Debug)]
82#[non_exhaustive]
83pub enum Designator {
84    /// This writes out the full word of each unit designation. For example,
85    /// `year`.
86    Verbose,
87    /// This writes out a short but not minimal label for each unit. For
88    /// example, `yr` for `year` and `yrs` for `years`.
89    Short,
90    /// This writes out the shortest possible label for each unit that is still
91    /// generally recognizable. For example, `y`. Note that in the compact
92    /// representation, and unlike the verbose and short representations, there
93    /// is no distinction between singular or plural.
94    Compact,
95    /// A special mode that uses designator labels that are known to be
96    /// compatible with the `humantime` crate.
97    ///
98    /// None of `Verbose`, `Short` or `Compact` are compatible with
99    /// `humantime`.
100    ///
101    /// `Compact` is, on its own, nearly compatible. When using `Compact`, all
102    /// designator labels are parsable by `humantime` except for months and
103    /// microseconds. For months, Jiff uses `mo` and `mos`, but `humantime`
104    /// only parses `months`, `month` and `M`. Jiff specifically doesn't
105    /// support `M` for months because of the confusability with minutes.
106    /// For microseconds, Jiff uses `µs` which `humantime` does not support
107    /// parsing.
108    ///
109    /// Most of the designator labels Jiff uses for `Short` aren't supported
110    /// by `humantime`. And even when they are, `humantime` is inconsistent.
111    /// For example, `humantime` supports `sec` and `secs`, but only `nsec`
112    /// and not `nsecs`.
113    ///
114    /// Finally, for `Verbose`, humantime supports spelling out some units
115    /// in their entirety (e.g., `seconds`) but not others (e.g., `nanoseconds`
116    /// is not supported by `humantime`).
117    ///
118    /// Therefore, this custom variant is provided so that designator labels
119    /// that are compatible with both Jiff and `humantime`, even when there
120    /// isn't a coherent concept otherwise connecting their style.
121    HumanTime,
122}
123
124/// Configuration for [`SpanPrinter::spacing`].
125///
126/// This controls how much or how little whitespace is inserted into a
127/// "friendly" formatted duration. Typically, one wants less whitespace when
128/// using short unit designators (i.e., `y` instead of `years`), and more
129/// whitespace when using longer unit designators.
130///
131/// # Example
132///
133/// ```
134/// use jiff::{
135///     fmt::friendly::{Designator, Spacing, SpanPrinter},
136///     ToSpan,
137/// };
138///
139/// let span = 1.year().months(2);
140///
141/// // The default tries to balance spacing with compact
142/// // unit designators.
143/// let printer = SpanPrinter::new();
144/// assert_eq!(printer.span_to_string(&span), "1y 2mo");
145///
146/// // But you can use slightly more descriptive
147/// // designators without being too verbose.
148/// let printer = SpanPrinter::new()
149///     .designator(Designator::Short);
150/// assert_eq!(printer.span_to_string(&span), "1yr 2mos");
151///
152/// // When spacing is removed, it usually looks nicer
153/// // to use compact unit designators.
154/// let printer = SpanPrinter::new()
155///     .spacing(Spacing::None)
156///     .designator(Designator::Compact);
157/// assert_eq!(printer.span_to_string(&span), "1y2mo");
158///
159/// // Conversely, when using more spacing, it usually
160/// // looks nicer to use verbose unit designators.
161/// let printer = SpanPrinter::new()
162///     .spacing(Spacing::BetweenUnitsAndDesignators)
163///     .designator(Designator::Verbose);
164/// assert_eq!(printer.span_to_string(&span), "1 year 2 months");
165/// ```
166#[derive(Clone, Copy, Debug)]
167#[non_exhaustive]
168pub enum Spacing {
169    /// Does not insert any ASCII whitespace.
170    ///
171    /// Except in the case that [`SpanPrinter::hours_minutes_seconds`] is
172    /// enabled and one is formatting a span with non-zero calendar units, then
173    /// an ASCII whitespace is inserted between the calendar and non-calendar
174    /// units even when `Spacing::None` is used.
175    None,
176    /// Inserts one ASCII whitespace between the unit designator and the next
177    /// unit value.
178    ///
179    /// For example, `1year 2months`.
180    BetweenUnits,
181    /// Inserts one ASCII whitespace between the unit value and the unit
182    /// designator, in addition to inserting one ASCII whitespace between the
183    /// unit designator and the next unit value.
184    ///
185    /// For example, `1 year 2 months`.
186    BetweenUnitsAndDesignators,
187}
188
189impl Spacing {
190    fn between_units(self) -> Option<u8> {
191        match self {
192            Spacing::None => None,
193            Spacing::BetweenUnits => Some(b' '),
194            Spacing::BetweenUnitsAndDesignators => Some(b' '),
195        }
196    }
197
198    fn between_units_and_designators(self) -> Option<u8> {
199        match self {
200            Spacing::None => None,
201            Spacing::BetweenUnits => None,
202            Spacing::BetweenUnitsAndDesignators => Some(b' '),
203        }
204    }
205}
206
207/// Configuration for [`SpanPrinter::direction`].
208///
209/// This controls how the sign, if at all, is included in the formatted
210/// duration.
211///
212/// When using the "hours-minutes-seconds" format, `Auto` and `Suffix` are
213/// both treated as equivalent to `Sign` when all calendar units (days and
214/// greater) are zero.
215///
216/// # Example
217///
218/// ```
219/// use jiff::{fmt::friendly::{Direction, SpanPrinter}, SignedDuration};
220///
221/// let duration = SignedDuration::from_secs(-1);
222///
223/// let printer = SpanPrinter::new();
224/// assert_eq!(printer.duration_to_string(&duration), "1s ago");
225///
226/// let printer = SpanPrinter::new().direction(Direction::Sign);
227/// assert_eq!(printer.duration_to_string(&duration), "-1s");
228/// ```
229#[derive(Clone, Copy, Debug)]
230#[non_exhaustive]
231pub enum Direction {
232    /// Sets the sign format based on other configuration options.
233    ///
234    /// When [`SpanPrinter::spacing`] is set to [`Spacing::None`], then
235    /// `Auto` is equivalent to `Sign`.
236    ///
237    /// When using the "hours-minutes-seconds" format, `Auto` is equivalent to
238    /// `Sign` when all calendar units (days and greater) are zero.
239    ///
240    /// Otherwise, `Auto` is equivalent to `Suffix`.
241    ///
242    /// This is the default used by [`SpanPrinter`].
243    Auto,
244    /// When set, a sign is only written when the span or duration is negative.
245    /// And when it is written, it is written as a prefix of the formatted
246    /// duration.
247    Sign,
248    /// A sign is always written, with `-` for negative spans and `+` for all
249    /// non-negative spans. The sign is always written as a prefix of the
250    /// formatted duration.
251    ForceSign,
252    /// When set, a sign is only written when the span or duration is negative.
253    /// And when it is written, it is written as a suffix via a trailing ` ago`
254    /// string.
255    Suffix,
256}
257
258impl Direction {
259    /// Returns the sign string to use (as either a prefix or a suffix) based
260    /// on the given parameters.
261    ///
262    /// This lets us do the case analysis for how to write the sign exactly
263    /// once.
264    fn sign(
265        self,
266        printer: &SpanPrinter,
267        has_calendar: bool,
268        signum: i8,
269    ) -> Option<DirectionSign> {
270        match self {
271            Direction::Auto => match printer.spacing {
272                Spacing::None => {
273                    if signum < 0 {
274                        Some(DirectionSign::Prefix(b'-'))
275                    } else {
276                        None
277                    }
278                }
279                Spacing::BetweenUnits
280                | Spacing::BetweenUnitsAndDesignators => {
281                    if signum < 0 {
282                        if printer.hms && !has_calendar {
283                            Some(DirectionSign::Prefix(b'-'))
284                        } else {
285                            Some(DirectionSign::Suffix)
286                        }
287                    } else {
288                        None
289                    }
290                }
291            },
292            Direction::Sign => {
293                if signum < 0 {
294                    Some(DirectionSign::Prefix(b'-'))
295                } else {
296                    None
297                }
298            }
299            Direction::ForceSign => {
300                Some(DirectionSign::Prefix(if signum < 0 {
301                    b'-'
302                } else {
303                    b'+'
304                }))
305            }
306            Direction::Suffix => {
307                if signum < 0 {
308                    Some(DirectionSign::Suffix)
309                } else {
310                    None
311                }
312            }
313        }
314    }
315}
316
317/// The sign to write and whether it should be a prefix or a suffix.
318#[derive(Clone, Copy, Debug)]
319enum DirectionSign {
320    Prefix(u8),
321    Suffix,
322}
323
324/// Configuration for [`SpanPrinter::fractional`].
325///
326/// This controls what kind of fractional unit to use when printing a duration.
327/// The default, unless [`SpanPrinter::hours_minutes_seconds`] is enabled, is
328/// to not write any fractional numbers at all.
329///
330/// The fractional unit set refers to the smallest whole integer that can occur
331/// in a "friendly" formatted duration. If there are any non-zero units less
332/// than the fractional unit in the duration, then they are formatted as a
333/// fraction.
334///
335/// # Example
336///
337/// This example shows how to write the same duration with different
338/// fractional settings:
339///
340/// ```
341/// use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, SignedDuration};
342///
343/// let duration = SignedDuration::from_secs(3663);
344///
345/// let printer = SpanPrinter::new()
346///     .fractional(Some(FractionalUnit::Hour));
347/// assert_eq!(printer.duration_to_string(&duration), "1.0175h");
348///
349/// let printer = SpanPrinter::new()
350///     .fractional(Some(FractionalUnit::Minute));
351/// assert_eq!(printer.duration_to_string(&duration), "1h 1.05m");
352///
353/// let printer = SpanPrinter::new()
354///     .fractional(Some(FractionalUnit::Second));
355/// assert_eq!(printer.duration_to_string(&duration), "1h 1m 3s");
356/// ```
357#[derive(Clone, Copy, Debug)]
358#[non_exhaustive]
359pub enum FractionalUnit {
360    /// The smallest whole integer unit allowed is hours.
361    ///
362    /// **WARNING**: Since fractional units are limited to 9 decimal places,
363    /// using this setting could result in precision loss.
364    Hour,
365    /// The smallest whole integer unit allowed is minutes.
366    ///
367    /// **WARNING**: Since fractional units are limited to 9 decimal places,
368    /// using this setting could result in precision loss.
369    Minute,
370    /// The smallest whole integer unit allowed is seconds.
371    Second,
372    /// The smallest whole integer unit allowed is milliseconds.
373    Millisecond,
374    /// The smallest whole integer unit allowed is microseconds.
375    Microsecond,
376}
377
378impl From<FractionalUnit> for Unit {
379    fn from(u: FractionalUnit) -> Unit {
380        match u {
381            FractionalUnit::Hour => Unit::Hour,
382            FractionalUnit::Minute => Unit::Minute,
383            FractionalUnit::Second => Unit::Second,
384            FractionalUnit::Millisecond => Unit::Millisecond,
385            FractionalUnit::Microsecond => Unit::Microsecond,
386        }
387    }
388}
389
390/// A printer for Jiff's "friendly" duration format.
391///
392/// This printer provides a lot of different knobs for controlling how
393/// durations are formatted. It supports formatting both [`SignedDuration`]
394/// and [`Span`].
395///
396/// # Example: automatic use through `Display`
397///
398/// The default configuration of this printer is used for "alternate" display
399/// formatting for both [`SignedDuration`] and [`Span`]:
400///
401/// ```
402/// use jiff::{SignedDuration, ToSpan};
403///
404/// let span = 1.year().months(2).hours(15).seconds(30).nanoseconds(1);
405/// assert_eq!(format!("{span:#}"), "1y 2mo 15h 30s 1ns");
406///
407/// let sdur = SignedDuration::new(15 * 60 * 60 + 30, 1);
408/// assert_eq!(format!("{sdur:#}"), "15h 30s 1ns");
409/// ```
410///
411/// # Example: variety of formatting configurations
412///
413/// This example shows a few different ways of formatting the same `Span`:
414///
415/// ```
416/// use jiff::{
417///     fmt::friendly::{Designator, Spacing, SpanPrinter},
418///     ToSpan,
419/// };
420///
421/// let span = 1.year().months(2).hours(15).seconds(30).nanoseconds(1);
422///
423/// let printer = SpanPrinter::new();
424/// assert_eq!(
425///     printer.span_to_string(&span),
426///     "1y 2mo 15h 30s 1ns",
427/// );
428///
429/// let printer = SpanPrinter::new()
430///     .designator(Designator::Short);
431/// assert_eq!(
432///     printer.span_to_string(&span),
433///     "1yr 2mos 15hrs 30secs 1nsec",
434/// );
435///
436/// let printer = SpanPrinter::new()
437///     .spacing(Spacing::None)
438///     .designator(Designator::Compact);
439/// assert_eq!(
440///     printer.span_to_string(&span),
441///     "1y2mo15h30s1ns",
442/// );
443///
444/// let printer = SpanPrinter::new()
445///     .spacing(Spacing::BetweenUnitsAndDesignators)
446///     .comma_after_designator(true)
447///     .designator(Designator::Verbose);
448/// assert_eq!(
449///     printer.span_to_string(&span),
450///     "1 year, 2 months, 15 hours, 30 seconds, 1 nanosecond",
451/// );
452///
453/// let printer = SpanPrinter::new()
454///     .hours_minutes_seconds(true)
455///     .spacing(Spacing::BetweenUnitsAndDesignators)
456///     .comma_after_designator(true)
457///     .designator(Designator::Verbose);
458/// assert_eq!(
459///     printer.span_to_string(&span),
460///     "1 year, 2 months, 15:00:30.000000001",
461/// );
462/// ```
463///
464/// # Example: negative durations
465///
466/// By default, a negative duration will be represented with an ` ago` suffix:
467///
468/// ```
469/// use jiff::{fmt::friendly::SpanPrinter, ToSpan};
470///
471/// let span = -1.year().months(2).hours(15).seconds(30).nanoseconds(1);
472///
473/// let printer = SpanPrinter::new();
474/// assert_eq!(
475///     printer.span_to_string(&span),
476///     "1y 2mo 15h 30s 1ns ago",
477/// );
478/// ```
479///
480/// But one can also use a prefix `-` sign instead. Usually this works better
481/// without any spacing and compact designators:
482///
483/// ```
484/// use jiff::{fmt::friendly::{Designator, Spacing, SpanPrinter}, ToSpan};
485///
486/// let span = -1.year().months(2).hours(15).seconds(30).nanoseconds(1);
487///
488/// let printer = SpanPrinter::new()
489///     .spacing(Spacing::None)
490///     .designator(Designator::Compact);
491/// assert_eq!(
492///     printer.span_to_string(&span),
493///     "-1y2mo15h30s1ns",
494/// );
495/// ```
496#[derive(Clone, Debug)]
497pub struct SpanPrinter {
498    designators: &'static Designators,
499    spacing: Spacing,
500    direction: Direction,
501    fractional: Option<FractionalUnit>,
502    comma_after_designator: bool,
503    hms: bool,
504    padding: Option<u8>,
505    precision: Option<u8>,
506    zero_unit: Unit,
507}
508
509impl SpanPrinter {
510    /// Creates a new printer for the "friendly" duration format.
511    ///
512    /// The printer returned uses the default configuration. This is
513    /// identical to `SpanPrinter::default`, but it can be used in a `const`
514    /// context.
515    ///
516    /// # Example
517    ///
518    /// This example shows how to format a duration directly to a `Vec<u8>`.
519    ///
520    /// ```
521    /// use jiff::{fmt::friendly::SpanPrinter, ToSpan};
522    ///
523    /// static PRINTER: SpanPrinter = SpanPrinter::new();
524    ///
525    /// let span = 1.year().months(2);
526    /// let mut buf = vec![];
527    /// // Writing to a `Vec<u8>` never fails (aside from OOM).
528    /// PRINTER.print_span(&span, &mut buf).unwrap();
529    /// assert_eq!(buf, b"1y 2mo");
530    /// ```
531    #[inline]
532    pub const fn new() -> SpanPrinter {
533        SpanPrinter {
534            designators: Designators::new(Designator::Compact),
535            spacing: Spacing::BetweenUnits,
536            direction: Direction::Auto,
537            fractional: None,
538            comma_after_designator: false,
539            hms: false,
540            padding: None,
541            precision: None,
542            zero_unit: Unit::Second,
543        }
544    }
545
546    /// Configures the kind of unit designators to use.
547    ///
548    /// There are no specific advantages or disadvantages to the kind
549    /// of designator you pick other than aesthetic preference. Shorter
550    /// designators are also likely faster to parse and print.
551    ///
552    /// The default is [`Designator::Compact`], which uses things like `yr`
553    /// instead of `year` (verbose) or `y` (compact).
554    ///
555    /// # Example
556    ///
557    /// ```
558    /// use jiff::{
559    ///     fmt::friendly::{Designator, SpanPrinter},
560    ///     ToSpan,
561    /// };
562    ///
563    /// let span = 1.year().months(2);
564    ///
565    /// let printer = SpanPrinter::new();
566    /// assert_eq!(printer.span_to_string(&span), "1y 2mo");
567    ///
568    /// let printer = SpanPrinter::new().designator(Designator::Short);
569    /// assert_eq!(printer.span_to_string(&span), "1yr 2mos");
570    ///
571    /// let printer = SpanPrinter::new().designator(Designator::Verbose);
572    /// assert_eq!(printer.span_to_string(&span), "1year 2months");
573    /// ```
574    #[inline]
575    pub const fn designator(self, designator: Designator) -> SpanPrinter {
576        SpanPrinter { designators: Designators::new(designator), ..self }
577    }
578
579    /// Configures the spacing between the units and the designator labels.
580    ///
581    /// The default is [`Spacing::BetweenUnits`], which results in durations
582    /// like `1y 2mo`. `Spacing::None` would result in `1y2mo` and
583    /// `Spacing::BetweenUnitsAndDesignators` would result in `1 y 2 mo`.
584    ///
585    /// # Example
586    ///
587    /// ```
588    /// use jiff::{
589    ///     fmt::friendly::{Designator, Spacing, SpanPrinter},
590    ///     ToSpan,
591    /// };
592    ///
593    /// let span = 1.year().months(2);
594    ///
595    /// // The default tries to balance spacing with compact
596    /// // unit designators.
597    /// let printer = SpanPrinter::new();
598    /// assert_eq!(printer.span_to_string(&span), "1y 2mo");
599    ///
600    /// // But you can use slightly more descriptive
601    /// // designators without being too verbose.
602    /// let printer = SpanPrinter::new()
603    ///     .designator(Designator::Short);
604    /// assert_eq!(printer.span_to_string(&span), "1yr 2mos");
605    ///
606    /// // When spacing is removed, it usually looks nicer
607    /// // to use compact unit designators.
608    /// let printer = SpanPrinter::new()
609    ///     .spacing(Spacing::None)
610    ///     .designator(Designator::Compact);
611    /// assert_eq!(printer.span_to_string(&span), "1y2mo");
612    ///
613    /// // Conversely, when using more spacing, it usually
614    /// // looks nicer to use verbose unit designators.
615    /// let printer = SpanPrinter::new()
616    ///     .spacing(Spacing::BetweenUnitsAndDesignators)
617    ///     .designator(Designator::Verbose);
618    /// assert_eq!(printer.span_to_string(&span), "1 year 2 months");
619    /// ```
620    ///
621    /// # Example: `Spacing::None` can still result in whitespace
622    ///
623    /// In the case that [`SpanPrinter::hours_minutes_seconds`] is enabled
624    /// and one is formatting a span with non-zero calendar units, then an
625    /// ASCII whitespace is inserted between the calendar and non-calendar
626    /// units even when `Spacing::None` is used:
627    ///
628    /// ```
629    /// use jiff::{fmt::friendly::{Spacing, SpanPrinter}, ToSpan};
630    ///
631    /// let span = 1.year().months(2).hours(15);
632    ///
633    /// let printer = SpanPrinter::new()
634    ///     .spacing(Spacing::None)
635    ///     .hours_minutes_seconds(true);
636    /// assert_eq!(printer.span_to_string(&span), "1y2mo 15:00:00");
637    /// ```
638    #[inline]
639    pub const fn spacing(self, spacing: Spacing) -> SpanPrinter {
640        SpanPrinter { spacing, ..self }
641    }
642
643    /// Configures how and when the sign for the duration is written.
644    ///
645    /// The default is [`Direction::Auto`]. In most cases, this results in
646    /// writing the suffix ` ago` after printing the duration units when the
647    /// sign of the duration is negative. And when the sign is positive, there
648    /// is no suffix. However, this can vary based on other settings. For
649    /// example, when [`SpanPrinter::spacing`] is set to [`Spacing::None`],
650    /// then `Direction::Auto` is treated as if it were [`Direction::Sign`].
651    ///
652    /// # Example
653    ///
654    /// ```
655    /// use jiff::{fmt::friendly::{Direction, SpanPrinter}, SignedDuration};
656    ///
657    /// let duration = SignedDuration::from_secs(-1);
658    ///
659    /// let printer = SpanPrinter::new();
660    /// assert_eq!(printer.duration_to_string(&duration), "1s ago");
661    ///
662    /// let printer = SpanPrinter::new().direction(Direction::Sign);
663    /// assert_eq!(printer.duration_to_string(&duration), "-1s");
664    /// ```
665    #[inline]
666    pub const fn direction(self, direction: Direction) -> SpanPrinter {
667        SpanPrinter { direction, ..self }
668    }
669
670    /// Enable fractional formatting for the given unit.
671    ///
672    /// When [`SpanPrinter::hours_minutes_seconds`] is enabled, then this
673    /// setting is automatically set to [`FractionalUnit::Second`]. Otherwise,
674    /// it defaults to `None`, which means no fractions are ever written.
675    ///
676    /// # Example
677    ///
678    /// This example shows how to write the same duration with different
679    /// fractional settings:
680    ///
681    /// ```
682    /// use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, SignedDuration};
683    ///
684    /// let duration = SignedDuration::from_secs(3663);
685    ///
686    /// let printer = SpanPrinter::new()
687    ///     .fractional(Some(FractionalUnit::Hour));
688    /// assert_eq!(printer.duration_to_string(&duration), "1.0175h");
689    ///
690    /// let printer = SpanPrinter::new()
691    ///     .fractional(Some(FractionalUnit::Minute));
692    /// assert_eq!(printer.duration_to_string(&duration), "1h 1.05m");
693    ///
694    /// let printer = SpanPrinter::new()
695    ///     .fractional(Some(FractionalUnit::Second));
696    /// assert_eq!(printer.duration_to_string(&duration), "1h 1m 3s");
697    /// ```
698    ///
699    /// # Example: precision loss
700    ///
701    /// Because the "friendly" format is limited to 9 decimal places, when
702    /// using `FractionalUnit::Hour` or `FractionalUnit::Minute`, it is
703    /// possible for precision loss to occur.
704    ///
705    /// ```
706    /// use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, SignedDuration};
707    ///
708    /// // one nanosecond
709    /// let duration = SignedDuration::new(0, 1);
710    ///
711    /// let printer = SpanPrinter::new()
712    ///     .fractional(Some(FractionalUnit::Hour));
713    /// assert_eq!(printer.duration_to_string(&duration), "0h");
714    ///
715    /// let printer = SpanPrinter::new()
716    ///     .fractional(Some(FractionalUnit::Minute));
717    /// assert_eq!(printer.duration_to_string(&duration), "0m");
718    /// ```
719    #[inline]
720    pub const fn fractional(
721        self,
722        unit: Option<FractionalUnit>,
723    ) -> SpanPrinter {
724        SpanPrinter { fractional: unit, ..self }
725    }
726
727    /// When enabled, commas are written after unit designators.
728    ///
729    /// This is disabled by default.
730    ///
731    /// # Example
732    ///
733    /// ```
734    /// use jiff::{fmt::friendly::{Designator, Spacing, SpanPrinter}, ToSpan};
735    ///
736    /// static PRINTER: SpanPrinter = SpanPrinter::new()
737    ///     .designator(Designator::Verbose)
738    ///     .spacing(Spacing::BetweenUnitsAndDesignators)
739    ///     .comma_after_designator(true);
740    ///
741    /// let span = 5.years().months(3).milliseconds(123);
742    /// assert_eq!(
743    ///     PRINTER.span_to_string(&span),
744    ///     "5 years, 3 months, 123 milliseconds",
745    /// );
746    /// ```
747    #[inline]
748    pub const fn comma_after_designator(self, yes: bool) -> SpanPrinter {
749        SpanPrinter { comma_after_designator: yes, ..self }
750    }
751
752    /// Formats the span or duration into a `HH:MM:SS[.fffffffff]` format.
753    ///
754    /// When formatting a `Span` with non-zero calendar units (units of days
755    /// or greater), then the calendar units are formatted as typical with
756    /// their corresponding designators. For example, `1d 01:00:00`. Note
757    /// that when formatting a `SignedDuration`, calendar units are never used.
758    ///
759    /// When this is enabled, many of the other options are either ignored or
760    /// fixed to a specific setting:
761    ///
762    /// * Since this format does not use any unit designators for units of
763    /// hours or smaller, the [`SpanPrinter::designator`] setting is ignored
764    /// for hours or smaller. It is still used when formatting a `Span` with
765    /// non-zero calendar units.
766    /// * [`SpanPrinter::spacing`] setting is ignored for units of hours or
767    /// smaller.
768    /// * The [`SpanPrinter::fractional`] setting is forcefully set to
769    /// [`FractionalUnit::Second`]. It cannot be changed.
770    /// * The [`SpanPrinter::comma_after_designator`] setting is ignored for
771    /// units of hours or smaller.
772    /// * When the padding is not specified, it defaults to `2` for hours,
773    /// minutes and seconds and `0` for any calendar units present.
774    /// * The precision setting is respected as documented.
775    ///
776    /// This format is useful in contexts for interfacing with existing systems
777    /// that require this style of format, or if the `HH:MM:SS` is just in
778    /// general preferred.
779    ///
780    /// # Loss of fidelity
781    ///
782    /// When using this format with a `Span`, sub-second units are formatted
783    /// as a fractional second. This means that `1000 milliseconds` and
784    /// `1 second` format to precisely the same string. This is similar to the
785    /// loss of fidelity when using [`fmt::temporal`](crate::fmt::temporal)
786    /// to format spans in the ISO 8601 duration format.
787    ///
788    /// # Example
789    ///
790    /// This shows how to format a `Span` in `HH:MM:SS` format:
791    ///
792    /// ```
793    /// use jiff::{fmt::friendly::SpanPrinter, ToSpan};
794    ///
795    /// static PRINTER: SpanPrinter =
796    ///     SpanPrinter::new().hours_minutes_seconds(true);
797    ///
798    /// let span = 2.hours().minutes(59).seconds(15).milliseconds(123);
799    /// assert_eq!(PRINTER.span_to_string(&span), "02:59:15.123");
800    /// assert_eq!(PRINTER.span_to_string(&-span), "-02:59:15.123");
801    ///
802    /// // This shows what happens with calendar units.
803    /// let span = 15.days().hours(2).minutes(59).seconds(15).milliseconds(123);
804    /// assert_eq!(PRINTER.span_to_string(&span), "15d 02:59:15.123");
805    /// // Notice that because calendar units are specified and the sign
806    /// // setting is set to "auto" by default, it has switched to a suffix.
807    /// assert_eq!(PRINTER.span_to_string(&-span), "15d 02:59:15.123 ago");
808    /// ```
809    ///
810    /// And this shows the same, but with a [`SignedDuration`]:
811    ///
812    /// ```
813    /// use jiff::{fmt::friendly::SpanPrinter, SignedDuration};
814    ///
815    /// static PRINTER: SpanPrinter =
816    ///     SpanPrinter::new().hours_minutes_seconds(true);
817    ///
818    /// let duration = SignedDuration::new(
819    ///     2 * 60 * 60 + 59 * 60 + 15,
820    ///     123_000_000,
821    /// );
822    /// assert_eq!(PRINTER.duration_to_string(&duration), "02:59:15.123");
823    /// assert_eq!(PRINTER.duration_to_string(&-duration), "-02:59:15.123");
824    /// ```
825    ///
826    /// # Example: `Span` versus `SignedDuration`
827    ///
828    /// The main advantage of a `Span` is that, except for fractional
829    /// components, the unit values emitted correspond precisely to the values
830    /// in the `Span`. Where as for a `SignedDuration`, the units are always
831    /// computed from a single absolute duration in a way that is always
832    /// balanced:
833    ///
834    /// ```
835    /// use jiff::{fmt::friendly::SpanPrinter, SignedDuration, ToSpan};
836    ///
837    /// static PRINTER: SpanPrinter =
838    ///     SpanPrinter::new().hours_minutes_seconds(true);
839    ///
840    /// let span = 120.minutes();
841    /// assert_eq!(PRINTER.span_to_string(&span), "00:120:00");
842    ///
843    /// let duration = SignedDuration::from_mins(120);
844    /// assert_eq!(PRINTER.duration_to_string(&duration), "02:00:00");
845    /// ```
846    ///
847    /// Of course, a balanced duration is sometimes what you want. But `Span`
848    /// affords the flexibility of controlling precisely what the unit values
849    /// are.
850    #[inline]
851    pub const fn hours_minutes_seconds(self, yes: bool) -> SpanPrinter {
852        SpanPrinter { hms: yes, ..self }
853    }
854
855    /// The padding to use when writing unit values.
856    ///
857    /// If a unit value has fewer digits than specified here, it is padded to
858    /// the left with zeroes. (To control precision, i.e., padding to the right
859    /// when writing fractional values, use [`SpanPrinter::precision`].)
860    ///
861    /// By default, when writing in the hours-minutes-seconds format, a padding
862    /// of `2` is used for units of hours, minutes and seconds. Otherwise, a
863    /// padding of `0` is used.
864    ///
865    /// Padding is clamped to a maximum value of `20` (corresponding to the
866    /// number of digits in `u64::MAX`).
867    ///
868    /// # Example
869    ///
870    /// This shows some examples of configuring padding when writing in default
871    /// format with unit designators:
872    ///
873    /// ```
874    /// use jiff::{fmt::friendly::SpanPrinter, ToSpan};
875    ///
876    /// let printer = SpanPrinter::new();
877    /// assert_eq!(printer.span_to_string(&1.hour()), "1h");
878    /// let printer = SpanPrinter::new().padding(3);
879    /// assert_eq!(printer.span_to_string(&1.hour()), "001h");
880    /// ```
881    ///
882    /// And this shows some examples with the hours-minutes-seconds format.
883    /// Notice how padding is enabled by default.
884    ///
885    /// ```
886    /// use jiff::{fmt::friendly::SpanPrinter, ToSpan};
887    ///
888    /// let printer = SpanPrinter::new().hours_minutes_seconds(true);
889    /// assert_eq!(printer.span_to_string(&1.hour()), "01:00:00");
890    /// let printer = SpanPrinter::new().hours_minutes_seconds(true).padding(0);
891    /// assert_eq!(printer.span_to_string(&1.hour()), "1:0:0");
892    ///
893    /// // In this case, under the default configuration, the padding
894    /// // for calendar units is 0 but the padding for time units is 2.
895    /// let printer = SpanPrinter::new().hours_minutes_seconds(true);
896    /// assert_eq!(printer.span_to_string(&1.day().hours(1)), "1d 01:00:00");
897    /// ```
898    #[inline]
899    pub const fn padding(self, digits: u8) -> SpanPrinter {
900        SpanPrinter { padding: Some(digits), ..self }
901    }
902
903    /// The precision to use when writing fractional unit values.
904    ///
905    /// This setting has no effect if fractional formatting isn't enabled.
906    /// Fractional formatting is only enabled when [`SpanPrinter::fractional`]
907    /// is set or if [`SpanPrinter::hours_minutes_seconds`] are enabled.
908    /// Neither are enabled by default.
909    ///
910    /// A precision of `Some(0)` implies that truncation of any fractional
911    /// component always occurs.
912    ///
913    /// The default value is `None`, which means the precision is automatically
914    /// determined from the value. If no fractional component is needed, then
915    /// none will be printed.
916    ///
917    /// Precision is capped to a maximum value of `9` (corresponding to the
918    /// maximum precision supported by Jiff).
919    ///
920    /// # Example
921    ///
922    /// ```
923    /// use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, ToSpan};
924    ///
925    /// // No effect, because fractions aren't enabled.
926    /// let printer = SpanPrinter::new().precision(Some(2));
927    /// assert_eq!(printer.span_to_string(&1.hour()), "1h");
928    ///
929    /// // Precision setting takes effect!
930    /// let printer = SpanPrinter::new()
931    ///     .precision(Some(2))
932    ///     .fractional(Some(FractionalUnit::Hour));
933    /// assert_eq!(printer.span_to_string(&1.hour()), "1.00h");
934    ///
935    /// // The HH:MM:SS format automatically enables fractional
936    /// // second values.
937    /// let printer = SpanPrinter::new()
938    ///     // Truncate to millisecond precision.
939    ///     .precision(Some(3))
940    ///     .hours_minutes_seconds(true);
941    /// let span = 1.second().milliseconds(1).microseconds(1).nanoseconds(1);
942    /// assert_eq!(printer.span_to_string(&span), "00:00:01.001");
943    ///
944    /// // Same as above, but with the designator or "expanded"
945    /// // format. This requires explicitly enabling fractional
946    /// // units.
947    /// let printer = SpanPrinter::new()
948    ///     // Truncate to millisecond precision.
949    ///     .precision(Some(3))
950    ///     .fractional(Some(FractionalUnit::Second));
951    /// let span = 1.second().milliseconds(1).microseconds(1).nanoseconds(1);
952    /// assert_eq!(printer.span_to_string(&span), "1.001s");
953    /// ```
954    #[inline]
955    pub const fn precision(self, precision: Option<u8>) -> SpanPrinter {
956        SpanPrinter { precision, ..self }
957    }
958
959    /// Sets the unit to use when printing a duration that is zero.
960    ///
961    /// When [`SpanPrinter::fractional`] is set, then this setting is ignored
962    /// and the zero unit corresponds to the fractional unit specified.
963    ///
964    /// This defaults to [`Unit::Second`].
965    ///
966    /// # Example
967    ///
968    /// ```
969    /// use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, ToSpan, Unit};
970    ///
971    /// // The default just always uses seconds.
972    /// let printer = SpanPrinter::new();
973    /// assert_eq!(printer.span_to_string(&0.years()), "0s");
974    ///
975    /// // We can set our own unit.
976    /// let printer = SpanPrinter::new().zero_unit(Unit::Year);
977    /// assert_eq!(printer.span_to_string(&0.years()), "0y");
978    ///
979    /// // But it's overridden if fractional units are set.
980    /// let printer = SpanPrinter::new()
981    ///     .zero_unit(Unit::Year)
982    ///     .fractional(Some(FractionalUnit::Minute));
983    /// assert_eq!(printer.span_to_string(&0.years()), "0m");
984    ///
985    /// // One use case for this option is if you're rounding
986    /// // spans and want the zero unit to reflect the smallest
987    /// // unit you're using.
988    /// let printer = SpanPrinter::new().zero_unit(Unit::Minute);
989    /// let span = 5.hours().minutes(30).seconds(59);
990    /// let rounded = span.round(Unit::Minute)?;
991    /// assert_eq!(printer.span_to_string(&rounded), "5h 31m");
992    ///
993    /// let span = 5.seconds();
994    /// let rounded = span.round(Unit::Minute)?;
995    /// assert_eq!(printer.span_to_string(&rounded), "0m");
996    ///
997    /// # Ok::<(), Box<dyn std::error::Error>>(())
998    /// ```
999    ///
1000    /// The same applies for `SignedDuration`:
1001    ///
1002    /// ```
1003    /// use jiff::{fmt::friendly::SpanPrinter, SignedDuration, Unit};
1004    ///
1005    /// // The default just always uses seconds.
1006    /// let printer = SpanPrinter::new();
1007    /// assert_eq!(printer.duration_to_string(&SignedDuration::ZERO), "0s");
1008    ///
1009    /// // We can set our own unit.
1010    /// let printer = SpanPrinter::new().zero_unit(Unit::Minute);
1011    /// assert_eq!(printer.duration_to_string(&SignedDuration::ZERO), "0m");
1012    /// ```
1013    #[inline]
1014    pub const fn zero_unit(self, unit: Unit) -> SpanPrinter {
1015        SpanPrinter { zero_unit: unit, ..self }
1016    }
1017
1018    /// Format a `Span` into a string using the "friendly" format.
1019    ///
1020    /// This is a convenience routine for [`SpanPrinter::print_span`] with a
1021    /// `String`.
1022    ///
1023    /// # Example
1024    ///
1025    /// ```
1026    /// use jiff::{fmt::friendly::SpanPrinter, ToSpan};
1027    ///
1028    /// static PRINTER: SpanPrinter = SpanPrinter::new();
1029    ///
1030    /// let span = 3.years().months(5);
1031    /// assert_eq!(PRINTER.span_to_string(&span), "3y 5mo");
1032    /// ```
1033    #[cfg(any(test, feature = "alloc"))]
1034    pub fn span_to_string(&self, span: &Span) -> alloc::string::String {
1035        let mut buf = alloc::string::String::with_capacity(4);
1036        // OK because writing to `String` never fails.
1037        self.print_span(span, &mut buf).unwrap();
1038        buf
1039    }
1040
1041    /// Format a `SignedDuration` into a string using the "friendly" format.
1042    ///
1043    /// This balances the units of the duration up to at most hours
1044    /// automatically.
1045    ///
1046    /// This is a convenience routine for [`SpanPrinter::print_duration`] with
1047    /// a `String`.
1048    ///
1049    /// # Example
1050    ///
1051    /// ```
1052    /// use jiff::{fmt::friendly::{FractionalUnit, SpanPrinter}, SignedDuration};
1053    ///
1054    /// static PRINTER: SpanPrinter = SpanPrinter::new();
1055    ///
1056    /// let dur = SignedDuration::new(86_525, 123_000_789);
1057    /// assert_eq!(
1058    ///     PRINTER.duration_to_string(&dur),
1059    ///     "24h 2m 5s 123ms 789ns",
1060    /// );
1061    /// assert_eq!(
1062    ///     PRINTER.duration_to_string(&-dur),
1063    ///     "24h 2m 5s 123ms 789ns ago",
1064    /// );
1065    ///
1066    /// // Or, if you prefer fractional seconds:
1067    /// static PRINTER_FRACTIONAL: SpanPrinter = SpanPrinter::new()
1068    ///     .fractional(Some(FractionalUnit::Second));
1069    /// assert_eq!(
1070    ///     PRINTER_FRACTIONAL.duration_to_string(&-dur),
1071    ///     "24h 2m 5.123000789s ago",
1072    /// );
1073    /// ```
1074    #[cfg(any(test, feature = "alloc"))]
1075    pub fn duration_to_string(
1076        &self,
1077        duration: &SignedDuration,
1078    ) -> alloc::string::String {
1079        let mut buf = alloc::string::String::with_capacity(4);
1080        // OK because writing to `String` never fails.
1081        self.print_duration(duration, &mut buf).unwrap();
1082        buf
1083    }
1084
1085    /// Format a `std::time::Duration` into a string using the "friendly"
1086    /// format.
1087    ///
1088    /// This balances the units of the duration up to at most hours
1089    /// automatically.
1090    ///
1091    /// This is a convenience routine for
1092    /// [`SpanPrinter::print_unsigned_duration`] with a `String`.
1093    ///
1094    /// # Example
1095    ///
1096    /// ```
1097    /// use std::time::Duration;
1098    ///
1099    /// use jiff::fmt::friendly::{FractionalUnit, SpanPrinter};
1100    ///
1101    /// static PRINTER: SpanPrinter = SpanPrinter::new();
1102    ///
1103    /// let dur = Duration::new(86_525, 123_000_789);
1104    /// assert_eq!(
1105    ///     PRINTER.unsigned_duration_to_string(&dur),
1106    ///     "24h 2m 5s 123ms 789ns",
1107    /// );
1108    ///
1109    /// // Or, if you prefer fractional seconds:
1110    /// static PRINTER_FRACTIONAL: SpanPrinter = SpanPrinter::new()
1111    ///     .fractional(Some(FractionalUnit::Second));
1112    /// assert_eq!(
1113    ///     PRINTER_FRACTIONAL.unsigned_duration_to_string(&dur),
1114    ///     "24h 2m 5.123000789s",
1115    /// );
1116    /// ```
1117    #[cfg(any(test, feature = "alloc"))]
1118    pub fn unsigned_duration_to_string(
1119        &self,
1120        duration: &core::time::Duration,
1121    ) -> alloc::string::String {
1122        let mut buf = alloc::string::String::with_capacity(4);
1123        // OK because writing to `String` never fails.
1124        self.print_unsigned_duration(duration, &mut buf).unwrap();
1125        buf
1126    }
1127
1128    /// Print a `Span` to the given writer using the "friendly" format.
1129    ///
1130    /// # Errors
1131    ///
1132    /// This only returns an error when writing to the given [`Write`]
1133    /// implementation would fail. Some such implementations, like for `String`
1134    /// and `Vec<u8>`, never fail (unless memory allocation fails). In such
1135    /// cases, it would be appropriate to call `unwrap()` on the result.
1136    ///
1137    /// # Example
1138    ///
1139    /// ```
1140    /// use jiff::{fmt::friendly::SpanPrinter, ToSpan};
1141    ///
1142    /// static PRINTER: SpanPrinter = SpanPrinter::new();
1143    ///
1144    /// let span = 3.years().months(5);
1145    ///
1146    /// let mut buf = String::new();
1147    /// // Printing to a `String` can never fail.
1148    /// PRINTER.print_span(&span, &mut buf).unwrap();
1149    /// assert_eq!(buf, "3y 5mo");
1150    /// ```
1151    pub fn print_span<W: Write>(
1152        &self,
1153        span: &Span,
1154        mut wtr: W,
1155    ) -> Result<(), Error> {
1156        let mut buf = ArrayBuffer::<MAX_SPAN_LEN>::default();
1157        let mut bbuf = buf.as_borrowed();
1158        if self.hms {
1159            self.print_span_hms(span, &mut bbuf);
1160        } else {
1161            self.print_span_designators(span, &mut bbuf);
1162        }
1163        wtr.write_str(bbuf.filled())
1164    }
1165
1166    /// Print a `SignedDuration` to the given writer using the "friendly"
1167    /// format.
1168    ///
1169    /// This balances the units of the duration up to at most hours
1170    /// automatically.
1171    ///
1172    /// # Errors
1173    ///
1174    /// This only returns an error when writing to the given [`Write`]
1175    /// implementation would fail. Some such implementations, like for `String`
1176    /// and `Vec<u8>`, never fail (unless memory allocation fails). In such
1177    /// cases, it would be appropriate to call `unwrap()` on the result.
1178    ///
1179    /// # Example
1180    ///
1181    /// ```
1182    /// use jiff::{fmt::friendly::SpanPrinter, SignedDuration};
1183    ///
1184    /// static PRINTER: SpanPrinter = SpanPrinter::new();
1185    ///
1186    /// let dur = SignedDuration::new(86_525, 123_000_789);
1187    ///
1188    /// let mut buf = String::new();
1189    /// // Printing to a `String` can never fail.
1190    /// PRINTER.print_duration(&dur, &mut buf).unwrap();
1191    /// assert_eq!(buf, "24h 2m 5s 123ms 789ns");
1192    ///
1193    /// // Negative durations are supported.
1194    /// buf.clear();
1195    /// PRINTER.print_duration(&-dur, &mut buf).unwrap();
1196    /// assert_eq!(buf, "24h 2m 5s 123ms 789ns ago");
1197    /// ```
1198    pub fn print_duration<W: Write>(
1199        &self,
1200        duration: &SignedDuration,
1201        mut wtr: W,
1202    ) -> Result<(), Error> {
1203        let mut buf = ArrayBuffer::<MAX_SIGNED_DURATION_LEN>::default();
1204        let mut bbuf = buf.as_borrowed();
1205        if self.hms {
1206            self.print_signed_duration_hms(duration, &mut bbuf);
1207        } else {
1208            self.print_signed_duration_designators(duration, &mut bbuf);
1209        }
1210        wtr.write_str(bbuf.filled())
1211    }
1212
1213    /// Print a `std::time::Duration` to the given writer using the "friendly"
1214    /// format.
1215    ///
1216    /// This balances the units of the duration up to at most hours
1217    /// automatically.
1218    ///
1219    /// # Errors
1220    ///
1221    /// This only returns an error when writing to the given [`Write`]
1222    /// implementation would fail. Some such implementations, like for `String`
1223    /// and `Vec<u8>`, never fail (unless memory allocation fails). In such
1224    /// cases, it would be appropriate to call `unwrap()` on the result.
1225    ///
1226    /// # Example
1227    ///
1228    /// ```
1229    /// use std::time::Duration;
1230    ///
1231    /// use jiff::fmt::friendly::SpanPrinter;
1232    ///
1233    /// static PRINTER: SpanPrinter = SpanPrinter::new();
1234    ///
1235    /// let dur = Duration::new(86_525, 123_000_789);
1236    ///
1237    /// let mut buf = String::new();
1238    /// // Printing to a `String` can never fail.
1239    /// PRINTER.print_unsigned_duration(&dur, &mut buf).unwrap();
1240    /// assert_eq!(buf, "24h 2m 5s 123ms 789ns");
1241    /// ```
1242    pub fn print_unsigned_duration<W: Write>(
1243        &self,
1244        duration: &core::time::Duration,
1245        mut wtr: W,
1246    ) -> Result<(), Error> {
1247        let mut buf = ArrayBuffer::<MAX_UNSIGNED_DURATION_LEN>::default();
1248        let mut bbuf = buf.as_borrowed();
1249        if self.hms {
1250            self.print_unsigned_duration_hms(duration, &mut bbuf);
1251        } else {
1252            self.print_unsigned_duration_designators(duration, &mut bbuf);
1253        }
1254        wtr.write_str(bbuf.filled())
1255    }
1256
1257    fn print_span_designators(
1258        &self,
1259        span: &Span,
1260        bbuf: &mut BorrowedBuffer<'_>,
1261    ) {
1262        let mut wtr = DesignatorWriter::new(self, bbuf, false, span.signum());
1263        wtr.maybe_write_prefix_sign();
1264        match self.fractional {
1265            None => {
1266                self.print_span_designators_non_fraction(span, &mut wtr);
1267            }
1268            Some(unit) => {
1269                self.print_span_designators_fractional(span, unit, &mut wtr);
1270            }
1271        }
1272        wtr.maybe_write_zero();
1273        wtr.maybe_write_suffix_sign();
1274    }
1275
1276    fn print_span_designators_non_fraction<'p, 'w, 'd>(
1277        &self,
1278        span: &Span,
1279        wtr: &mut DesignatorWriter<'p, 'w, 'd>,
1280    ) {
1281        let units = span.units();
1282
1283        if units.contains(Unit::Year) {
1284            wtr.write(Unit::Year, span.get_years_unsigned().into());
1285        }
1286        if units.contains(Unit::Month) {
1287            wtr.write(Unit::Month, span.get_months_unsigned().into());
1288        }
1289        if units.contains(Unit::Week) {
1290            wtr.write(Unit::Week, span.get_weeks_unsigned().into());
1291        }
1292        if units.contains(Unit::Day) {
1293            wtr.write(Unit::Day, span.get_days_unsigned().into());
1294        }
1295        if units.contains(Unit::Hour) {
1296            wtr.write(Unit::Hour, span.get_hours_unsigned().into());
1297        }
1298        if units.contains(Unit::Minute) {
1299            wtr.write(Unit::Minute, span.get_minutes_unsigned());
1300        }
1301        if units.contains(Unit::Second) {
1302            wtr.write(Unit::Second, span.get_seconds_unsigned());
1303        }
1304        if units.contains(Unit::Millisecond) {
1305            wtr.write(Unit::Millisecond, span.get_milliseconds_unsigned());
1306        }
1307        if units.contains(Unit::Microsecond) {
1308            wtr.write(Unit::Microsecond, span.get_microseconds_unsigned());
1309        }
1310        if units.contains(Unit::Nanosecond) {
1311            wtr.write(Unit::Nanosecond, span.get_nanoseconds_unsigned());
1312        }
1313    }
1314
1315    fn print_span_calendar_designators_non_fraction<'p, 'w, 'd>(
1316        &self,
1317        span: &Span,
1318        wtr: &mut DesignatorWriter<'p, 'w, 'd>,
1319    ) {
1320        let units = span.units();
1321
1322        if units.contains(Unit::Year) {
1323            wtr.write(Unit::Year, span.get_years_unsigned().into());
1324        }
1325        if units.contains(Unit::Month) {
1326            wtr.write(Unit::Month, span.get_months_unsigned().into());
1327        }
1328        if units.contains(Unit::Week) {
1329            wtr.write(Unit::Week, span.get_weeks_unsigned().into());
1330        }
1331        if units.contains(Unit::Day) {
1332            wtr.write(Unit::Day, span.get_days_unsigned().into());
1333        }
1334    }
1335
1336    #[inline(never)]
1337    fn print_span_designators_fractional<'p, 'w, 'd>(
1338        &self,
1339        span: &Span,
1340        unit: FractionalUnit,
1341        wtr: &mut DesignatorWriter<'p, 'w, 'd>,
1342    ) {
1343        // OK because the biggest FractionalUnit is Hour, and there is always
1344        // a Unit bigger than hour.
1345        let split_at = Unit::from(unit).next().unwrap();
1346        let non_fractional = span.without_lower(split_at);
1347        let fractional = span.only_lower(split_at);
1348        self.print_span_designators_non_fraction(&non_fractional, wtr);
1349        wtr.write_fractional_duration(
1350            unit,
1351            &fractional.to_invariant_duration().unsigned_abs(),
1352        );
1353    }
1354
1355    fn print_span_hms(&self, span: &Span, bbuf: &mut BorrowedBuffer<'_>) {
1356        let has_cal = !span.units().only_calendar().is_empty();
1357        let mut wtr =
1358            DesignatorWriter::new(self, bbuf, has_cal, span.signum());
1359        let span = span.abs();
1360
1361        wtr.maybe_write_prefix_sign();
1362        if has_cal {
1363            self.print_span_calendar_designators_non_fraction(&span, &mut wtr);
1364            wtr.finish_preceding();
1365            // When spacing is disabled, then `finish_preceding` won't write
1366            // any spaces. But this would result in, e.g., `1yr15:00:00`, which
1367            // is just totally wrong. So detect that case here and insert a
1368            // space forcefully.
1369            if matches!(self.spacing, Spacing::None) {
1370                wtr.bbuf.write_ascii_char(b' ');
1371            }
1372        }
1373
1374        let padding = self.padding.unwrap_or(2);
1375        wtr.bbuf.write_int_pad(span.get_hours_unsigned(), b'0', padding);
1376        wtr.bbuf.write_ascii_char(b':');
1377        wtr.bbuf.write_int_pad(span.get_minutes_unsigned(), b'0', padding);
1378        wtr.bbuf.write_ascii_char(b':');
1379        // You'd think we could do better here from a code size
1380        // perspective. But when I tried to inline the logic to
1381        // get a `SignedDuration` from just sub-minute units,
1382        // code size actually increased. ¯\_(ツ)_/¯
1383        let fp = FractionalPrinter::from_span_seconds(
1384            &span.only_lower(Unit::Minute),
1385            padding,
1386            self.precision,
1387        );
1388        fp.print(wtr.bbuf);
1389        wtr.maybe_write_suffix_sign();
1390    }
1391
1392    fn print_signed_duration_designators(
1393        &self,
1394        dur: &SignedDuration,
1395        bbuf: &mut BorrowedBuffer<'_>,
1396    ) {
1397        let mut wtr = DesignatorWriter::new(self, bbuf, false, dur.signum());
1398        wtr.maybe_write_prefix_sign();
1399        self.print_duration_designators(&dur.unsigned_abs(), &mut wtr);
1400        wtr.maybe_write_zero();
1401        wtr.maybe_write_suffix_sign();
1402    }
1403
1404    fn print_unsigned_duration_designators(
1405        &self,
1406        dur: &core::time::Duration,
1407        bbuf: &mut BorrowedBuffer<'_>,
1408    ) {
1409        let mut wtr = DesignatorWriter::new(self, bbuf, false, 1);
1410        wtr.maybe_write_prefix_sign();
1411        self.print_duration_designators(dur, &mut wtr);
1412        wtr.maybe_write_zero();
1413    }
1414
1415    fn print_duration_designators(
1416        &self,
1417        dur: &core::time::Duration,
1418        wtr: &mut DesignatorWriter<'_, '_, '_>,
1419    ) {
1420        match self.fractional {
1421            None => {
1422                let mut secs = dur.as_secs();
1423                wtr.write(Unit::Hour, secs / SECS_PER_HOUR);
1424                secs %= MINS_PER_HOUR * SECS_PER_MIN;
1425                wtr.write(Unit::Minute, secs / SECS_PER_MIN);
1426                wtr.write(Unit::Second, secs % SECS_PER_MIN);
1427                let mut nanos = dur.subsec_nanos();
1428                wtr.write(Unit::Millisecond, (nanos / NANOS_PER_MILLI).into());
1429                nanos %= NANOS_PER_MILLI;
1430                wtr.write(Unit::Microsecond, (nanos / NANOS_PER_MICRO).into());
1431                wtr.write(Unit::Nanosecond, (nanos % NANOS_PER_MICRO).into());
1432            }
1433            Some(FractionalUnit::Hour) => {
1434                wtr.write_fractional_duration(FractionalUnit::Hour, &dur);
1435            }
1436            Some(FractionalUnit::Minute) => {
1437                let mut secs = dur.as_secs();
1438                wtr.write(Unit::Hour, secs / SECS_PER_HOUR);
1439                secs %= MINS_PER_HOUR * SECS_PER_MIN;
1440
1441                let leftovers =
1442                    core::time::Duration::new(secs, dur.subsec_nanos());
1443                wtr.write_fractional_duration(
1444                    FractionalUnit::Minute,
1445                    &leftovers,
1446                );
1447            }
1448            Some(FractionalUnit::Second) => {
1449                let mut secs = dur.as_secs();
1450                wtr.write(Unit::Hour, secs / SECS_PER_HOUR);
1451                secs %= MINS_PER_HOUR * SECS_PER_MIN;
1452                wtr.write(Unit::Minute, secs / SECS_PER_MIN);
1453                secs %= SECS_PER_MIN;
1454
1455                let leftovers =
1456                    core::time::Duration::new(secs, dur.subsec_nanos());
1457                wtr.write_fractional_duration(
1458                    FractionalUnit::Second,
1459                    &leftovers,
1460                );
1461            }
1462            Some(FractionalUnit::Millisecond) => {
1463                let mut secs = dur.as_secs();
1464                wtr.write(Unit::Hour, secs / SECS_PER_HOUR);
1465                secs %= MINS_PER_HOUR * SECS_PER_MIN;
1466                wtr.write(Unit::Minute, secs / SECS_PER_MIN);
1467                wtr.write(Unit::Second, secs % SECS_PER_MIN);
1468
1469                let leftovers =
1470                    core::time::Duration::new(0, dur.subsec_nanos());
1471                wtr.write_fractional_duration(
1472                    FractionalUnit::Millisecond,
1473                    &leftovers,
1474                );
1475            }
1476            Some(FractionalUnit::Microsecond) => {
1477                let mut secs = dur.as_secs();
1478                wtr.write(Unit::Hour, secs / SECS_PER_HOUR);
1479                secs %= MINS_PER_HOUR * SECS_PER_MIN;
1480                wtr.write(Unit::Minute, secs / SECS_PER_MIN);
1481                wtr.write(Unit::Second, secs % SECS_PER_MIN);
1482                let mut nanos = dur.subsec_nanos();
1483                wtr.write(Unit::Millisecond, (nanos / NANOS_PER_MILLI).into());
1484                nanos %= NANOS_PER_MILLI;
1485
1486                let leftovers = core::time::Duration::new(0, nanos);
1487                wtr.write_fractional_duration(
1488                    FractionalUnit::Microsecond,
1489                    &leftovers,
1490                );
1491            }
1492        }
1493    }
1494
1495    fn print_signed_duration_hms(
1496        &self,
1497        dur: &SignedDuration,
1498        bbuf: &mut BorrowedBuffer<'_>,
1499    ) {
1500        if dur.is_negative() {
1501            if !matches!(self.direction, Direction::Suffix) {
1502                bbuf.write_ascii_char(b'-');
1503            }
1504        } else if let Direction::ForceSign = self.direction {
1505            bbuf.write_ascii_char(b'+');
1506        }
1507        self.print_duration_hms(&dur.unsigned_abs(), bbuf);
1508        if dur.is_negative() {
1509            if matches!(self.direction, Direction::Suffix) {
1510                bbuf.write_str(" ago");
1511            }
1512        }
1513    }
1514
1515    fn print_unsigned_duration_hms(
1516        &self,
1517        dur: &core::time::Duration,
1518        bbuf: &mut BorrowedBuffer<'_>,
1519    ) {
1520        if let Direction::ForceSign = self.direction {
1521            bbuf.write_ascii_char(b'+');
1522        }
1523        self.print_duration_hms(dur, bbuf);
1524    }
1525
1526    fn print_duration_hms(
1527        &self,
1528        udur: &core::time::Duration,
1529        bbuf: &mut BorrowedBuffer<'_>,
1530    ) {
1531        // N.B. It should be technically correct to convert a `SignedDuration`
1532        // (or `core::time::Duration`) to `Span` (since this process balances)
1533        // and then format the `Span` as-is. But this doesn't work because the
1534        // range of a `SignedDuration` (and `core::time::Duration`) is much
1535        // bigger.
1536
1537        let padding = self.padding.unwrap_or(2);
1538
1539        let mut secs = udur.as_secs();
1540        // OK because guaranteed to be bigger than i64::MIN.
1541        let hours = secs / (MINS_PER_HOUR * SECS_PER_MIN);
1542        secs %= MINS_PER_HOUR * SECS_PER_MIN;
1543        // OK because guaranteed to be bigger than i64::MIN.
1544        let minutes = secs / SECS_PER_MIN;
1545        // OK because guaranteed to be bigger than i64::MIN.
1546        secs = secs % SECS_PER_MIN;
1547
1548        bbuf.write_int_pad(hours, b'0', padding);
1549        bbuf.write_ascii_char(b':');
1550        bbuf.write_int_pad(minutes, b'0', padding);
1551        bbuf.write_ascii_char(b':');
1552        let fp = FractionalPrinter::from_duration_seconds(
1553            // OK because -999_999_999 <= nanos <= 999_999_999 and secs < 60.
1554            &core::time::Duration::new(secs, udur.subsec_nanos()),
1555            padding,
1556            self.precision,
1557        );
1558        fp.print(bbuf);
1559    }
1560}
1561
1562impl Default for SpanPrinter {
1563    fn default() -> SpanPrinter {
1564        SpanPrinter::new()
1565    }
1566}
1567
1568/// A type that represents the designator choice.
1569///
1570/// Basically, whether we want verbose, short or compact designators. This in
1571/// turn permits lookups based on `Unit`, which makes writing generic code for
1572/// writing designators a bit nicer and still fast.
1573#[derive(Clone, Debug)]
1574struct Designators {
1575    singular: [&'static str; 10],
1576    plural: [&'static str; 10],
1577}
1578
1579impl Designators {
1580    const VERBOSE_SINGULAR: [&'static str; 10] = [
1581        "nanosecond",
1582        "microsecond",
1583        "millisecond",
1584        "second",
1585        "minute",
1586        "hour",
1587        "day",
1588        "week",
1589        "month",
1590        "year",
1591    ];
1592    const VERBOSE_PLURAL: [&'static str; 10] = [
1593        "nanoseconds",
1594        "microseconds",
1595        "milliseconds",
1596        "seconds",
1597        "minutes",
1598        "hours",
1599        "days",
1600        "weeks",
1601        "months",
1602        "years",
1603    ];
1604
1605    const SHORT_SINGULAR: [&'static str; 10] =
1606        ["nsec", "µsec", "msec", "sec", "min", "hr", "day", "wk", "mo", "yr"];
1607    const SHORT_PLURAL: [&'static str; 10] = [
1608        "nsecs", "µsecs", "msecs", "secs", "mins", "hrs", "days", "wks",
1609        "mos", "yrs",
1610    ];
1611
1612    const COMPACT: [&'static str; 10] =
1613        ["ns", "µs", "ms", "s", "m", "h", "d", "w", "mo", "y"];
1614
1615    const HUMAN_TIME_SINGULAR: [&'static str; 10] =
1616        ["ns", "us", "ms", "s", "m", "h", "d", "w", "month", "y"];
1617    const HUMAN_TIME_PLURAL: [&'static str; 10] =
1618        ["ns", "us", "ms", "s", "m", "h", "d", "w", "months", "y"];
1619
1620    const fn new(config: Designator) -> &'static Designators {
1621        match config {
1622            Designator::Verbose => &Designators {
1623                singular: Designators::VERBOSE_SINGULAR,
1624                plural: Designators::VERBOSE_PLURAL,
1625            },
1626            Designator::Short => &Designators {
1627                singular: Designators::SHORT_SINGULAR,
1628                plural: Designators::SHORT_PLURAL,
1629            },
1630            Designator::Compact => &Designators {
1631                singular: Designators::COMPACT,
1632                plural: Designators::COMPACT,
1633            },
1634            Designator::HumanTime => &Designators {
1635                singular: Designators::HUMAN_TIME_SINGULAR,
1636                plural: Designators::HUMAN_TIME_PLURAL,
1637            },
1638        }
1639    }
1640
1641    fn designator(&self, unit: impl Into<Unit>, plural: bool) -> &'static str {
1642        let unit = unit.into();
1643        let index = unit as usize;
1644        if plural {
1645            self.plural[index]
1646        } else {
1647            self.singular[index]
1648        }
1649    }
1650}
1651
1652/// An abstraction for writing the "designator" variant of the friendly format.
1653///
1654/// This takes care of computing some initial state and keeping track of some
1655/// mutable state that influences printing. For example, whether to write a
1656/// delimiter or not (one should only come after a unit that has been written).
1657#[derive(Debug)]
1658struct DesignatorWriter<'p, 'w, 'd> {
1659    printer: &'p SpanPrinter,
1660    bbuf: &'w mut BorrowedBuffer<'d>,
1661    sign: Option<DirectionSign>,
1662    padding: u8,
1663    precision: Option<u8>,
1664    written_non_zero_unit: bool,
1665}
1666
1667impl<'p, 'w, 'd> DesignatorWriter<'p, 'w, 'd> {
1668    fn new(
1669        printer: &'p SpanPrinter,
1670        bbuf: &'w mut BorrowedBuffer<'d>,
1671        has_calendar: bool,
1672        signum: i8,
1673    ) -> DesignatorWriter<'p, 'w, 'd> {
1674        let sign = printer.direction.sign(printer, has_calendar, signum);
1675        DesignatorWriter {
1676            printer,
1677            bbuf,
1678            sign,
1679            padding: printer.padding.unwrap_or(0),
1680            precision: printer.precision,
1681            written_non_zero_unit: false,
1682        }
1683    }
1684
1685    fn maybe_write_prefix_sign(&mut self) {
1686        if let Some(DirectionSign::Prefix(sign)) = self.sign {
1687            self.bbuf.write_ascii_char(sign);
1688        }
1689    }
1690
1691    fn maybe_write_suffix_sign(&mut self) {
1692        if let Some(DirectionSign::Suffix) = self.sign {
1693            self.bbuf.write_str(" ago");
1694        }
1695    }
1696
1697    fn maybe_write_zero(&mut self) {
1698        #[cold]
1699        #[inline(never)]
1700        fn imp(wtr: &mut DesignatorWriter<'_, '_, '_>) {
1701            wtr.bbuf.write_int_pad(0u64, b'0', wtr.padding);
1702            if let Some(byte) =
1703                wtr.printer.spacing.between_units_and_designators()
1704            {
1705                wtr.bbuf.write_ascii_char(byte);
1706            }
1707            // If a fractional unit is set, then we should use that unit
1708            // specifically to express "zero."
1709            let unit = wtr
1710                .printer
1711                .fractional
1712                .map(Unit::from)
1713                .unwrap_or(wtr.printer.zero_unit);
1714            wtr.bbuf.write_str(wtr.printer.designators.designator(unit, true));
1715        }
1716
1717        if !self.written_non_zero_unit {
1718            imp(self);
1719        }
1720    }
1721
1722    #[inline(never)]
1723    fn write(&mut self, unit: Unit, value: u64) {
1724        if value == 0 {
1725            return;
1726        }
1727        self.finish_preceding();
1728        self.written_non_zero_unit = true;
1729        self.bbuf.write_int_pad0(value, self.padding);
1730        if let Some(byte) =
1731            self.printer.spacing.between_units_and_designators()
1732        {
1733            self.bbuf.write_ascii_char(byte);
1734        }
1735        self.bbuf
1736            .write_str(self.printer.designators.designator(unit, value != 1));
1737    }
1738
1739    fn write_fractional_duration(
1740        &mut self,
1741        unit: FractionalUnit,
1742        duration: &core::time::Duration,
1743    ) {
1744        let fp = FractionalPrinter::from_duration(
1745            duration,
1746            unit,
1747            self.padding,
1748            self.precision,
1749        );
1750        if !fp.must_write_digits() {
1751            return;
1752        }
1753        self.finish_preceding();
1754        self.written_non_zero_unit = true;
1755        fp.print(&mut *self.bbuf);
1756        if let Some(byte) =
1757            self.printer.spacing.between_units_and_designators()
1758        {
1759            self.bbuf.write_ascii_char(byte);
1760        }
1761        self.bbuf.write_str(
1762            self.printer.designators.designator(unit, fp.is_plural()),
1763        );
1764    }
1765
1766    fn finish_preceding(&mut self) {
1767        if self.written_non_zero_unit {
1768            if self.printer.comma_after_designator {
1769                self.bbuf.write_ascii_char(b',');
1770            }
1771            if let Some(byte) = self.printer.spacing.between_units() {
1772                self.bbuf.write_ascii_char(byte);
1773            }
1774        }
1775    }
1776}
1777
1778/// A printer for a fraction with an integer and fraction component.
1779///
1780/// This also includes the formatter for the integer component and the
1781/// formatter for the fractional component.
1782struct FractionalPrinter {
1783    integer: u64,
1784    fraction: u32,
1785    padding: u8,
1786    precision: Option<u8>,
1787}
1788
1789impl FractionalPrinter {
1790    /// Build a fractional printer for the `Span` given. This includes the `.`.
1791    ///
1792    /// Callers must ensure that all units greater than `FractionalUnit` are
1793    /// zero in the span given.
1794    ///
1795    /// Note that the printer returned only prints a fractional component
1796    /// if necessary. For example, if the fractional component is zero and
1797    /// precision is `None`, or if `precision` is `Some(0)`, then no fractional
1798    /// component will be emitted.
1799    fn from_span_seconds(
1800        span: &Span,
1801        padding: u8,
1802        precision: Option<u8>,
1803    ) -> FractionalPrinter {
1804        FractionalPrinter::from_duration_seconds(
1805            &span.to_invariant_duration().unsigned_abs(),
1806            padding,
1807            precision,
1808        )
1809    }
1810
1811    /// Like `from_span_seconds`, but for `SignedDuration`.
1812    fn from_duration_seconds(
1813        dur: &core::time::Duration,
1814        padding: u8,
1815        precision: Option<u8>,
1816    ) -> FractionalPrinter {
1817        let integer = dur.as_secs();
1818        let fraction = u32::from(dur.subsec_nanos());
1819        FractionalPrinter { integer, fraction, padding, precision }
1820    }
1821
1822    /// Like `from_duration_seconds`, but for any fractional unit.
1823    fn from_duration(
1824        dur: &core::time::Duration,
1825        unit: FractionalUnit,
1826        padding: u8,
1827        precision: Option<u8>,
1828    ) -> FractionalPrinter {
1829        match unit {
1830            FractionalUnit::Hour => {
1831                let integer = dur.as_secs() / SECS_PER_HOUR;
1832                let mut fraction = dur.as_nanos() % NANOS_PER_HOUR;
1833                // Drop precision since we're only allowed 9 decimal places.
1834                fraction /= u128::from(SECS_PER_HOUR);
1835                // OK because NANOS_PER_HOUR / SECS_PER_HOUR fits in a u32.
1836                let fraction = u32::try_from(fraction).unwrap();
1837                FractionalPrinter { integer, fraction, padding, precision }
1838            }
1839            FractionalUnit::Minute => {
1840                let integer = dur.as_secs() / SECS_PER_MIN;
1841                let mut fraction = dur.as_nanos() % NANOS_PER_MIN;
1842                // Drop precision since we're only allowed 9 decimal places.
1843                fraction /= u128::from(SECS_PER_MIN);
1844                // OK because NANOS_PER_MIN fits in an u32.
1845                let fraction = u32::try_from(fraction).unwrap();
1846                FractionalPrinter { integer, fraction, padding, precision }
1847            }
1848            FractionalUnit::Second => {
1849                let integer = dur.as_secs();
1850                let fraction = u32::from(dur.subsec_nanos());
1851                FractionalPrinter { integer, fraction, padding, precision }
1852            }
1853            FractionalUnit::Millisecond => {
1854                // Unwrap is OK, but this is subtle. For printing a
1855                // SignedDuration, as_millis() can never return anything
1856                // bigger than 1 second, because the duration given is reduced
1857                // in a balanced fashion before hitting this routine. But
1858                // for printing a Span, it can, since spans can be totally
1859                // unbalanced. But Spans have limits on their units such that
1860                // each will fit into an i64. So this is also okay in that case
1861                // too.
1862                let integer = u64::try_from(dur.as_millis()).unwrap();
1863                let fraction =
1864                    u32::from((dur.subsec_nanos() % NANOS_PER_MILLI) * 1_000);
1865                FractionalPrinter { integer, fraction, padding, precision }
1866            }
1867            FractionalUnit::Microsecond => {
1868                // Unwrap is OK, but this is subtle. For printing a
1869                // SignedDuration, as_millis() can never return anything
1870                // bigger than 1 second, because the duration given is reduced
1871                // in a balanced fashion before hitting this routine. But
1872                // for printing a Span, it can, since spans can be totally
1873                // unbalanced. But Spans have limits on their units such that
1874                // each will fit into an i64. So this is also okay in that case
1875                // too.
1876                let integer = u64::try_from(dur.as_micros()).unwrap();
1877                let fraction = u32::from(
1878                    (dur.subsec_nanos() % NANOS_PER_MICRO) * 1_000_000,
1879                );
1880                FractionalPrinter { integer, fraction, padding, precision }
1881            }
1882        }
1883    }
1884
1885    /// Returns true if both the integer and fractional component are zero.
1886    fn is_zero(&self) -> bool {
1887        self.integer == 0 && self.fraction == 0
1888    }
1889
1890    /// Returns true if this integer/fraction should be considered plural
1891    /// when choosing what designator to use.
1892    fn is_plural(&self) -> bool {
1893        self.integer != 1
1894            || (self.fraction != 0 && !self.has_zero_fixed_precision())
1895    }
1896
1897    /// Returns true if and only if this printer must write some kind of number
1898    /// when `print` is called.
1899    ///
1900    /// The only case where this returns `false` is when both the integer and
1901    /// fractional component are zero *and* the precision is fixed to a number
1902    /// greater than zero.
1903    fn must_write_digits(&self) -> bool {
1904        !self.is_zero() || self.has_non_zero_fixed_precision()
1905    }
1906
1907    /// Returns true if and only if at least one digit will be written for the
1908    /// given value.
1909    ///
1910    /// This is useful for callers that need to know whether to write
1911    /// a decimal separator, e.g., `.`, before the digits.
1912    fn will_write_digits(&self) -> bool {
1913        self.precision.map_or_else(|| self.fraction != 0, |p| p > 0)
1914    }
1915
1916    /// Returns true if and only if this formatter has an explicit non-zero
1917    /// precision setting.
1918    ///
1919    /// This is useful for determining whether something like `0.000` needs to
1920    /// be written in the case of a `precision=Some(3)` setting and a zero
1921    /// value.
1922    fn has_non_zero_fixed_precision(&self) -> bool {
1923        self.precision.map_or(false, |p| p > 0)
1924    }
1925
1926    /// Returns true if and only if this formatter has fixed zero precision.
1927    /// That is, no matter what is given as input, a fraction is never written.
1928    fn has_zero_fixed_precision(&self) -> bool {
1929        self.precision.map_or(false, |p| p == 0)
1930    }
1931
1932    /// Prints the integer and optional fractional component.
1933    ///
1934    /// This will always print the integer, even if it's zero. Therefore, if
1935    /// the caller wants to omit printing zero, the caller should do their own
1936    /// conditional logic.
1937    fn print(&self, bbuf: &mut BorrowedBuffer<'_>) {
1938        bbuf.write_int_pad(self.integer, b'0', self.padding);
1939        if self.will_write_digits() {
1940            bbuf.write_ascii_char(b'.');
1941            bbuf.write_fraction(self.precision, self.fraction);
1942        }
1943    }
1944}
1945
1946#[cfg(feature = "alloc")]
1947#[cfg(test)]
1948mod tests {
1949    use crate::ToSpan;
1950
1951    use super::*;
1952
1953    #[test]
1954    fn print_span_designator_default() {
1955        let printer = || SpanPrinter::new();
1956        let p = |span| printer().span_to_string(&span);
1957
1958        insta::assert_snapshot!(p(1.second()), @"1s");
1959        insta::assert_snapshot!(p(2.seconds()), @"2s");
1960        insta::assert_snapshot!(p(10.seconds()), @"10s");
1961        insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m 40s");
1962
1963        insta::assert_snapshot!(p(1.minute()), @"1m");
1964        insta::assert_snapshot!(p(2.minutes()), @"2m");
1965        insta::assert_snapshot!(p(10.minutes()), @"10m");
1966        insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h 40m");
1967
1968        insta::assert_snapshot!(p(1.hour()), @"1h");
1969        insta::assert_snapshot!(p(2.hours()), @"2h");
1970        insta::assert_snapshot!(p(10.hours()), @"10h");
1971        insta::assert_snapshot!(p(100.hours()), @"100h");
1972
1973        insta::assert_snapshot!(
1974            p(1.hour().minutes(1).seconds(1)),
1975            @"1h 1m 1s",
1976        );
1977        insta::assert_snapshot!(
1978            p(2.hours().minutes(2).seconds(2)),
1979            @"2h 2m 2s",
1980        );
1981        insta::assert_snapshot!(
1982            p(10.hours().minutes(10).seconds(10)),
1983            @"10h 10m 10s",
1984        );
1985        insta::assert_snapshot!(
1986            p(100.hours().minutes(100).seconds(100)),
1987            @"100h 100m 100s",
1988        );
1989
1990        insta::assert_snapshot!(p(-1.hour()), @"1h ago");
1991        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1h 30s ago");
1992
1993        insta::assert_snapshot!(
1994            p(1.second().milliseconds(2000)),
1995            @"1s 2000ms",
1996        );
1997    }
1998
1999    #[test]
2000    fn print_span_designator_verbose() {
2001        let printer = || SpanPrinter::new().designator(Designator::Verbose);
2002        let p = |span| printer().span_to_string(&span);
2003
2004        insta::assert_snapshot!(p(1.second()), @"1second");
2005        insta::assert_snapshot!(p(2.seconds()), @"2seconds");
2006        insta::assert_snapshot!(p(10.seconds()), @"10seconds");
2007        insta::assert_snapshot!(p(1.minute().seconds(40)), @"1minute 40seconds");
2008
2009        insta::assert_snapshot!(p(1.minute()), @"1minute");
2010        insta::assert_snapshot!(p(2.minutes()), @"2minutes");
2011        insta::assert_snapshot!(p(10.minutes()), @"10minutes");
2012        insta::assert_snapshot!(p(1.hour().minutes(40)), @"1hour 40minutes");
2013
2014        insta::assert_snapshot!(p(1.hour()), @"1hour");
2015        insta::assert_snapshot!(p(2.hours()), @"2hours");
2016        insta::assert_snapshot!(p(10.hours()), @"10hours");
2017        insta::assert_snapshot!(p(100.hours()), @"100hours");
2018
2019        insta::assert_snapshot!(
2020            p(1.hour().minutes(1).seconds(1)),
2021            @"1hour 1minute 1second",
2022        );
2023        insta::assert_snapshot!(
2024            p(2.hours().minutes(2).seconds(2)),
2025            @"2hours 2minutes 2seconds",
2026        );
2027        insta::assert_snapshot!(
2028            p(10.hours().minutes(10).seconds(10)),
2029            @"10hours 10minutes 10seconds",
2030        );
2031        insta::assert_snapshot!(
2032            p(100.hours().minutes(100).seconds(100)),
2033            @"100hours 100minutes 100seconds",
2034        );
2035
2036        insta::assert_snapshot!(p(-1.hour()), @"1hour ago");
2037        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1hour 30seconds ago");
2038    }
2039
2040    #[test]
2041    fn print_span_designator_short() {
2042        let printer = || SpanPrinter::new().designator(Designator::Short);
2043        let p = |span| printer().span_to_string(&span);
2044
2045        insta::assert_snapshot!(p(1.second()), @"1sec");
2046        insta::assert_snapshot!(p(2.seconds()), @"2secs");
2047        insta::assert_snapshot!(p(10.seconds()), @"10secs");
2048        insta::assert_snapshot!(p(1.minute().seconds(40)), @"1min 40secs");
2049
2050        insta::assert_snapshot!(p(1.minute()), @"1min");
2051        insta::assert_snapshot!(p(2.minutes()), @"2mins");
2052        insta::assert_snapshot!(p(10.minutes()), @"10mins");
2053        insta::assert_snapshot!(p(1.hour().minutes(40)), @"1hr 40mins");
2054
2055        insta::assert_snapshot!(p(1.hour()), @"1hr");
2056        insta::assert_snapshot!(p(2.hours()), @"2hrs");
2057        insta::assert_snapshot!(p(10.hours()), @"10hrs");
2058        insta::assert_snapshot!(p(100.hours()), @"100hrs");
2059
2060        insta::assert_snapshot!(
2061            p(1.hour().minutes(1).seconds(1)),
2062            @"1hr 1min 1sec",
2063        );
2064        insta::assert_snapshot!(
2065            p(2.hours().minutes(2).seconds(2)),
2066            @"2hrs 2mins 2secs",
2067        );
2068        insta::assert_snapshot!(
2069            p(10.hours().minutes(10).seconds(10)),
2070            @"10hrs 10mins 10secs",
2071        );
2072        insta::assert_snapshot!(
2073            p(100.hours().minutes(100).seconds(100)),
2074            @"100hrs 100mins 100secs",
2075        );
2076
2077        insta::assert_snapshot!(p(-1.hour()), @"1hr ago");
2078        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1hr 30secs ago");
2079    }
2080
2081    #[test]
2082    fn print_span_designator_compact() {
2083        let printer = || SpanPrinter::new().designator(Designator::Compact);
2084        let p = |span| printer().span_to_string(&span);
2085
2086        insta::assert_snapshot!(p(1.second()), @"1s");
2087        insta::assert_snapshot!(p(2.seconds()), @"2s");
2088        insta::assert_snapshot!(p(10.seconds()), @"10s");
2089        insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m 40s");
2090
2091        insta::assert_snapshot!(p(1.minute()), @"1m");
2092        insta::assert_snapshot!(p(2.minutes()), @"2m");
2093        insta::assert_snapshot!(p(10.minutes()), @"10m");
2094        insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h 40m");
2095
2096        insta::assert_snapshot!(p(1.hour()), @"1h");
2097        insta::assert_snapshot!(p(2.hours()), @"2h");
2098        insta::assert_snapshot!(p(10.hours()), @"10h");
2099        insta::assert_snapshot!(p(100.hours()), @"100h");
2100
2101        insta::assert_snapshot!(
2102            p(1.hour().minutes(1).seconds(1)),
2103            @"1h 1m 1s",
2104        );
2105        insta::assert_snapshot!(
2106            p(2.hours().minutes(2).seconds(2)),
2107            @"2h 2m 2s",
2108        );
2109        insta::assert_snapshot!(
2110            p(10.hours().minutes(10).seconds(10)),
2111            @"10h 10m 10s",
2112        );
2113        insta::assert_snapshot!(
2114            p(100.hours().minutes(100).seconds(100)),
2115            @"100h 100m 100s",
2116        );
2117
2118        insta::assert_snapshot!(p(-1.hour()), @"1h ago");
2119        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1h 30s ago");
2120    }
2121
2122    #[test]
2123    fn print_span_designator_direction_force() {
2124        let printer = || SpanPrinter::new().direction(Direction::ForceSign);
2125        let p = |span| printer().span_to_string(&span);
2126
2127        insta::assert_snapshot!(p(1.second()), @"+1s");
2128        insta::assert_snapshot!(p(2.seconds()), @"+2s");
2129        insta::assert_snapshot!(p(10.seconds()), @"+10s");
2130        insta::assert_snapshot!(p(1.minute().seconds(40)), @"+1m 40s");
2131
2132        insta::assert_snapshot!(p(1.minute()), @"+1m");
2133        insta::assert_snapshot!(p(2.minutes()), @"+2m");
2134        insta::assert_snapshot!(p(10.minutes()), @"+10m");
2135        insta::assert_snapshot!(p(1.hour().minutes(40)), @"+1h 40m");
2136
2137        insta::assert_snapshot!(p(1.hour()), @"+1h");
2138        insta::assert_snapshot!(p(2.hours()), @"+2h");
2139        insta::assert_snapshot!(p(10.hours()), @"+10h");
2140        insta::assert_snapshot!(p(100.hours()), @"+100h");
2141
2142        insta::assert_snapshot!(
2143            p(1.hour().minutes(1).seconds(1)),
2144            @"+1h 1m 1s",
2145        );
2146        insta::assert_snapshot!(
2147            p(2.hours().minutes(2).seconds(2)),
2148            @"+2h 2m 2s",
2149        );
2150        insta::assert_snapshot!(
2151            p(10.hours().minutes(10).seconds(10)),
2152            @"+10h 10m 10s",
2153        );
2154        insta::assert_snapshot!(
2155            p(100.hours().minutes(100).seconds(100)),
2156            @"+100h 100m 100s",
2157        );
2158
2159        insta::assert_snapshot!(p(-1.hour()), @"-1h");
2160        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"-1h 30s");
2161    }
2162
2163    #[test]
2164    fn print_span_designator_padding() {
2165        let printer = || SpanPrinter::new().padding(2);
2166        let p = |span| printer().span_to_string(&span);
2167
2168        insta::assert_snapshot!(p(1.second()), @"01s");
2169        insta::assert_snapshot!(p(2.seconds()), @"02s");
2170        insta::assert_snapshot!(p(10.seconds()), @"10s");
2171        insta::assert_snapshot!(p(1.minute().seconds(40)), @"01m 40s");
2172
2173        insta::assert_snapshot!(p(1.minute()), @"01m");
2174        insta::assert_snapshot!(p(2.minutes()), @"02m");
2175        insta::assert_snapshot!(p(10.minutes()), @"10m");
2176        insta::assert_snapshot!(p(1.hour().minutes(40)), @"01h 40m");
2177
2178        insta::assert_snapshot!(p(1.hour()), @"01h");
2179        insta::assert_snapshot!(p(2.hours()), @"02h");
2180        insta::assert_snapshot!(p(10.hours()), @"10h");
2181        insta::assert_snapshot!(p(100.hours()), @"100h");
2182
2183        insta::assert_snapshot!(
2184            p(1.hour().minutes(1).seconds(1)),
2185            @"01h 01m 01s",
2186        );
2187        insta::assert_snapshot!(
2188            p(2.hours().minutes(2).seconds(2)),
2189            @"02h 02m 02s",
2190        );
2191        insta::assert_snapshot!(
2192            p(10.hours().minutes(10).seconds(10)),
2193            @"10h 10m 10s",
2194        );
2195        insta::assert_snapshot!(
2196            p(100.hours().minutes(100).seconds(100)),
2197            @"100h 100m 100s",
2198        );
2199
2200        insta::assert_snapshot!(p(-1.hour()), @"01h ago");
2201        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"01h 30s ago");
2202    }
2203
2204    #[test]
2205    fn print_span_designator_spacing_none() {
2206        let printer = || SpanPrinter::new().spacing(Spacing::None);
2207        let p = |span| printer().span_to_string(&span);
2208
2209        insta::assert_snapshot!(p(1.second()), @"1s");
2210        insta::assert_snapshot!(p(2.seconds()), @"2s");
2211        insta::assert_snapshot!(p(10.seconds()), @"10s");
2212        insta::assert_snapshot!(p(1.minute().seconds(40)), @"1m40s");
2213
2214        insta::assert_snapshot!(p(1.minute()), @"1m");
2215        insta::assert_snapshot!(p(2.minutes()), @"2m");
2216        insta::assert_snapshot!(p(10.minutes()), @"10m");
2217        insta::assert_snapshot!(p(1.hour().minutes(40)), @"1h40m");
2218
2219        insta::assert_snapshot!(p(1.hour()), @"1h");
2220        insta::assert_snapshot!(p(2.hours()), @"2h");
2221        insta::assert_snapshot!(p(10.hours()), @"10h");
2222        insta::assert_snapshot!(p(100.hours()), @"100h");
2223
2224        insta::assert_snapshot!(
2225            p(1.hour().minutes(1).seconds(1)),
2226            @"1h1m1s",
2227        );
2228        insta::assert_snapshot!(
2229            p(2.hours().minutes(2).seconds(2)),
2230            @"2h2m2s",
2231        );
2232        insta::assert_snapshot!(
2233            p(10.hours().minutes(10).seconds(10)),
2234            @"10h10m10s",
2235        );
2236        insta::assert_snapshot!(
2237            p(100.hours().minutes(100).seconds(100)),
2238            @"100h100m100s",
2239        );
2240
2241        insta::assert_snapshot!(p(-1.hour()), @"-1h");
2242        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"-1h30s");
2243    }
2244
2245    #[test]
2246    fn print_span_designator_spacing_more() {
2247        let printer =
2248            || SpanPrinter::new().spacing(Spacing::BetweenUnitsAndDesignators);
2249        let p = |span| printer().span_to_string(&span);
2250
2251        insta::assert_snapshot!(p(1.second()), @"1 s");
2252        insta::assert_snapshot!(p(2.seconds()), @"2 s");
2253        insta::assert_snapshot!(p(10.seconds()), @"10 s");
2254        insta::assert_snapshot!(p(1.minute().seconds(40)), @"1 m 40 s");
2255
2256        insta::assert_snapshot!(p(1.minute()), @"1 m");
2257        insta::assert_snapshot!(p(2.minutes()), @"2 m");
2258        insta::assert_snapshot!(p(10.minutes()), @"10 m");
2259        insta::assert_snapshot!(p(1.hour().minutes(40)), @"1 h 40 m");
2260
2261        insta::assert_snapshot!(p(1.hour()), @"1 h");
2262        insta::assert_snapshot!(p(2.hours()), @"2 h");
2263        insta::assert_snapshot!(p(10.hours()), @"10 h");
2264        insta::assert_snapshot!(p(100.hours()), @"100 h");
2265
2266        insta::assert_snapshot!(
2267            p(1.hour().minutes(1).seconds(1)),
2268            @"1 h 1 m 1 s",
2269        );
2270        insta::assert_snapshot!(
2271            p(2.hours().minutes(2).seconds(2)),
2272            @"2 h 2 m 2 s",
2273        );
2274        insta::assert_snapshot!(
2275            p(10.hours().minutes(10).seconds(10)),
2276            @"10 h 10 m 10 s",
2277        );
2278        insta::assert_snapshot!(
2279            p(100.hours().minutes(100).seconds(100)),
2280            @"100 h 100 m 100 s",
2281        );
2282
2283        insta::assert_snapshot!(p(-1.hour()), @"1 h ago");
2284        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1 h 30 s ago");
2285    }
2286
2287    #[test]
2288    fn print_span_designator_spacing_comma() {
2289        let printer = || {
2290            SpanPrinter::new()
2291                .comma_after_designator(true)
2292                .spacing(Spacing::BetweenUnitsAndDesignators)
2293        };
2294        let p = |span| printer().span_to_string(&span);
2295
2296        insta::assert_snapshot!(p(1.second()), @"1 s");
2297        insta::assert_snapshot!(p(2.seconds()), @"2 s");
2298        insta::assert_snapshot!(p(10.seconds()), @"10 s");
2299        insta::assert_snapshot!(p(1.minute().seconds(40)), @"1 m, 40 s");
2300
2301        insta::assert_snapshot!(p(1.minute()), @"1 m");
2302        insta::assert_snapshot!(p(2.minutes()), @"2 m");
2303        insta::assert_snapshot!(p(10.minutes()), @"10 m");
2304        insta::assert_snapshot!(p(1.hour().minutes(40)), @"1 h, 40 m");
2305
2306        insta::assert_snapshot!(p(1.hour()), @"1 h");
2307        insta::assert_snapshot!(p(2.hours()), @"2 h");
2308        insta::assert_snapshot!(p(10.hours()), @"10 h");
2309        insta::assert_snapshot!(p(100.hours()), @"100 h");
2310
2311        insta::assert_snapshot!(
2312            p(1.hour().minutes(1).seconds(1)),
2313            @"1 h, 1 m, 1 s",
2314        );
2315        insta::assert_snapshot!(
2316            p(2.hours().minutes(2).seconds(2)),
2317            @"2 h, 2 m, 2 s",
2318        );
2319        insta::assert_snapshot!(
2320            p(10.hours().minutes(10).seconds(10)),
2321            @"10 h, 10 m, 10 s",
2322        );
2323        insta::assert_snapshot!(
2324            p(100.hours().minutes(100).seconds(100)),
2325            @"100 h, 100 m, 100 s",
2326        );
2327
2328        insta::assert_snapshot!(p(-1.hour()), @"1 h ago");
2329        insta::assert_snapshot!(p(-1.hour().seconds(30)), @"1 h, 30 s ago");
2330    }
2331
2332    #[test]
2333    fn print_span_designator_fractional_hour() {
2334        let printer =
2335            || SpanPrinter::new().fractional(Some(FractionalUnit::Hour));
2336        let p = |span| printer().span_to_string(&span);
2337        let pp = |precision, span| {
2338            printer().precision(Some(precision)).span_to_string(&span)
2339        };
2340
2341        insta::assert_snapshot!(p(1.hour()), @"1h");
2342        insta::assert_snapshot!(pp(0, 1.hour()), @"1h");
2343        insta::assert_snapshot!(pp(1, 1.hour()), @"1.0h");
2344        insta::assert_snapshot!(pp(2, 1.hour()), @"1.00h");
2345
2346        insta::assert_snapshot!(p(1.hour().minutes(30)), @"1.5h");
2347        insta::assert_snapshot!(pp(0, 1.hour().minutes(30)), @"1h");
2348        insta::assert_snapshot!(pp(1, 1.hour().minutes(30)), @"1.5h");
2349        insta::assert_snapshot!(pp(2, 1.hour().minutes(30)), @"1.50h");
2350
2351        insta::assert_snapshot!(p(1.hour().minutes(3)), @"1.05h");
2352        insta::assert_snapshot!(p(1.hour().minutes(3).nanoseconds(1)), @"1.05h");
2353        insta::assert_snapshot!(p(1.second()), @"0.000277777h");
2354        // precision loss!
2355        insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"0.000277777h");
2356        insta::assert_snapshot!(p(0.seconds()), @"0h");
2357        // precision loss!
2358        insta::assert_snapshot!(p(1.nanosecond()), @"0h");
2359    }
2360
2361    #[test]
2362    fn print_span_designator_fractional_minute() {
2363        let printer =
2364            || SpanPrinter::new().fractional(Some(FractionalUnit::Minute));
2365        let p = |span| printer().span_to_string(&span);
2366        let pp = |precision, span| {
2367            printer().precision(Some(precision)).span_to_string(&span)
2368        };
2369
2370        insta::assert_snapshot!(p(1.hour()), @"1h");
2371        insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2372
2373        insta::assert_snapshot!(p(1.minute()), @"1m");
2374        insta::assert_snapshot!(pp(0, 1.minute()), @"1m");
2375        insta::assert_snapshot!(pp(1, 1.minute()), @"1.0m");
2376        insta::assert_snapshot!(pp(2, 1.minute()), @"1.00m");
2377
2378        insta::assert_snapshot!(p(1.minute().seconds(30)), @"1.5m");
2379        insta::assert_snapshot!(pp(0, 1.minute().seconds(30)), @"1m");
2380        insta::assert_snapshot!(pp(1, 1.minute().seconds(30)), @"1.5m");
2381        insta::assert_snapshot!(pp(2, 1.minute().seconds(30)), @"1.50m");
2382
2383        insta::assert_snapshot!(p(1.hour().nanoseconds(1)), @"1h");
2384        insta::assert_snapshot!(p(1.minute().seconds(3)), @"1.05m");
2385        insta::assert_snapshot!(p(1.minute().seconds(3).nanoseconds(1)), @"1.05m");
2386        insta::assert_snapshot!(p(1.second()), @"0.016666666m");
2387        // precision loss!
2388        insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"0.016666666m");
2389        insta::assert_snapshot!(p(0.seconds()), @"0m");
2390        // precision loss!
2391        insta::assert_snapshot!(p(1.nanosecond()), @"0m");
2392    }
2393
2394    #[test]
2395    fn print_span_designator_fractional_second() {
2396        let printer =
2397            || SpanPrinter::new().fractional(Some(FractionalUnit::Second));
2398        let p = |span| printer().span_to_string(&span);
2399        let pp = |precision, span| {
2400            printer().precision(Some(precision)).span_to_string(&span)
2401        };
2402
2403        insta::assert_snapshot!(p(1.hour()), @"1h");
2404        insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2405
2406        insta::assert_snapshot!(p(1.second()), @"1s");
2407        insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2408        insta::assert_snapshot!(pp(1, 1.second()), @"1.0s");
2409        insta::assert_snapshot!(pp(2, 1.second()), @"1.00s");
2410
2411        insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1.5s");
2412        insta::assert_snapshot!(pp(0, 1.second().milliseconds(500)), @"1s");
2413        insta::assert_snapshot!(pp(1, 1.second().milliseconds(500)), @"1.5s");
2414        insta::assert_snapshot!(pp(2, 1.second().milliseconds(500)), @"1.50s");
2415
2416        insta::assert_snapshot!(p(1.second().nanoseconds(1)), @"1.000000001s");
2417        insta::assert_snapshot!(p(1.nanosecond()), @"0.000000001s");
2418        insta::assert_snapshot!(p(0.seconds()), @"0s");
2419
2420        insta::assert_snapshot!(p(1.second().milliseconds(2000)), @"3s");
2421    }
2422
2423    #[test]
2424    fn print_span_designator_fractional_millisecond() {
2425        let printer = || {
2426            SpanPrinter::new().fractional(Some(FractionalUnit::Millisecond))
2427        };
2428        let p = |span| printer().span_to_string(&span);
2429        let pp = |precision, span| {
2430            printer().precision(Some(precision)).span_to_string(&span)
2431        };
2432
2433        insta::assert_snapshot!(p(1.hour()), @"1h");
2434        insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2435        insta::assert_snapshot!(
2436            p(1.hour().minutes(30).seconds(10)),
2437            @"1h 30m 10s",
2438        );
2439
2440        insta::assert_snapshot!(p(1.second()), @"1s");
2441        insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2442        insta::assert_snapshot!(pp(1, 1.second()), @"1s 0.0ms");
2443        insta::assert_snapshot!(pp(2, 1.second()), @"1s 0.00ms");
2444
2445        insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1s 500ms");
2446        insta::assert_snapshot!(
2447            pp(0, 1.second().milliseconds(1).microseconds(500)),
2448            @"1s 1ms",
2449        );
2450        insta::assert_snapshot!(
2451            pp(1, 1.second().milliseconds(1).microseconds(500)),
2452            @"1s 1.5ms",
2453        );
2454        insta::assert_snapshot!(
2455            pp(2, 1.second().milliseconds(1).microseconds(500)),
2456            @"1s 1.50ms",
2457        );
2458
2459        insta::assert_snapshot!(p(1.millisecond().nanoseconds(1)), @"1.000001ms");
2460        insta::assert_snapshot!(p(1.nanosecond()), @"0.000001ms");
2461        insta::assert_snapshot!(p(0.seconds()), @"0ms");
2462    }
2463
2464    #[test]
2465    fn print_span_designator_fractional_microsecond() {
2466        let printer = || {
2467            SpanPrinter::new().fractional(Some(FractionalUnit::Microsecond))
2468        };
2469        let p = |span| printer().span_to_string(&span);
2470        let pp = |precision, span| {
2471            printer().precision(Some(precision)).span_to_string(&span)
2472        };
2473
2474        insta::assert_snapshot!(p(1.hour()), @"1h");
2475        insta::assert_snapshot!(p(1.hour().minutes(30)), @"1h 30m");
2476        insta::assert_snapshot!(
2477            p(1.hour().minutes(30).seconds(10)),
2478            @"1h 30m 10s",
2479        );
2480
2481        insta::assert_snapshot!(p(1.second()), @"1s");
2482        insta::assert_snapshot!(pp(0, 1.second()), @"1s");
2483        insta::assert_snapshot!(pp(1, 1.second()), @"1s 0.0µs");
2484        insta::assert_snapshot!(pp(2, 1.second()), @"1s 0.00µs");
2485
2486        insta::assert_snapshot!(p(1.second().milliseconds(500)), @"1s 500ms");
2487        insta::assert_snapshot!(
2488            pp(0, 1.second().milliseconds(1).microseconds(500)),
2489            @"1s 1ms 500µs",
2490        );
2491        insta::assert_snapshot!(
2492            pp(1, 1.second().milliseconds(1).microseconds(500)),
2493            @"1s 1ms 500.0µs",
2494        );
2495        insta::assert_snapshot!(
2496            pp(2, 1.second().milliseconds(1).microseconds(500)),
2497            @"1s 1ms 500.00µs",
2498        );
2499
2500        insta::assert_snapshot!(
2501            p(1.millisecond().nanoseconds(1)),
2502            @"1ms 0.001µs",
2503        );
2504        insta::assert_snapshot!(p(1.nanosecond()), @"0.001µs");
2505        insta::assert_snapshot!(p(0.second()), @"0µs");
2506    }
2507
2508    #[test]
2509    fn print_signed_duration_designator_default() {
2510        let printer = || SpanPrinter::new();
2511        let p = |secs| {
2512            printer().duration_to_string(&SignedDuration::from_secs(secs))
2513        };
2514
2515        insta::assert_snapshot!(p(1), @"1s");
2516        insta::assert_snapshot!(p(2), @"2s");
2517        insta::assert_snapshot!(p(10), @"10s");
2518        insta::assert_snapshot!(p(100), @"1m 40s");
2519
2520        insta::assert_snapshot!(p(1 * 60), @"1m");
2521        insta::assert_snapshot!(p(2 * 60), @"2m");
2522        insta::assert_snapshot!(p(10 * 60), @"10m");
2523        insta::assert_snapshot!(p(100 * 60), @"1h 40m");
2524
2525        insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2526        insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2527        insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2528        insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2529
2530        insta::assert_snapshot!(
2531            p(60 * 60 + 60 + 1),
2532            @"1h 1m 1s",
2533        );
2534        insta::assert_snapshot!(
2535            p(2 * 60 * 60 + 2 * 60 + 2),
2536            @"2h 2m 2s",
2537        );
2538        insta::assert_snapshot!(
2539            p(10 * 60 * 60 + 10 * 60 + 10),
2540            @"10h 10m 10s",
2541        );
2542        insta::assert_snapshot!(
2543            p(100 * 60 * 60 + 100 * 60 + 100),
2544            @"101h 41m 40s",
2545        );
2546
2547        insta::assert_snapshot!(p(-1 * 60 * 60), @"1h ago");
2548        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1h 30s ago");
2549    }
2550
2551    #[test]
2552    fn print_signed_duration_designator_verbose() {
2553        let printer = || SpanPrinter::new().designator(Designator::Verbose);
2554        let p = |secs| {
2555            printer().duration_to_string(&SignedDuration::from_secs(secs))
2556        };
2557
2558        insta::assert_snapshot!(p(1), @"1second");
2559        insta::assert_snapshot!(p(2), @"2seconds");
2560        insta::assert_snapshot!(p(10), @"10seconds");
2561        insta::assert_snapshot!(p(100), @"1minute 40seconds");
2562
2563        insta::assert_snapshot!(p(1 * 60), @"1minute");
2564        insta::assert_snapshot!(p(2 * 60), @"2minutes");
2565        insta::assert_snapshot!(p(10 * 60), @"10minutes");
2566        insta::assert_snapshot!(p(100 * 60), @"1hour 40minutes");
2567
2568        insta::assert_snapshot!(p(1 * 60 * 60), @"1hour");
2569        insta::assert_snapshot!(p(2 * 60 * 60), @"2hours");
2570        insta::assert_snapshot!(p(10 * 60 * 60), @"10hours");
2571        insta::assert_snapshot!(p(100 * 60 * 60), @"100hours");
2572
2573        insta::assert_snapshot!(
2574            p(60 * 60 + 60 + 1),
2575            @"1hour 1minute 1second",
2576        );
2577        insta::assert_snapshot!(
2578            p(2 * 60 * 60 + 2 * 60 + 2),
2579            @"2hours 2minutes 2seconds",
2580        );
2581        insta::assert_snapshot!(
2582            p(10 * 60 * 60 + 10 * 60 + 10),
2583            @"10hours 10minutes 10seconds",
2584        );
2585        insta::assert_snapshot!(
2586            p(100 * 60 * 60 + 100 * 60 + 100),
2587            @"101hours 41minutes 40seconds",
2588        );
2589
2590        insta::assert_snapshot!(p(-1 * 60 * 60), @"1hour ago");
2591        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1hour 30seconds ago");
2592    }
2593
2594    #[test]
2595    fn print_signed_duration_designator_short() {
2596        let printer = || SpanPrinter::new().designator(Designator::Short);
2597        let p = |secs| {
2598            printer().duration_to_string(&SignedDuration::from_secs(secs))
2599        };
2600
2601        insta::assert_snapshot!(p(1), @"1sec");
2602        insta::assert_snapshot!(p(2), @"2secs");
2603        insta::assert_snapshot!(p(10), @"10secs");
2604        insta::assert_snapshot!(p(100), @"1min 40secs");
2605
2606        insta::assert_snapshot!(p(1 * 60), @"1min");
2607        insta::assert_snapshot!(p(2 * 60), @"2mins");
2608        insta::assert_snapshot!(p(10 * 60), @"10mins");
2609        insta::assert_snapshot!(p(100 * 60), @"1hr 40mins");
2610
2611        insta::assert_snapshot!(p(1 * 60 * 60), @"1hr");
2612        insta::assert_snapshot!(p(2 * 60 * 60), @"2hrs");
2613        insta::assert_snapshot!(p(10 * 60 * 60), @"10hrs");
2614        insta::assert_snapshot!(p(100 * 60 * 60), @"100hrs");
2615
2616        insta::assert_snapshot!(
2617            p(60 * 60 + 60 + 1),
2618            @"1hr 1min 1sec",
2619        );
2620        insta::assert_snapshot!(
2621            p(2 * 60 * 60 + 2 * 60 + 2),
2622            @"2hrs 2mins 2secs",
2623        );
2624        insta::assert_snapshot!(
2625            p(10 * 60 * 60 + 10 * 60 + 10),
2626            @"10hrs 10mins 10secs",
2627        );
2628        insta::assert_snapshot!(
2629            p(100 * 60 * 60 + 100 * 60 + 100),
2630            @"101hrs 41mins 40secs",
2631        );
2632
2633        insta::assert_snapshot!(p(-1 * 60 * 60), @"1hr ago");
2634        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1hr 30secs ago");
2635    }
2636
2637    #[test]
2638    fn print_signed_duration_designator_compact() {
2639        let printer = || SpanPrinter::new().designator(Designator::Compact);
2640        let p = |secs| {
2641            printer().duration_to_string(&SignedDuration::from_secs(secs))
2642        };
2643
2644        insta::assert_snapshot!(p(1), @"1s");
2645        insta::assert_snapshot!(p(2), @"2s");
2646        insta::assert_snapshot!(p(10), @"10s");
2647        insta::assert_snapshot!(p(100), @"1m 40s");
2648
2649        insta::assert_snapshot!(p(1 * 60), @"1m");
2650        insta::assert_snapshot!(p(2 * 60), @"2m");
2651        insta::assert_snapshot!(p(10 * 60), @"10m");
2652        insta::assert_snapshot!(p(100 * 60), @"1h 40m");
2653
2654        insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2655        insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2656        insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2657        insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2658
2659        insta::assert_snapshot!(
2660            p(60 * 60 + 60 + 1),
2661            @"1h 1m 1s",
2662        );
2663        insta::assert_snapshot!(
2664            p(2 * 60 * 60 + 2 * 60 + 2),
2665            @"2h 2m 2s",
2666        );
2667        insta::assert_snapshot!(
2668            p(10 * 60 * 60 + 10 * 60 + 10),
2669            @"10h 10m 10s",
2670        );
2671        insta::assert_snapshot!(
2672            p(100 * 60 * 60 + 100 * 60 + 100),
2673            @"101h 41m 40s",
2674        );
2675
2676        insta::assert_snapshot!(p(-1 * 60 * 60), @"1h ago");
2677        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1h 30s ago");
2678    }
2679
2680    #[test]
2681    fn print_signed_duration_designator_direction_force() {
2682        let printer = || SpanPrinter::new().direction(Direction::ForceSign);
2683        let p = |secs| {
2684            printer().duration_to_string(&SignedDuration::from_secs(secs))
2685        };
2686
2687        insta::assert_snapshot!(p(1), @"+1s");
2688        insta::assert_snapshot!(p(2), @"+2s");
2689        insta::assert_snapshot!(p(10), @"+10s");
2690        insta::assert_snapshot!(p(100), @"+1m 40s");
2691
2692        insta::assert_snapshot!(p(1 * 60), @"+1m");
2693        insta::assert_snapshot!(p(2 * 60), @"+2m");
2694        insta::assert_snapshot!(p(10 * 60), @"+10m");
2695        insta::assert_snapshot!(p(100 * 60), @"+1h 40m");
2696
2697        insta::assert_snapshot!(p(1 * 60 * 60), @"+1h");
2698        insta::assert_snapshot!(p(2 * 60 * 60), @"+2h");
2699        insta::assert_snapshot!(p(10 * 60 * 60), @"+10h");
2700        insta::assert_snapshot!(p(100 * 60 * 60), @"+100h");
2701
2702        insta::assert_snapshot!(
2703            p(60 * 60 + 60 + 1),
2704            @"+1h 1m 1s",
2705        );
2706        insta::assert_snapshot!(
2707            p(2 * 60 * 60 + 2 * 60 + 2),
2708            @"+2h 2m 2s",
2709        );
2710        insta::assert_snapshot!(
2711            p(10 * 60 * 60 + 10 * 60 + 10),
2712            @"+10h 10m 10s",
2713        );
2714        insta::assert_snapshot!(
2715            p(100 * 60 * 60 + 100 * 60 + 100),
2716            @"+101h 41m 40s",
2717        );
2718
2719        insta::assert_snapshot!(p(-1 * 60 * 60), @"-1h");
2720        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"-1h 30s");
2721    }
2722
2723    #[test]
2724    fn print_signed_duration_designator_padding() {
2725        let printer = || SpanPrinter::new().padding(2);
2726        let p = |secs| {
2727            printer().duration_to_string(&SignedDuration::from_secs(secs))
2728        };
2729
2730        insta::assert_snapshot!(p(1), @"01s");
2731        insta::assert_snapshot!(p(2), @"02s");
2732        insta::assert_snapshot!(p(10), @"10s");
2733        insta::assert_snapshot!(p(100), @"01m 40s");
2734
2735        insta::assert_snapshot!(p(1 * 60), @"01m");
2736        insta::assert_snapshot!(p(2 * 60), @"02m");
2737        insta::assert_snapshot!(p(10 * 60), @"10m");
2738        insta::assert_snapshot!(p(100 * 60), @"01h 40m");
2739
2740        insta::assert_snapshot!(p(1 * 60 * 60), @"01h");
2741        insta::assert_snapshot!(p(2 * 60 * 60), @"02h");
2742        insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2743        insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2744
2745        insta::assert_snapshot!(
2746            p(60 * 60 + 60 + 1),
2747            @"01h 01m 01s",
2748        );
2749        insta::assert_snapshot!(
2750            p(2 * 60 * 60 + 2 * 60 + 2),
2751            @"02h 02m 02s",
2752        );
2753        insta::assert_snapshot!(
2754            p(10 * 60 * 60 + 10 * 60 + 10),
2755            @"10h 10m 10s",
2756        );
2757        insta::assert_snapshot!(
2758            p(100 * 60 * 60 + 100 * 60 + 100),
2759            @"101h 41m 40s",
2760        );
2761
2762        insta::assert_snapshot!(p(-1 * 60 * 60), @"01h ago");
2763        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"01h 30s ago");
2764    }
2765
2766    #[test]
2767    fn print_signed_duration_designator_spacing_none() {
2768        let printer = || SpanPrinter::new().spacing(Spacing::None);
2769        let p = |secs| {
2770            printer().duration_to_string(&SignedDuration::from_secs(secs))
2771        };
2772
2773        insta::assert_snapshot!(p(1), @"1s");
2774        insta::assert_snapshot!(p(2), @"2s");
2775        insta::assert_snapshot!(p(10), @"10s");
2776        insta::assert_snapshot!(p(100), @"1m40s");
2777
2778        insta::assert_snapshot!(p(1 * 60), @"1m");
2779        insta::assert_snapshot!(p(2 * 60), @"2m");
2780        insta::assert_snapshot!(p(10 * 60), @"10m");
2781        insta::assert_snapshot!(p(100 * 60), @"1h40m");
2782
2783        insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
2784        insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
2785        insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
2786        insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
2787
2788        insta::assert_snapshot!(
2789            p(60 * 60 + 60 + 1),
2790            @"1h1m1s",
2791        );
2792        insta::assert_snapshot!(
2793            p(2 * 60 * 60 + 2 * 60 + 2),
2794            @"2h2m2s",
2795        );
2796        insta::assert_snapshot!(
2797            p(10 * 60 * 60 + 10 * 60 + 10),
2798            @"10h10m10s",
2799        );
2800        insta::assert_snapshot!(
2801            p(100 * 60 * 60 + 100 * 60 + 100),
2802            @"101h41m40s",
2803        );
2804
2805        insta::assert_snapshot!(p(-1 * 60 * 60), @"-1h");
2806        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"-1h30s");
2807    }
2808
2809    #[test]
2810    fn print_signed_duration_designator_spacing_more() {
2811        let printer =
2812            || SpanPrinter::new().spacing(Spacing::BetweenUnitsAndDesignators);
2813        let p = |secs| {
2814            printer().duration_to_string(&SignedDuration::from_secs(secs))
2815        };
2816
2817        insta::assert_snapshot!(p(1), @"1 s");
2818        insta::assert_snapshot!(p(2), @"2 s");
2819        insta::assert_snapshot!(p(10), @"10 s");
2820        insta::assert_snapshot!(p(100), @"1 m 40 s");
2821
2822        insta::assert_snapshot!(p(1 * 60), @"1 m");
2823        insta::assert_snapshot!(p(2 * 60), @"2 m");
2824        insta::assert_snapshot!(p(10 * 60), @"10 m");
2825        insta::assert_snapshot!(p(100 * 60), @"1 h 40 m");
2826
2827        insta::assert_snapshot!(p(1 * 60 * 60), @"1 h");
2828        insta::assert_snapshot!(p(2 * 60 * 60), @"2 h");
2829        insta::assert_snapshot!(p(10 * 60 * 60), @"10 h");
2830        insta::assert_snapshot!(p(100 * 60 * 60), @"100 h");
2831
2832        insta::assert_snapshot!(
2833            p(60 * 60 + 60 + 1),
2834            @"1 h 1 m 1 s",
2835        );
2836        insta::assert_snapshot!(
2837            p(2 * 60 * 60 + 2 * 60 + 2),
2838            @"2 h 2 m 2 s",
2839        );
2840        insta::assert_snapshot!(
2841            p(10 * 60 * 60 + 10 * 60 + 10),
2842            @"10 h 10 m 10 s",
2843        );
2844        insta::assert_snapshot!(
2845            p(100 * 60 * 60 + 100 * 60 + 100),
2846            @"101 h 41 m 40 s",
2847        );
2848
2849        insta::assert_snapshot!(p(-1 * 60 * 60), @"1 h ago");
2850        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1 h 30 s ago");
2851    }
2852
2853    #[test]
2854    fn print_signed_duration_designator_spacing_comma() {
2855        let printer = || {
2856            SpanPrinter::new()
2857                .comma_after_designator(true)
2858                .spacing(Spacing::BetweenUnitsAndDesignators)
2859        };
2860        let p = |secs| {
2861            printer().duration_to_string(&SignedDuration::from_secs(secs))
2862        };
2863
2864        insta::assert_snapshot!(p(1), @"1 s");
2865        insta::assert_snapshot!(p(2), @"2 s");
2866        insta::assert_snapshot!(p(10), @"10 s");
2867        insta::assert_snapshot!(p(100), @"1 m, 40 s");
2868
2869        insta::assert_snapshot!(p(1 * 60), @"1 m");
2870        insta::assert_snapshot!(p(2 * 60), @"2 m");
2871        insta::assert_snapshot!(p(10 * 60), @"10 m");
2872        insta::assert_snapshot!(p(100 * 60), @"1 h, 40 m");
2873
2874        insta::assert_snapshot!(p(1 * 60 * 60), @"1 h");
2875        insta::assert_snapshot!(p(2 * 60 * 60), @"2 h");
2876        insta::assert_snapshot!(p(10 * 60 * 60), @"10 h");
2877        insta::assert_snapshot!(p(100 * 60 * 60), @"100 h");
2878
2879        insta::assert_snapshot!(
2880            p(60 * 60 + 60 + 1),
2881            @"1 h, 1 m, 1 s",
2882        );
2883        insta::assert_snapshot!(
2884            p(2 * 60 * 60 + 2 * 60 + 2),
2885            @"2 h, 2 m, 2 s",
2886        );
2887        insta::assert_snapshot!(
2888            p(10 * 60 * 60 + 10 * 60 + 10),
2889            @"10 h, 10 m, 10 s",
2890        );
2891        insta::assert_snapshot!(
2892            p(100 * 60 * 60 + 100 * 60 + 100),
2893            @"101 h, 41 m, 40 s",
2894        );
2895
2896        insta::assert_snapshot!(p(-1 * 60 * 60), @"1 h ago");
2897        insta::assert_snapshot!(p(-1 * 60 * 60 - 30), @"1 h, 30 s ago");
2898    }
2899
2900    #[test]
2901    fn print_signed_duration_designator_fractional_hour() {
2902        let printer =
2903            || SpanPrinter::new().fractional(Some(FractionalUnit::Hour));
2904        let p = |secs, nanos| {
2905            printer().duration_to_string(&SignedDuration::new(secs, nanos))
2906        };
2907        let pp = |precision, secs, nanos| {
2908            printer()
2909                .precision(Some(precision))
2910                .duration_to_string(&SignedDuration::new(secs, nanos))
2911        };
2912
2913        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2914        insta::assert_snapshot!(pp(0, 1 * 60 * 60, 0), @"1h");
2915        insta::assert_snapshot!(pp(1, 1 * 60 * 60, 0), @"1.0h");
2916        insta::assert_snapshot!(pp(2, 1 * 60 * 60, 0), @"1.00h");
2917
2918        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1.5h");
2919        insta::assert_snapshot!(pp(0, 1 * 60 * 60 + 30 * 60, 0), @"1h");
2920        insta::assert_snapshot!(pp(1, 1 * 60 * 60 + 30 * 60, 0), @"1.5h");
2921        insta::assert_snapshot!(pp(2, 1 * 60 * 60 + 30 * 60, 0), @"1.50h");
2922
2923        insta::assert_snapshot!(p(1 * 60 * 60 + 3 * 60, 0), @"1.05h");
2924        insta::assert_snapshot!(p(1 * 60 * 60 + 3 * 60, 1), @"1.05h");
2925        insta::assert_snapshot!(p(1, 0), @"0.000277777h");
2926        // precision loss!
2927        insta::assert_snapshot!(p(1, 1), @"0.000277777h");
2928        insta::assert_snapshot!(p(0, 0), @"0h");
2929        // precision loss!
2930        insta::assert_snapshot!(p(0, 1), @"0h");
2931
2932        insta::assert_snapshot!(
2933            printer().duration_to_string(&SignedDuration::MIN),
2934            @"2562047788015215.502499999h ago",
2935        );
2936    }
2937
2938    #[test]
2939    fn print_signed_duration_designator_fractional_minute() {
2940        let printer =
2941            || SpanPrinter::new().fractional(Some(FractionalUnit::Minute));
2942        let p = |secs, nanos| {
2943            printer().duration_to_string(&SignedDuration::new(secs, nanos))
2944        };
2945        let pp = |precision, secs, nanos| {
2946            printer()
2947                .precision(Some(precision))
2948                .duration_to_string(&SignedDuration::new(secs, nanos))
2949        };
2950
2951        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2952        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2953
2954        insta::assert_snapshot!(p(60, 0), @"1m");
2955        insta::assert_snapshot!(pp(0, 60, 0), @"1m");
2956        insta::assert_snapshot!(pp(1, 60, 0), @"1.0m");
2957        insta::assert_snapshot!(pp(2, 60, 0), @"1.00m");
2958
2959        insta::assert_snapshot!(p(90, 0), @"1.5m");
2960        insta::assert_snapshot!(pp(0, 90, 0), @"1m");
2961        insta::assert_snapshot!(pp(1, 90, 0), @"1.5m");
2962        insta::assert_snapshot!(pp(2, 90, 0), @"1.50m");
2963
2964        insta::assert_snapshot!(p(1 * 60 * 60, 1), @"1h");
2965        insta::assert_snapshot!(p(63, 0), @"1.05m");
2966        insta::assert_snapshot!(p(63, 1), @"1.05m");
2967        insta::assert_snapshot!(p(1, 0), @"0.016666666m");
2968        // precision loss!
2969        insta::assert_snapshot!(p(1, 1), @"0.016666666m");
2970        insta::assert_snapshot!(p(0, 0), @"0m");
2971        // precision loss!
2972        insta::assert_snapshot!(p(0, 1), @"0m");
2973
2974        insta::assert_snapshot!(
2975            printer().duration_to_string(&SignedDuration::MIN),
2976            @"2562047788015215h 30.149999999m ago",
2977        );
2978    }
2979
2980    #[test]
2981    fn print_signed_duration_designator_fractional_second() {
2982        let printer =
2983            || SpanPrinter::new().fractional(Some(FractionalUnit::Second));
2984        let p = |secs, nanos| {
2985            printer().duration_to_string(&SignedDuration::new(secs, nanos))
2986        };
2987        let pp = |precision, secs, nanos| {
2988            printer()
2989                .precision(Some(precision))
2990                .duration_to_string(&SignedDuration::new(secs, nanos))
2991        };
2992
2993        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
2994        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
2995
2996        insta::assert_snapshot!(p(1, 0), @"1s");
2997        insta::assert_snapshot!(pp(0, 1, 0), @"1s");
2998        insta::assert_snapshot!(pp(1, 1, 0), @"1.0s");
2999        insta::assert_snapshot!(pp(2, 1, 0), @"1.00s");
3000
3001        insta::assert_snapshot!(p(1, 500_000_000), @"1.5s");
3002        insta::assert_snapshot!(pp(0, 1, 500_000_000), @"1s");
3003        insta::assert_snapshot!(pp(1, 1, 500_000_000), @"1.5s");
3004        insta::assert_snapshot!(pp(2, 1, 500_000_000), @"1.50s");
3005
3006        insta::assert_snapshot!(p(1, 1), @"1.000000001s");
3007        insta::assert_snapshot!(p(0, 1), @"0.000000001s");
3008        insta::assert_snapshot!(p(0, 0), @"0s");
3009
3010        insta::assert_snapshot!(
3011            printer().duration_to_string(&SignedDuration::MIN),
3012            @"2562047788015215h 30m 8.999999999s ago",
3013        );
3014    }
3015
3016    #[test]
3017    fn print_signed_duration_designator_fractional_millisecond() {
3018        let printer = || {
3019            SpanPrinter::new().fractional(Some(FractionalUnit::Millisecond))
3020        };
3021        let p = |secs, nanos| {
3022            printer().duration_to_string(&SignedDuration::new(secs, nanos))
3023        };
3024        let pp = |precision, secs, nanos| {
3025            printer()
3026                .precision(Some(precision))
3027                .duration_to_string(&SignedDuration::new(secs, nanos))
3028        };
3029
3030        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
3031        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
3032        insta::assert_snapshot!(
3033            p(1 * 60 * 60 + 30 * 60 + 10, 0),
3034            @"1h 30m 10s",
3035        );
3036
3037        insta::assert_snapshot!(p(1, 0), @"1s");
3038        insta::assert_snapshot!(pp(0, 1, 0), @"1s");
3039        insta::assert_snapshot!(pp(1, 1, 0), @"1s 0.0ms");
3040        insta::assert_snapshot!(pp(2, 1, 0), @"1s 0.00ms");
3041
3042        insta::assert_snapshot!(p(1, 500_000_000), @"1s 500ms");
3043        insta::assert_snapshot!(pp(0, 1, 1_500_000), @"1s 1ms");
3044        insta::assert_snapshot!(pp(1, 1, 1_500_000), @"1s 1.5ms");
3045        insta::assert_snapshot!(pp(2, 1, 1_500_000), @"1s 1.50ms");
3046
3047        insta::assert_snapshot!(p(0, 1_000_001), @"1.000001ms");
3048        insta::assert_snapshot!(p(0, 0_000_001), @"0.000001ms");
3049        insta::assert_snapshot!(p(0, 0), @"0ms");
3050
3051        insta::assert_snapshot!(
3052            printer().duration_to_string(&SignedDuration::MIN),
3053            @"2562047788015215h 30m 8s 999.999999ms ago",
3054        );
3055    }
3056
3057    #[test]
3058    fn print_signed_duration_designator_fractional_microsecond() {
3059        let printer = || {
3060            SpanPrinter::new().fractional(Some(FractionalUnit::Microsecond))
3061        };
3062        let p = |secs, nanos| {
3063            printer().duration_to_string(&SignedDuration::new(secs, nanos))
3064        };
3065        let pp = |precision, secs, nanos| {
3066            printer()
3067                .precision(Some(precision))
3068                .duration_to_string(&SignedDuration::new(secs, nanos))
3069        };
3070
3071        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
3072        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
3073        insta::assert_snapshot!(
3074            p(1 * 60 * 60 + 30 * 60 + 10, 0),
3075            @"1h 30m 10s",
3076        );
3077
3078        insta::assert_snapshot!(p(1, 0), @"1s");
3079        insta::assert_snapshot!(pp(0, 1, 0), @"1s");
3080        insta::assert_snapshot!(pp(1, 1, 0), @"1s 0.0µs");
3081        insta::assert_snapshot!(pp(2, 1, 0), @"1s 0.00µs");
3082
3083        insta::assert_snapshot!(p(1, 500_000_000), @"1s 500ms");
3084        insta::assert_snapshot!(pp(0, 1, 1_500_000), @"1s 1ms 500µs");
3085        insta::assert_snapshot!(pp(1, 1, 1_500_000), @"1s 1ms 500.0µs");
3086        insta::assert_snapshot!(pp(2, 1, 1_500_000), @"1s 1ms 500.00µs");
3087
3088        insta::assert_snapshot!(p(0, 1_000_001), @"1ms 0.001µs");
3089        insta::assert_snapshot!(p(0, 0_000_001), @"0.001µs");
3090        insta::assert_snapshot!(p(0, 0), @"0µs");
3091
3092        insta::assert_snapshot!(
3093            printer().duration_to_string(&SignedDuration::MIN),
3094            @"2562047788015215h 30m 8s 999ms 999.999µs ago",
3095        );
3096    }
3097
3098    #[test]
3099    fn print_unsigned_duration_designator_default() {
3100        let printer = || SpanPrinter::new();
3101        let p = |secs| {
3102            printer().unsigned_duration_to_string(
3103                &core::time::Duration::from_secs(secs),
3104            )
3105        };
3106
3107        insta::assert_snapshot!(p(1), @"1s");
3108        insta::assert_snapshot!(p(2), @"2s");
3109        insta::assert_snapshot!(p(10), @"10s");
3110        insta::assert_snapshot!(p(100), @"1m 40s");
3111
3112        insta::assert_snapshot!(p(1 * 60), @"1m");
3113        insta::assert_snapshot!(p(2 * 60), @"2m");
3114        insta::assert_snapshot!(p(10 * 60), @"10m");
3115        insta::assert_snapshot!(p(100 * 60), @"1h 40m");
3116
3117        insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
3118        insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
3119        insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
3120        insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
3121
3122        insta::assert_snapshot!(
3123            p(60 * 60 + 60 + 1),
3124            @"1h 1m 1s",
3125        );
3126        insta::assert_snapshot!(
3127            p(2 * 60 * 60 + 2 * 60 + 2),
3128            @"2h 2m 2s",
3129        );
3130        insta::assert_snapshot!(
3131            p(10 * 60 * 60 + 10 * 60 + 10),
3132            @"10h 10m 10s",
3133        );
3134        insta::assert_snapshot!(
3135            p(100 * 60 * 60 + 100 * 60 + 100),
3136            @"101h 41m 40s",
3137        );
3138    }
3139
3140    #[test]
3141    fn print_unsigned_duration_designator_verbose() {
3142        let printer = || SpanPrinter::new().designator(Designator::Verbose);
3143        let p = |secs| {
3144            printer().unsigned_duration_to_string(
3145                &core::time::Duration::from_secs(secs),
3146            )
3147        };
3148
3149        insta::assert_snapshot!(p(1), @"1second");
3150        insta::assert_snapshot!(p(2), @"2seconds");
3151        insta::assert_snapshot!(p(10), @"10seconds");
3152        insta::assert_snapshot!(p(100), @"1minute 40seconds");
3153
3154        insta::assert_snapshot!(p(1 * 60), @"1minute");
3155        insta::assert_snapshot!(p(2 * 60), @"2minutes");
3156        insta::assert_snapshot!(p(10 * 60), @"10minutes");
3157        insta::assert_snapshot!(p(100 * 60), @"1hour 40minutes");
3158
3159        insta::assert_snapshot!(p(1 * 60 * 60), @"1hour");
3160        insta::assert_snapshot!(p(2 * 60 * 60), @"2hours");
3161        insta::assert_snapshot!(p(10 * 60 * 60), @"10hours");
3162        insta::assert_snapshot!(p(100 * 60 * 60), @"100hours");
3163
3164        insta::assert_snapshot!(
3165            p(60 * 60 + 60 + 1),
3166            @"1hour 1minute 1second",
3167        );
3168        insta::assert_snapshot!(
3169            p(2 * 60 * 60 + 2 * 60 + 2),
3170            @"2hours 2minutes 2seconds",
3171        );
3172        insta::assert_snapshot!(
3173            p(10 * 60 * 60 + 10 * 60 + 10),
3174            @"10hours 10minutes 10seconds",
3175        );
3176        insta::assert_snapshot!(
3177            p(100 * 60 * 60 + 100 * 60 + 100),
3178            @"101hours 41minutes 40seconds",
3179        );
3180    }
3181
3182    #[test]
3183    fn print_unsigned_duration_designator_short() {
3184        let printer = || SpanPrinter::new().designator(Designator::Short);
3185        let p = |secs| {
3186            printer().unsigned_duration_to_string(
3187                &core::time::Duration::from_secs(secs),
3188            )
3189        };
3190
3191        insta::assert_snapshot!(p(1), @"1sec");
3192        insta::assert_snapshot!(p(2), @"2secs");
3193        insta::assert_snapshot!(p(10), @"10secs");
3194        insta::assert_snapshot!(p(100), @"1min 40secs");
3195
3196        insta::assert_snapshot!(p(1 * 60), @"1min");
3197        insta::assert_snapshot!(p(2 * 60), @"2mins");
3198        insta::assert_snapshot!(p(10 * 60), @"10mins");
3199        insta::assert_snapshot!(p(100 * 60), @"1hr 40mins");
3200
3201        insta::assert_snapshot!(p(1 * 60 * 60), @"1hr");
3202        insta::assert_snapshot!(p(2 * 60 * 60), @"2hrs");
3203        insta::assert_snapshot!(p(10 * 60 * 60), @"10hrs");
3204        insta::assert_snapshot!(p(100 * 60 * 60), @"100hrs");
3205
3206        insta::assert_snapshot!(
3207            p(60 * 60 + 60 + 1),
3208            @"1hr 1min 1sec",
3209        );
3210        insta::assert_snapshot!(
3211            p(2 * 60 * 60 + 2 * 60 + 2),
3212            @"2hrs 2mins 2secs",
3213        );
3214        insta::assert_snapshot!(
3215            p(10 * 60 * 60 + 10 * 60 + 10),
3216            @"10hrs 10mins 10secs",
3217        );
3218        insta::assert_snapshot!(
3219            p(100 * 60 * 60 + 100 * 60 + 100),
3220            @"101hrs 41mins 40secs",
3221        );
3222    }
3223
3224    #[test]
3225    fn print_unsigned_duration_designator_compact() {
3226        let printer = || SpanPrinter::new().designator(Designator::Compact);
3227        let p = |secs| {
3228            printer().unsigned_duration_to_string(
3229                &core::time::Duration::from_secs(secs),
3230            )
3231        };
3232
3233        insta::assert_snapshot!(p(1), @"1s");
3234        insta::assert_snapshot!(p(2), @"2s");
3235        insta::assert_snapshot!(p(10), @"10s");
3236        insta::assert_snapshot!(p(100), @"1m 40s");
3237
3238        insta::assert_snapshot!(p(1 * 60), @"1m");
3239        insta::assert_snapshot!(p(2 * 60), @"2m");
3240        insta::assert_snapshot!(p(10 * 60), @"10m");
3241        insta::assert_snapshot!(p(100 * 60), @"1h 40m");
3242
3243        insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
3244        insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
3245        insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
3246        insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
3247
3248        insta::assert_snapshot!(
3249            p(60 * 60 + 60 + 1),
3250            @"1h 1m 1s",
3251        );
3252        insta::assert_snapshot!(
3253            p(2 * 60 * 60 + 2 * 60 + 2),
3254            @"2h 2m 2s",
3255        );
3256        insta::assert_snapshot!(
3257            p(10 * 60 * 60 + 10 * 60 + 10),
3258            @"10h 10m 10s",
3259        );
3260        insta::assert_snapshot!(
3261            p(100 * 60 * 60 + 100 * 60 + 100),
3262            @"101h 41m 40s",
3263        );
3264    }
3265
3266    #[test]
3267    fn print_unsigned_duration_designator_direction_force() {
3268        let printer = || SpanPrinter::new().direction(Direction::ForceSign);
3269        let p = |secs| {
3270            printer().unsigned_duration_to_string(
3271                &core::time::Duration::from_secs(secs),
3272            )
3273        };
3274
3275        insta::assert_snapshot!(p(1), @"+1s");
3276        insta::assert_snapshot!(p(2), @"+2s");
3277        insta::assert_snapshot!(p(10), @"+10s");
3278        insta::assert_snapshot!(p(100), @"+1m 40s");
3279
3280        insta::assert_snapshot!(p(1 * 60), @"+1m");
3281        insta::assert_snapshot!(p(2 * 60), @"+2m");
3282        insta::assert_snapshot!(p(10 * 60), @"+10m");
3283        insta::assert_snapshot!(p(100 * 60), @"+1h 40m");
3284
3285        insta::assert_snapshot!(p(1 * 60 * 60), @"+1h");
3286        insta::assert_snapshot!(p(2 * 60 * 60), @"+2h");
3287        insta::assert_snapshot!(p(10 * 60 * 60), @"+10h");
3288        insta::assert_snapshot!(p(100 * 60 * 60), @"+100h");
3289
3290        insta::assert_snapshot!(
3291            p(60 * 60 + 60 + 1),
3292            @"+1h 1m 1s",
3293        );
3294        insta::assert_snapshot!(
3295            p(2 * 60 * 60 + 2 * 60 + 2),
3296            @"+2h 2m 2s",
3297        );
3298        insta::assert_snapshot!(
3299            p(10 * 60 * 60 + 10 * 60 + 10),
3300            @"+10h 10m 10s",
3301        );
3302        insta::assert_snapshot!(
3303            p(100 * 60 * 60 + 100 * 60 + 100),
3304            @"+101h 41m 40s",
3305        );
3306    }
3307
3308    #[test]
3309    fn print_unsigned_duration_designator_padding() {
3310        let printer = || SpanPrinter::new().padding(2);
3311        let p = |secs| {
3312            printer().unsigned_duration_to_string(
3313                &core::time::Duration::from_secs(secs),
3314            )
3315        };
3316
3317        insta::assert_snapshot!(p(1), @"01s");
3318        insta::assert_snapshot!(p(2), @"02s");
3319        insta::assert_snapshot!(p(10), @"10s");
3320        insta::assert_snapshot!(p(100), @"01m 40s");
3321
3322        insta::assert_snapshot!(p(1 * 60), @"01m");
3323        insta::assert_snapshot!(p(2 * 60), @"02m");
3324        insta::assert_snapshot!(p(10 * 60), @"10m");
3325        insta::assert_snapshot!(p(100 * 60), @"01h 40m");
3326
3327        insta::assert_snapshot!(p(1 * 60 * 60), @"01h");
3328        insta::assert_snapshot!(p(2 * 60 * 60), @"02h");
3329        insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
3330        insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
3331
3332        insta::assert_snapshot!(
3333            p(60 * 60 + 60 + 1),
3334            @"01h 01m 01s",
3335        );
3336        insta::assert_snapshot!(
3337            p(2 * 60 * 60 + 2 * 60 + 2),
3338            @"02h 02m 02s",
3339        );
3340        insta::assert_snapshot!(
3341            p(10 * 60 * 60 + 10 * 60 + 10),
3342            @"10h 10m 10s",
3343        );
3344        insta::assert_snapshot!(
3345            p(100 * 60 * 60 + 100 * 60 + 100),
3346            @"101h 41m 40s",
3347        );
3348    }
3349
3350    #[test]
3351    fn print_unsigned_duration_designator_spacing_none() {
3352        let printer = || SpanPrinter::new().spacing(Spacing::None);
3353        let p = |secs| {
3354            printer().unsigned_duration_to_string(
3355                &core::time::Duration::from_secs(secs),
3356            )
3357        };
3358
3359        insta::assert_snapshot!(p(1), @"1s");
3360        insta::assert_snapshot!(p(2), @"2s");
3361        insta::assert_snapshot!(p(10), @"10s");
3362        insta::assert_snapshot!(p(100), @"1m40s");
3363
3364        insta::assert_snapshot!(p(1 * 60), @"1m");
3365        insta::assert_snapshot!(p(2 * 60), @"2m");
3366        insta::assert_snapshot!(p(10 * 60), @"10m");
3367        insta::assert_snapshot!(p(100 * 60), @"1h40m");
3368
3369        insta::assert_snapshot!(p(1 * 60 * 60), @"1h");
3370        insta::assert_snapshot!(p(2 * 60 * 60), @"2h");
3371        insta::assert_snapshot!(p(10 * 60 * 60), @"10h");
3372        insta::assert_snapshot!(p(100 * 60 * 60), @"100h");
3373
3374        insta::assert_snapshot!(
3375            p(60 * 60 + 60 + 1),
3376            @"1h1m1s",
3377        );
3378        insta::assert_snapshot!(
3379            p(2 * 60 * 60 + 2 * 60 + 2),
3380            @"2h2m2s",
3381        );
3382        insta::assert_snapshot!(
3383            p(10 * 60 * 60 + 10 * 60 + 10),
3384            @"10h10m10s",
3385        );
3386        insta::assert_snapshot!(
3387            p(100 * 60 * 60 + 100 * 60 + 100),
3388            @"101h41m40s",
3389        );
3390    }
3391
3392    #[test]
3393    fn print_unsigned_duration_designator_spacing_more() {
3394        let printer =
3395            || SpanPrinter::new().spacing(Spacing::BetweenUnitsAndDesignators);
3396        let p = |secs| {
3397            printer().unsigned_duration_to_string(
3398                &core::time::Duration::from_secs(secs),
3399            )
3400        };
3401
3402        insta::assert_snapshot!(p(1), @"1 s");
3403        insta::assert_snapshot!(p(2), @"2 s");
3404        insta::assert_snapshot!(p(10), @"10 s");
3405        insta::assert_snapshot!(p(100), @"1 m 40 s");
3406
3407        insta::assert_snapshot!(p(1 * 60), @"1 m");
3408        insta::assert_snapshot!(p(2 * 60), @"2 m");
3409        insta::assert_snapshot!(p(10 * 60), @"10 m");
3410        insta::assert_snapshot!(p(100 * 60), @"1 h 40 m");
3411
3412        insta::assert_snapshot!(p(1 * 60 * 60), @"1 h");
3413        insta::assert_snapshot!(p(2 * 60 * 60), @"2 h");
3414        insta::assert_snapshot!(p(10 * 60 * 60), @"10 h");
3415        insta::assert_snapshot!(p(100 * 60 * 60), @"100 h");
3416
3417        insta::assert_snapshot!(
3418            p(60 * 60 + 60 + 1),
3419            @"1 h 1 m 1 s",
3420        );
3421        insta::assert_snapshot!(
3422            p(2 * 60 * 60 + 2 * 60 + 2),
3423            @"2 h 2 m 2 s",
3424        );
3425        insta::assert_snapshot!(
3426            p(10 * 60 * 60 + 10 * 60 + 10),
3427            @"10 h 10 m 10 s",
3428        );
3429        insta::assert_snapshot!(
3430            p(100 * 60 * 60 + 100 * 60 + 100),
3431            @"101 h 41 m 40 s",
3432        );
3433    }
3434
3435    #[test]
3436    fn print_unsigned_duration_designator_spacing_comma() {
3437        let printer = || {
3438            SpanPrinter::new()
3439                .comma_after_designator(true)
3440                .spacing(Spacing::BetweenUnitsAndDesignators)
3441        };
3442        let p = |secs| {
3443            printer().unsigned_duration_to_string(
3444                &core::time::Duration::from_secs(secs),
3445            )
3446        };
3447
3448        insta::assert_snapshot!(p(1), @"1 s");
3449        insta::assert_snapshot!(p(2), @"2 s");
3450        insta::assert_snapshot!(p(10), @"10 s");
3451        insta::assert_snapshot!(p(100), @"1 m, 40 s");
3452
3453        insta::assert_snapshot!(p(1 * 60), @"1 m");
3454        insta::assert_snapshot!(p(2 * 60), @"2 m");
3455        insta::assert_snapshot!(p(10 * 60), @"10 m");
3456        insta::assert_snapshot!(p(100 * 60), @"1 h, 40 m");
3457
3458        insta::assert_snapshot!(p(1 * 60 * 60), @"1 h");
3459        insta::assert_snapshot!(p(2 * 60 * 60), @"2 h");
3460        insta::assert_snapshot!(p(10 * 60 * 60), @"10 h");
3461        insta::assert_snapshot!(p(100 * 60 * 60), @"100 h");
3462
3463        insta::assert_snapshot!(
3464            p(60 * 60 + 60 + 1),
3465            @"1 h, 1 m, 1 s",
3466        );
3467        insta::assert_snapshot!(
3468            p(2 * 60 * 60 + 2 * 60 + 2),
3469            @"2 h, 2 m, 2 s",
3470        );
3471        insta::assert_snapshot!(
3472            p(10 * 60 * 60 + 10 * 60 + 10),
3473            @"10 h, 10 m, 10 s",
3474        );
3475        insta::assert_snapshot!(
3476            p(100 * 60 * 60 + 100 * 60 + 100),
3477            @"101 h, 41 m, 40 s",
3478        );
3479    }
3480
3481    #[test]
3482    fn print_unsigned_duration_designator_fractional_hour() {
3483        let printer =
3484            || SpanPrinter::new().fractional(Some(FractionalUnit::Hour));
3485        let p = |secs, nanos| {
3486            printer().unsigned_duration_to_string(&core::time::Duration::new(
3487                secs, nanos,
3488            ))
3489        };
3490        let pp = |precision, secs, nanos| {
3491            printer()
3492                .precision(Some(precision))
3493                .duration_to_string(&SignedDuration::new(secs, nanos))
3494        };
3495
3496        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
3497        insta::assert_snapshot!(pp(0, 1 * 60 * 60, 0), @"1h");
3498        insta::assert_snapshot!(pp(1, 1 * 60 * 60, 0), @"1.0h");
3499        insta::assert_snapshot!(pp(2, 1 * 60 * 60, 0), @"1.00h");
3500
3501        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1.5h");
3502        insta::assert_snapshot!(pp(0, 1 * 60 * 60 + 30 * 60, 0), @"1h");
3503        insta::assert_snapshot!(pp(1, 1 * 60 * 60 + 30 * 60, 0), @"1.5h");
3504        insta::assert_snapshot!(pp(2, 1 * 60 * 60 + 30 * 60, 0), @"1.50h");
3505
3506        insta::assert_snapshot!(p(1 * 60 * 60 + 3 * 60, 0), @"1.05h");
3507        insta::assert_snapshot!(p(1 * 60 * 60 + 3 * 60, 1), @"1.05h");
3508        insta::assert_snapshot!(p(1, 0), @"0.000277777h");
3509        // precision loss!
3510        insta::assert_snapshot!(p(1, 1), @"0.000277777h");
3511        insta::assert_snapshot!(p(0, 0), @"0h");
3512        // precision loss!
3513        insta::assert_snapshot!(p(0, 1), @"0h");
3514    }
3515
3516    #[test]
3517    fn print_unsigned_duration_designator_fractional_minute() {
3518        let printer =
3519            || SpanPrinter::new().fractional(Some(FractionalUnit::Minute));
3520        let p = |secs, nanos| {
3521            printer().unsigned_duration_to_string(&core::time::Duration::new(
3522                secs, nanos,
3523            ))
3524        };
3525        let pp = |precision, secs, nanos| {
3526            printer()
3527                .precision(Some(precision))
3528                .duration_to_string(&SignedDuration::new(secs, nanos))
3529        };
3530
3531        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
3532        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
3533
3534        insta::assert_snapshot!(p(60, 0), @"1m");
3535        insta::assert_snapshot!(pp(0, 60, 0), @"1m");
3536        insta::assert_snapshot!(pp(1, 60, 0), @"1.0m");
3537        insta::assert_snapshot!(pp(2, 60, 0), @"1.00m");
3538
3539        insta::assert_snapshot!(p(90, 0), @"1.5m");
3540        insta::assert_snapshot!(pp(0, 90, 0), @"1m");
3541        insta::assert_snapshot!(pp(1, 90, 0), @"1.5m");
3542        insta::assert_snapshot!(pp(2, 90, 0), @"1.50m");
3543
3544        insta::assert_snapshot!(p(1 * 60 * 60, 1), @"1h");
3545        insta::assert_snapshot!(p(63, 0), @"1.05m");
3546        insta::assert_snapshot!(p(63, 1), @"1.05m");
3547        insta::assert_snapshot!(p(1, 0), @"0.016666666m");
3548        // precision loss!
3549        insta::assert_snapshot!(p(1, 1), @"0.016666666m");
3550        insta::assert_snapshot!(p(0, 0), @"0m");
3551        // precision loss!
3552        insta::assert_snapshot!(p(0, 1), @"0m");
3553    }
3554
3555    #[test]
3556    fn print_unsigned_duration_designator_fractional_second() {
3557        let printer =
3558            || SpanPrinter::new().fractional(Some(FractionalUnit::Second));
3559        let p = |secs, nanos| {
3560            printer().unsigned_duration_to_string(&core::time::Duration::new(
3561                secs, nanos,
3562            ))
3563        };
3564        let pp = |precision, secs, nanos| {
3565            printer()
3566                .precision(Some(precision))
3567                .duration_to_string(&SignedDuration::new(secs, nanos))
3568        };
3569
3570        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
3571        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
3572
3573        insta::assert_snapshot!(p(1, 0), @"1s");
3574        insta::assert_snapshot!(pp(0, 1, 0), @"1s");
3575        insta::assert_snapshot!(pp(1, 1, 0), @"1.0s");
3576        insta::assert_snapshot!(pp(2, 1, 0), @"1.00s");
3577
3578        insta::assert_snapshot!(p(1, 500_000_000), @"1.5s");
3579        insta::assert_snapshot!(pp(0, 1, 500_000_000), @"1s");
3580        insta::assert_snapshot!(pp(1, 1, 500_000_000), @"1.5s");
3581        insta::assert_snapshot!(pp(2, 1, 500_000_000), @"1.50s");
3582
3583        insta::assert_snapshot!(p(1, 1), @"1.000000001s");
3584        insta::assert_snapshot!(p(0, 1), @"0.000000001s");
3585        insta::assert_snapshot!(p(0, 0), @"0s");
3586    }
3587
3588    #[test]
3589    fn print_unsigned_duration_designator_fractional_millisecond() {
3590        let printer = || {
3591            SpanPrinter::new().fractional(Some(FractionalUnit::Millisecond))
3592        };
3593        let p = |secs, nanos| {
3594            printer().unsigned_duration_to_string(&core::time::Duration::new(
3595                secs, nanos,
3596            ))
3597        };
3598        let pp = |precision, secs, nanos| {
3599            printer()
3600                .precision(Some(precision))
3601                .duration_to_string(&SignedDuration::new(secs, nanos))
3602        };
3603
3604        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
3605        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
3606        insta::assert_snapshot!(
3607            p(1 * 60 * 60 + 30 * 60 + 10, 0),
3608            @"1h 30m 10s",
3609        );
3610
3611        insta::assert_snapshot!(p(1, 0), @"1s");
3612        insta::assert_snapshot!(pp(0, 1, 0), @"1s");
3613        insta::assert_snapshot!(pp(1, 1, 0), @"1s 0.0ms");
3614        insta::assert_snapshot!(pp(2, 1, 0), @"1s 0.00ms");
3615
3616        insta::assert_snapshot!(p(1, 500_000_000), @"1s 500ms");
3617        insta::assert_snapshot!(pp(0, 1, 1_500_000), @"1s 1ms");
3618        insta::assert_snapshot!(pp(1, 1, 1_500_000), @"1s 1.5ms");
3619        insta::assert_snapshot!(pp(2, 1, 1_500_000), @"1s 1.50ms");
3620
3621        insta::assert_snapshot!(p(0, 1_000_001), @"1.000001ms");
3622        insta::assert_snapshot!(p(0, 0_000_001), @"0.000001ms");
3623        insta::assert_snapshot!(p(0, 0), @"0ms");
3624    }
3625
3626    #[test]
3627    fn print_unsigned_duration_designator_fractional_microsecond() {
3628        let printer = || {
3629            SpanPrinter::new().fractional(Some(FractionalUnit::Microsecond))
3630        };
3631        let p = |secs, nanos| {
3632            printer().unsigned_duration_to_string(&core::time::Duration::new(
3633                secs, nanos,
3634            ))
3635        };
3636        let pp = |precision, secs, nanos| {
3637            printer().precision(Some(precision)).unsigned_duration_to_string(
3638                &core::time::Duration::new(secs, nanos),
3639            )
3640        };
3641
3642        insta::assert_snapshot!(p(1 * 60 * 60, 0), @"1h");
3643        insta::assert_snapshot!(p(1 * 60 * 60 + 30 * 60, 0), @"1h 30m");
3644        insta::assert_snapshot!(
3645            p(1 * 60 * 60 + 30 * 60 + 10, 0),
3646            @"1h 30m 10s",
3647        );
3648
3649        insta::assert_snapshot!(p(1, 0), @"1s");
3650        insta::assert_snapshot!(pp(0, 1, 0), @"1s");
3651        insta::assert_snapshot!(pp(1, 1, 0), @"1s 0.0µs");
3652        insta::assert_snapshot!(pp(2, 1, 0), @"1s 0.00µs");
3653
3654        insta::assert_snapshot!(p(1, 500_000_000), @"1s 500ms");
3655        insta::assert_snapshot!(pp(0, 1, 1_500_000), @"1s 1ms 500µs");
3656        insta::assert_snapshot!(pp(1, 1, 1_500_000), @"1s 1ms 500.0µs");
3657        insta::assert_snapshot!(pp(2, 1, 1_500_000), @"1s 1ms 500.00µs");
3658
3659        insta::assert_snapshot!(p(0, 1_000_001), @"1ms 0.001µs");
3660        insta::assert_snapshot!(p(0, 0_000_001), @"0.001µs");
3661        insta::assert_snapshot!(p(0, 0), @"0µs");
3662    }
3663
3664    #[test]
3665    fn print_span_hms() {
3666        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3667        let p = |span| printer().span_to_string(&span);
3668
3669        insta::assert_snapshot!(p(1.second()), @"00:00:01");
3670        insta::assert_snapshot!(p(2.seconds()), @"00:00:02");
3671        insta::assert_snapshot!(p(10.seconds()), @"00:00:10");
3672        insta::assert_snapshot!(p(100.seconds()), @"00:00:100");
3673
3674        insta::assert_snapshot!(p(1.minute()), @"00:01:00");
3675        insta::assert_snapshot!(p(2.minutes()), @"00:02:00");
3676        insta::assert_snapshot!(p(10.minutes()), @"00:10:00");
3677        insta::assert_snapshot!(p(100.minutes()), @"00:100:00");
3678
3679        insta::assert_snapshot!(p(1.hour()), @"01:00:00");
3680        insta::assert_snapshot!(p(2.hours()), @"02:00:00");
3681        insta::assert_snapshot!(p(10.hours()), @"10:00:00");
3682        insta::assert_snapshot!(p(100.hours()), @"100:00:00");
3683
3684        insta::assert_snapshot!(
3685            p(1.hour().minutes(1).seconds(1)),
3686            @"01:01:01",
3687        );
3688        insta::assert_snapshot!(
3689            p(2.hours().minutes(2).seconds(2)),
3690            @"02:02:02",
3691        );
3692        insta::assert_snapshot!(
3693            p(10.hours().minutes(10).seconds(10)),
3694            @"10:10:10",
3695        );
3696        insta::assert_snapshot!(
3697            p(100.hours().minutes(100).seconds(100)),
3698            @"100:100:100",
3699        );
3700
3701        insta::assert_snapshot!(
3702            p(1.day().hours(1).minutes(1).seconds(1)),
3703            @"1d 01:01:01",
3704        );
3705        insta::assert_snapshot!(
3706            p(1.day()),
3707            @"1d 00:00:00",
3708        );
3709        insta::assert_snapshot!(
3710            p(1.day().seconds(2)),
3711            @"1d 00:00:02",
3712        );
3713    }
3714
3715    #[test]
3716    fn print_span_hms_fmt() {
3717        let printer = || {
3718            SpanPrinter::new()
3719                .hours_minutes_seconds(true)
3720                .comma_after_designator(true)
3721                .spacing(Spacing::BetweenUnitsAndDesignators)
3722        };
3723        let p = |span| printer().span_to_string(&span);
3724
3725        insta::assert_snapshot!(
3726            p(1.day().hours(1).minutes(1).seconds(1)),
3727            @"1 d, 01:01:01",
3728        );
3729        insta::assert_snapshot!(
3730            p(1.year().months(1).weeks(1).days(1).hours(1).minutes(1).seconds(1)),
3731            @"1 y, 1 mo, 1 w, 1 d, 01:01:01",
3732        );
3733        insta::assert_snapshot!(
3734            p(1.day().hours(1).minutes(1).seconds(1).nanoseconds(1)),
3735            @"1 d, 01:01:01.000000001",
3736        );
3737    }
3738
3739    #[test]
3740    fn print_span_hms_sign() {
3741        let printer = |direction| {
3742            SpanPrinter::new().hours_minutes_seconds(true).direction(direction)
3743        };
3744        let p = |direction, span| printer(direction).span_to_string(&span);
3745
3746        insta::assert_snapshot!(
3747            p(Direction::Auto, 1.hour()),
3748            @"01:00:00",
3749        );
3750        insta::assert_snapshot!(
3751            p(Direction::Sign, 1.hour()),
3752            @"01:00:00",
3753        );
3754        insta::assert_snapshot!(
3755            p(Direction::ForceSign, 1.hour()),
3756            @"+01:00:00",
3757        );
3758        insta::assert_snapshot!(
3759            p(Direction::Suffix, 1.hour()),
3760            @"01:00:00",
3761        );
3762        insta::assert_snapshot!(
3763            p(Direction::Auto, -1.hour()),
3764            @"-01:00:00",
3765        );
3766        insta::assert_snapshot!(
3767            p(Direction::Sign, -1.hour()),
3768            @"-01:00:00",
3769        );
3770        insta::assert_snapshot!(
3771            p(Direction::ForceSign, -1.hour()),
3772            @"-01:00:00",
3773        );
3774        insta::assert_snapshot!(
3775            p(Direction::Suffix, -1.hour()),
3776            @"01:00:00 ago",
3777        );
3778
3779        insta::assert_snapshot!(
3780            p(Direction::Auto, 1.day().hours(1)),
3781            @"1d 01:00:00",
3782        );
3783        insta::assert_snapshot!(
3784            p(Direction::Sign, 1.day().hours(1)),
3785            @"1d 01:00:00",
3786        );
3787        insta::assert_snapshot!(
3788            p(Direction::ForceSign, 1.day().hours(1)),
3789            @"+1d 01:00:00",
3790        );
3791        insta::assert_snapshot!(
3792            p(Direction::Suffix, 1.day().hours(1)),
3793            @"1d 01:00:00",
3794        );
3795        // This is the main change from above. With non-zero
3796        // calendar units, the default for expressing a negative
3797        // sign switches to a suffix in the HH:MM:SS format.
3798        insta::assert_snapshot!(
3799            p(Direction::Auto, -1.day().hours(1)),
3800            @"1d 01:00:00 ago",
3801        );
3802        insta::assert_snapshot!(
3803            p(Direction::Sign, -1.day().hours(1)),
3804            @"-1d 01:00:00",
3805        );
3806        insta::assert_snapshot!(
3807            p(Direction::ForceSign, -1.day().hours(1)),
3808            @"-1d 01:00:00",
3809        );
3810        insta::assert_snapshot!(
3811            p(Direction::Suffix, -1.day().hours(1)),
3812            @"1d 01:00:00 ago",
3813        );
3814    }
3815
3816    #[test]
3817    fn print_span_hms_fraction_auto() {
3818        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3819        let p = |span| printer().span_to_string(&span);
3820
3821        insta::assert_snapshot!(p(1.nanosecond()), @"00:00:00.000000001");
3822        insta::assert_snapshot!(p(-1.nanosecond()), @"-00:00:00.000000001");
3823        insta::assert_snapshot!(
3824            printer().direction(Direction::ForceSign).span_to_string(&1.nanosecond()),
3825            @"+00:00:00.000000001",
3826        );
3827
3828        insta::assert_snapshot!(
3829            p(1.second().nanoseconds(123)),
3830            @"00:00:01.000000123",
3831        );
3832        insta::assert_snapshot!(
3833            p(1.second().milliseconds(123)),
3834            @"00:00:01.123",
3835        );
3836        insta::assert_snapshot!(
3837            p(1.second().milliseconds(1_123)),
3838            @"00:00:02.123",
3839        );
3840        insta::assert_snapshot!(
3841            p(1.second().milliseconds(61_123)),
3842            @"00:00:62.123",
3843        );
3844    }
3845
3846    #[test]
3847    fn print_span_hms_fraction_fixed_precision() {
3848        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3849        let p = |precision, span| {
3850            printer().precision(Some(precision)).span_to_string(&span)
3851        };
3852
3853        insta::assert_snapshot!(p(3, 1.second()), @"00:00:01.000");
3854        insta::assert_snapshot!(
3855            p(3, 1.second().milliseconds(1)),
3856            @"00:00:01.001",
3857        );
3858        insta::assert_snapshot!(
3859            p(3, 1.second().milliseconds(123)),
3860            @"00:00:01.123",
3861        );
3862        insta::assert_snapshot!(
3863            p(3, 1.second().milliseconds(100)),
3864            @"00:00:01.100",
3865        );
3866
3867        insta::assert_snapshot!(p(0, 1.second()), @"00:00:01");
3868        insta::assert_snapshot!(p(0, 1.second().milliseconds(1)), @"00:00:01");
3869        insta::assert_snapshot!(
3870            p(1, 1.second().milliseconds(999)),
3871            @"00:00:01.9",
3872        );
3873    }
3874
3875    #[test]
3876    fn print_signed_duration_hms() {
3877        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3878        let p = |secs| {
3879            printer().duration_to_string(&SignedDuration::from_secs(secs))
3880        };
3881
3882        // Note the differences with `Span`, since with a `SignedDuration`,
3883        // all units are balanced.
3884
3885        insta::assert_snapshot!(p(1), @"00:00:01");
3886        insta::assert_snapshot!(p(2), @"00:00:02");
3887        insta::assert_snapshot!(p(10), @"00:00:10");
3888        insta::assert_snapshot!(p(100), @"00:01:40");
3889
3890        insta::assert_snapshot!(p(1 * 60), @"00:01:00");
3891        insta::assert_snapshot!(p(2 * 60), @"00:02:00");
3892        insta::assert_snapshot!(p(10 * 60), @"00:10:00");
3893        insta::assert_snapshot!(p(100 * 60), @"01:40:00");
3894
3895        insta::assert_snapshot!(p(1 * 60 * 60), @"01:00:00");
3896        insta::assert_snapshot!(p(2 * 60 * 60), @"02:00:00");
3897        insta::assert_snapshot!(p(10 * 60 * 60), @"10:00:00");
3898        insta::assert_snapshot!(p(100 * 60 * 60), @"100:00:00");
3899
3900        insta::assert_snapshot!(
3901            p(60 * 60 + 60 + 1),
3902            @"01:01:01",
3903        );
3904        insta::assert_snapshot!(
3905            p(2 * 60 * 60 + 2 * 60 + 2),
3906            @"02:02:02",
3907        );
3908        insta::assert_snapshot!(
3909            p(10 * 60 * 60 + 10 * 60 + 10),
3910            @"10:10:10",
3911        );
3912        insta::assert_snapshot!(
3913            p(100 * 60 * 60 + 100 * 60 + 100),
3914            @"101:41:40",
3915        );
3916    }
3917
3918    #[test]
3919    fn print_signed_duration_hms_sign() {
3920        let printer = |direction| {
3921            SpanPrinter::new().hours_minutes_seconds(true).direction(direction)
3922        };
3923        let p = |direction, secs| {
3924            printer(direction)
3925                .duration_to_string(&SignedDuration::from_secs(secs))
3926        };
3927
3928        insta::assert_snapshot!(p(Direction::Auto, 1), @"00:00:01");
3929        insta::assert_snapshot!(p(Direction::Sign, 1), @"00:00:01");
3930        insta::assert_snapshot!(p(Direction::ForceSign, 1), @"+00:00:01");
3931        insta::assert_snapshot!(p(Direction::Suffix, 1), @"00:00:01");
3932
3933        insta::assert_snapshot!(p(Direction::Auto, -1), @"-00:00:01");
3934        insta::assert_snapshot!(p(Direction::Sign, -1), @"-00:00:01");
3935        insta::assert_snapshot!(p(Direction::ForceSign, -1), @"-00:00:01");
3936        insta::assert_snapshot!(p(Direction::Suffix, -1), @"00:00:01 ago");
3937    }
3938
3939    #[test]
3940    fn print_signed_duration_hms_fraction_auto() {
3941        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3942        let p = |secs, nanos| {
3943            printer().duration_to_string(&SignedDuration::new(secs, nanos))
3944        };
3945
3946        insta::assert_snapshot!(p(0, 1), @"00:00:00.000000001");
3947        insta::assert_snapshot!(p(0, -1), @"-00:00:00.000000001");
3948        insta::assert_snapshot!(
3949            printer().direction(Direction::ForceSign).duration_to_string(
3950                &SignedDuration::new(0, 1),
3951            ),
3952            @"+00:00:00.000000001",
3953        );
3954
3955        insta::assert_snapshot!(
3956            p(1, 123),
3957            @"00:00:01.000000123",
3958        );
3959        insta::assert_snapshot!(
3960            p(1, 123_000_000),
3961            @"00:00:01.123",
3962        );
3963        insta::assert_snapshot!(
3964            p(1, 1_123_000_000),
3965            @"00:00:02.123",
3966        );
3967        insta::assert_snapshot!(
3968            p(61, 1_123_000_000),
3969            @"00:01:02.123",
3970        );
3971    }
3972
3973    #[test]
3974    fn print_signed_duration_hms_fraction_fixed_precision() {
3975        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
3976        let p = |precision, secs, nanos| {
3977            printer()
3978                .precision(Some(precision))
3979                .duration_to_string(&SignedDuration::new(secs, nanos))
3980        };
3981
3982        insta::assert_snapshot!(p(3, 1, 0), @"00:00:01.000");
3983        insta::assert_snapshot!(
3984            p(3, 1, 1_000_000),
3985            @"00:00:01.001",
3986        );
3987        insta::assert_snapshot!(
3988            p(3, 1, 123_000_000),
3989            @"00:00:01.123",
3990        );
3991        insta::assert_snapshot!(
3992            p(3, 1, 100_000_000),
3993            @"00:00:01.100",
3994        );
3995
3996        insta::assert_snapshot!(p(0, 1, 0), @"00:00:01");
3997        insta::assert_snapshot!(p(0, 1, 1_000_000), @"00:00:01");
3998        insta::assert_snapshot!(
3999            p(1, 1, 999_000_000),
4000            @"00:00:01.9",
4001        );
4002    }
4003
4004    #[test]
4005    fn print_unsigned_duration_hms() {
4006        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
4007        let p = |secs| {
4008            printer().unsigned_duration_to_string(
4009                &core::time::Duration::from_secs(secs),
4010            )
4011        };
4012
4013        // Note the differences with `Span`, since with a `Duration`,
4014        // all units are balanced.
4015
4016        insta::assert_snapshot!(p(1), @"00:00:01");
4017        insta::assert_snapshot!(p(2), @"00:00:02");
4018        insta::assert_snapshot!(p(10), @"00:00:10");
4019        insta::assert_snapshot!(p(100), @"00:01:40");
4020
4021        insta::assert_snapshot!(p(1 * 60), @"00:01:00");
4022        insta::assert_snapshot!(p(2 * 60), @"00:02:00");
4023        insta::assert_snapshot!(p(10 * 60), @"00:10:00");
4024        insta::assert_snapshot!(p(100 * 60), @"01:40:00");
4025
4026        insta::assert_snapshot!(p(1 * 60 * 60), @"01:00:00");
4027        insta::assert_snapshot!(p(2 * 60 * 60), @"02:00:00");
4028        insta::assert_snapshot!(p(10 * 60 * 60), @"10:00:00");
4029        insta::assert_snapshot!(p(100 * 60 * 60), @"100:00:00");
4030
4031        insta::assert_snapshot!(
4032            p(60 * 60 + 60 + 1),
4033            @"01:01:01",
4034        );
4035        insta::assert_snapshot!(
4036            p(2 * 60 * 60 + 2 * 60 + 2),
4037            @"02:02:02",
4038        );
4039        insta::assert_snapshot!(
4040            p(10 * 60 * 60 + 10 * 60 + 10),
4041            @"10:10:10",
4042        );
4043        insta::assert_snapshot!(
4044            p(100 * 60 * 60 + 100 * 60 + 100),
4045            @"101:41:40",
4046        );
4047    }
4048
4049    #[test]
4050    fn print_unsigned_duration_hms_sign() {
4051        let printer = |direction| {
4052            SpanPrinter::new().hours_minutes_seconds(true).direction(direction)
4053        };
4054        let p = |direction, secs| {
4055            printer(direction).unsigned_duration_to_string(
4056                &core::time::Duration::from_secs(secs),
4057            )
4058        };
4059
4060        insta::assert_snapshot!(p(Direction::Auto, 1), @"00:00:01");
4061        insta::assert_snapshot!(p(Direction::Sign, 1), @"00:00:01");
4062        insta::assert_snapshot!(p(Direction::ForceSign, 1), @"+00:00:01");
4063        insta::assert_snapshot!(p(Direction::Suffix, 1), @"00:00:01");
4064    }
4065
4066    #[test]
4067    fn print_unsigned_duration_hms_fraction_auto() {
4068        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
4069        let p = |secs, nanos| {
4070            printer().unsigned_duration_to_string(&core::time::Duration::new(
4071                secs, nanos,
4072            ))
4073        };
4074
4075        insta::assert_snapshot!(p(0, 1), @"00:00:00.000000001");
4076        insta::assert_snapshot!(
4077            printer().direction(Direction::ForceSign).duration_to_string(
4078                &SignedDuration::new(0, 1),
4079            ),
4080            @"+00:00:00.000000001",
4081        );
4082
4083        insta::assert_snapshot!(
4084            p(1, 123),
4085            @"00:00:01.000000123",
4086        );
4087        insta::assert_snapshot!(
4088            p(1, 123_000_000),
4089            @"00:00:01.123",
4090        );
4091        insta::assert_snapshot!(
4092            p(1, 1_123_000_000),
4093            @"00:00:02.123",
4094        );
4095        insta::assert_snapshot!(
4096            p(61, 1_123_000_000),
4097            @"00:01:02.123",
4098        );
4099    }
4100
4101    #[test]
4102    fn print_unsigned_duration_hms_fraction_fixed_precision() {
4103        let printer = || SpanPrinter::new().hours_minutes_seconds(true);
4104        let p = |precision, secs, nanos| {
4105            printer().precision(Some(precision)).unsigned_duration_to_string(
4106                &core::time::Duration::new(secs, nanos),
4107            )
4108        };
4109
4110        insta::assert_snapshot!(p(3, 1, 0), @"00:00:01.000");
4111        insta::assert_snapshot!(
4112            p(3, 1, 1_000_000),
4113            @"00:00:01.001",
4114        );
4115        insta::assert_snapshot!(
4116            p(3, 1, 123_000_000),
4117            @"00:00:01.123",
4118        );
4119        insta::assert_snapshot!(
4120            p(3, 1, 100_000_000),
4121            @"00:00:01.100",
4122        );
4123
4124        insta::assert_snapshot!(p(0, 1, 0), @"00:00:01");
4125        insta::assert_snapshot!(p(0, 1, 1_000_000), @"00:00:01");
4126        insta::assert_snapshot!(
4127            p(1, 1, 999_000_000),
4128            @"00:00:01.9",
4129        );
4130    }
4131
4132    /// Tests that we can write the maximal string successfully.
4133    ///
4134    /// This test doesn't guarantee that we'll always attempt the true
4135    /// maximum. The maximums here were determined by human inspection.
4136    #[test]
4137    fn maximums() {
4138        let p = SpanPrinter::new()
4139            .padding(u8::MAX)
4140            .designator(Designator::Verbose)
4141            .spacing(Spacing::BetweenUnitsAndDesignators)
4142            .comma_after_designator(true);
4143
4144        let span = -19_998
4145            .year()
4146            .months(239_976)
4147            .weeks(1_043_497)
4148            .days(7_304_484)
4149            .hours(175_307_616)
4150            .minutes(10_518_456_960i64)
4151            .seconds(631_107_417_600i64)
4152            .milliseconds(631_107_417_600_000i64)
4153            .microseconds(631_107_416_600_000_000i64)
4154            .nanoseconds(9_223_372_036_854_775_807i64);
4155        insta::assert_snapshot!(
4156            p.span_to_string(&span),
4157            @"00000000000000019998 years, 00000000000000239976 months, 00000000000001043497 weeks, 00000000000007304484 days, 00000000000175307616 hours, 00000000010518456960 minutes, 00000000631107417600 seconds, 00000631107417600000 milliseconds, 00631107416600000000 microseconds, 09223372036854775807 nanoseconds ago",
4158        );
4159
4160        let sdur = SignedDuration::MAX;
4161        insta::assert_snapshot!(
4162            p.duration_to_string(&sdur),
4163            @"00002562047788015215 hours, 00000000000000000030 minutes, 00000000000000000007 seconds, 00000000000000000999 milliseconds, 00000000000000000999 microseconds, 00000000000000000999 nanoseconds",
4164        );
4165
4166        let udur =
4167            core::time::Duration::MAX - core::time::Duration::from_secs(16);
4168        insta::assert_snapshot!(
4169            p.unsigned_duration_to_string(&udur),
4170            @"00005124095576030430 hours, 00000000000000000059 minutes, 00000000000000000059 seconds, 00000000000000000999 milliseconds, 00000000000000000999 microseconds, 00000000000000000999 nanoseconds",
4171        );
4172    }
4173}