kine_core/
duration.rs

1use core::{
2    fmt::{self, Debug, Display},
3    ops::{Add, AddAssign, Sub, SubAssign},
4};
5
6/// A (signed) Duration.
7///
8/// It can represent time intervals of roughly 10^22 years either way.
9#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
10pub struct Duration {
11    nanos: i128,
12}
13
14const NANOS_IN_MICROS: i128 = 1000;
15const NANOS_IN_MILLIS: i128 = NANOS_IN_MICROS * 1000;
16const NANOS_IN_SECS: i128 = NANOS_IN_MILLIS * 1000;
17const NANOS_IN_MINS: i128 = NANOS_IN_SECS * 60;
18const NANOS_IN_HOURS: i128 = NANOS_IN_MINS * 60;
19const NANOS_IN_DAYS: i128 = NANOS_IN_HOURS * 24;
20const NANOS_IN_WEEKS: i128 = NANOS_IN_DAYS * 7;
21
22impl Duration {
23    /// Duration of zero, between a given time point and itself
24    pub const ZERO: Duration = Duration { nanos: 0 };
25
26    /// Create a Duration that lasts for `nanos` nanoseconds
27    ///
28    /// This cannot panic.
29    pub const fn from_nanos(nanos: i128) -> Duration {
30        Duration { nanos }
31    }
32
33    /// Create a Duration that lasts for `micros` microseconds
34    ///
35    /// This cannot panic.
36    pub const fn from_micros(micros: i64) -> Duration {
37        Duration {
38            nanos: micros as i128 * NANOS_IN_MICROS,
39        }
40    }
41
42    /// Create a Duration that lasts for `millis` milliseconds
43    ///
44    /// This cannot panic.
45    pub const fn from_millis(millis: i64) -> Duration {
46        Duration {
47            nanos: millis as i128 * NANOS_IN_MILLIS,
48        }
49    }
50
51    /// Create a Duration that lasts for `secs` seconds
52    ///
53    /// This cannot panic.
54    pub const fn from_secs(secs: i64) -> Duration {
55        Duration {
56            nanos: secs as i128 * NANOS_IN_SECS,
57        }
58    }
59
60    /// Create a Duration that lasts for `mins` minutes
61    ///
62    /// This cannot panic.
63    pub const fn from_minutes(mins: i64) -> Duration {
64        Duration {
65            nanos: mins as i128 * NANOS_IN_MINS,
66        }
67    }
68
69    /// Create a Duration that lasts for `hours` hours
70    ///
71    /// This cannot panic.
72    pub const fn from_hours(hours: i64) -> Duration {
73        Duration {
74            nanos: hours as i128 * NANOS_IN_HOURS,
75        }
76    }
77
78    /// Create a Duration that lasts for `days` days
79    ///
80    /// This cannot panic.
81    pub const fn from_days(days: i64) -> Duration {
82        Duration {
83            nanos: days as i128 * NANOS_IN_DAYS,
84        }
85    }
86
87    /// Create a Duration that lasts for `weeks` weeks
88    ///
89    /// This cannot panic.
90    pub const fn from_weeks(weeks: i64) -> Duration {
91        Duration {
92            nanos: weeks as i128 * NANOS_IN_WEEKS,
93        }
94    }
95
96    /// Retrieve the number of nanoseconds this Duration is
97    pub const fn nanos(&self) -> i128 {
98        self.nanos
99    }
100
101    /// Retrieve the number of microseconds this Duration is (rounded to zero)
102    pub const fn micros(&self) -> i128 {
103        self.nanos / NANOS_IN_MICROS
104    }
105
106    /// Retrieve the number of milliseconds this Duration is (rounded to zero)
107    pub const fn millis(&self) -> i128 {
108        self.nanos / NANOS_IN_MILLIS
109    }
110
111    /// Retrieve the number of seconds this Duration is (rounded to zero)
112    pub const fn secs(&self) -> i128 {
113        self.nanos / NANOS_IN_SECS
114    }
115
116    /// Retrieve the number of minutes this Duration is (rounded to zero)
117    pub const fn mins(&self) -> i128 {
118        self.nanos / NANOS_IN_MINS
119    }
120
121    /// Retrieve the number of hours this Duration is (rounded to zero)
122    pub const fn hours(&self) -> i128 {
123        self.nanos / NANOS_IN_HOURS
124    }
125
126    /// Retrieve the number of days this Duration is (rounded to zero)
127    pub const fn days(&self) -> i128 {
128        self.nanos / NANOS_IN_DAYS
129    }
130
131    /// Retrieve the number of weeks this Duration is (rounded to zero)
132    pub const fn weeks(&self) -> i128 {
133        self.nanos / NANOS_IN_WEEKS
134    }
135
136    /// Add `rhs`, returning `None` on overflow
137    pub fn checked_add(&self, rhs: &Duration) -> Option<Self> {
138        self.nanos
139            .checked_add(rhs.nanos)
140            .map(|nanos| Duration { nanos })
141    }
142
143    /// Subtract `rhs`, returning `None` on overflow
144    pub fn checked_sub(&self, rhs: &Duration) -> Option<Self> {
145        self.nanos
146            .checked_sub(rhs.nanos)
147            .map(|nanos| Duration { nanos })
148    }
149}
150
151impl Add<Duration> for Duration {
152    type Output = Duration;
153
154    fn add(self, rhs: Self) -> Self::Output {
155        self.checked_add(&rhs)
156            .expect("overflow while adding durations")
157    }
158}
159
160impl AddAssign<Duration> for Duration {
161    fn add_assign(&mut self, rhs: Duration) {
162        *self = *self + rhs
163    }
164}
165
166impl Sub<Duration> for Duration {
167    type Output = Duration;
168
169    fn sub(self, rhs: Duration) -> Self::Output {
170        self.checked_sub(&rhs)
171            .expect("overflow while subtracting durations")
172    }
173}
174
175impl SubAssign<Duration> for Duration {
176    fn sub_assign(&mut self, rhs: Duration) {
177        *self = *self - rhs
178    }
179}
180
181impl Default for Duration {
182    fn default() -> Self {
183        Duration::ZERO
184    }
185}
186
187impl Debug for Duration {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        <Duration as Display>::fmt(self, f)
190    }
191}
192
193impl Display for Duration {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        if self.nanos % NANOS_IN_HOURS == 0 {
196            write!(f, "{}h", self.nanos / NANOS_IN_HOURS)
197        } else if self.nanos % NANOS_IN_MINS == 0 {
198            write!(f, "{}min", self.nanos / NANOS_IN_MINS)
199        } else if self.nanos % NANOS_IN_SECS == 0 {
200            write!(f, "{}s", self.nanos / NANOS_IN_SECS)
201        } else if self.nanos % NANOS_IN_MILLIS == 0 {
202            write!(f, "{}ms", self.nanos / NANOS_IN_MILLIS)
203        } else {
204            // Do not actually check micros: it's probably not worth it and greek
205            // letters may be surprising
206            write!(f, "{}ns", self.nanos)
207        }
208    }
209}