1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/// Helper for formatting times.
#[derive(Debug, Eq, PartialEq)]
pub struct TimeParts {
    pub hours: u32,
    pub mins: u32,
    pub secs: u64,
    pub nanos: u64,
}

impl TimeParts {
    pub fn from_nanos(total_nanos: u128) -> Self {
        // Total number of integer seconds.
        let whole_secs = u64::try_from(total_nanos / 1_000_000_000).unwrap();
        // Total number of integer minutes.
        let whole_mins = u32::try_from(whole_secs / 60).unwrap();
        // Total number of integer hours, which is also the hours part.
        let whole_hours = whole_mins / 60;

        // Integer minutes, after whole hours are subtracted out.
        let mins_part = whole_mins - whole_hours * 60;
        // Integers secs, after integer minutes are subtracted out.
        let secs_part = whole_secs - u64::from(whole_mins) * 60;
        // Nanos, after integer secs are subtracted out.
        let nanos_part =
            u64::try_from(total_nanos - u128::from(whole_secs) * 1_000_000_000).unwrap();

        Self {
            hours: whole_hours,
            mins: mins_part,
            secs: secs_part,
            nanos: nanos_part,
        }
    }

    /// Format as HH:MM:SS.
    pub fn fmt_hr_min_sec(&self) -> TimePartsFmtHrMinSec {
        TimePartsFmtHrMinSec { time: self }
    }

    /// Format as HH:MM:SS.mmm.
    pub fn fmt_hr_min_sec_milli(&self) -> TimePartsFmtHrMinSecMilli {
        TimePartsFmtHrMinSecMilli { time: self }
    }

    /// Format as HH:MM:SS.nnnnnnnnn.
    pub fn fmt_hr_min_sec_nano(&self) -> TimePartsFmtHrMinSecNano {
        TimePartsFmtHrMinSecNano { time: self }
    }
}

pub struct TimePartsFmtHrMinSec<'a> {
    time: &'a TimeParts,
}

impl std::fmt::Display for TimePartsFmtHrMinSec<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{:02}:{:02}:{:02}",
            self.time.hours, self.time.mins, self.time.secs
        )
    }
}

pub struct TimePartsFmtHrMinSecMilli<'a> {
    time: &'a TimeParts,
}

impl std::fmt::Display for TimePartsFmtHrMinSecMilli<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{:02}:{:02}:{:02}.{:03}",
            self.time.hours,
            self.time.mins,
            self.time.secs,
            self.time.nanos / 1_000_000
        )
    }
}

pub struct TimePartsFmtHrMinSecNano<'a> {
    time: &'a TimeParts,
}

impl std::fmt::Display for TimePartsFmtHrMinSecNano<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{:02}:{:02}:{:02}.{:09}",
            self.time.hours, self.time.mins, self.time.secs, self.time.nanos,
        )
    }
}

#[cfg(test)]
#[test]
fn test_time_parts() {
    use std::time::Duration;
    assert_eq!(
        TimeParts::from_nanos(
            (Duration::from_nanos(1) + Duration::from_secs(3600 + 60 + 1)).as_nanos()
        ),
        TimeParts {
            hours: 1,
            mins: 1,
            secs: 1,
            nanos: 1
        }
    );
}