1pub use parse_duration::parse::Error as ParseError;
8
9use crate::math::AsPrimitive;
10use crate::prelude::*;
11use parse_duration::parse;
12
13#[derive(Clone, Copy, Default, PartialEq, PartialOrd)]
18pub struct Duration {
19 secs: f64,
20}
21
22impl Duration {
23 pub const INFINITE: Self = Self { secs: f64::INFINITY };
25
26 pub const ZERO: Self = Self { secs: 0.0 };
28
29 pub fn weeks(weeks: impl AsPrimitive<f64>) -> Self {
31 Self::secs(weeks.as_() * 7.0 * 24.0 * 60.0 * 60.0)
32 }
33
34 pub fn days(days: impl AsPrimitive<f64>) -> Self {
36 Self::secs(days.as_() * 24.0 * 60.0 * 60.0)
37 }
38
39 pub fn hours(hours: impl AsPrimitive<f64>) -> Self {
41 Self::secs(hours.as_() * 60.0 * 60.0)
42 }
43
44 pub fn mins(mins: impl AsPrimitive<f64>) -> Self {
46 Self::secs(mins.as_() * 60.0)
47 }
48
49 pub fn secs(secs: impl AsPrimitive<f64>) -> Self {
51 let secs = secs.as_();
52
53 assert!(!secs.is_nan(), "Duration cannot be NaN.");
54
55 Self { secs: secs.max(0.0) }
56 }
57
58 pub fn hz(hz: impl AsPrimitive<f64>) -> Self {
60 Self::secs(1.0 / hz.as_())
61 }
62
63 pub fn ms(ms: impl AsPrimitive<f64>) -> Duration {
65 Self::secs(ms.as_() / 1000.0)
66 }
67
68 pub fn as_weeks(self) -> f64 {
70 self.as_secs() / 7.0 / 24.0 / 60.0 / 60.0
71 }
72
73 pub fn as_days(self) -> f64 {
75 self.as_secs() / 24.0 / 60.0 / 60.0
76 }
77
78 pub fn as_hours(self) -> f64 {
80 self.as_secs() / 60.0 / 60.0
81 }
82
83 pub fn as_mins(self) -> f64 {
85 self.as_secs() / 60.0
86 }
87
88 pub const fn as_secs(self) -> f64 {
90 self.secs
91 }
92
93 pub fn as_hz(self) -> f64 {
95 1.0 / self.secs
96 }
97
98 pub fn as_ms(self) -> f64 {
100 self.secs * 1000.0
101 }
102
103 pub fn is_finite(&self) -> bool {
105 self.secs.is_finite()
106 }
107
108 pub fn is_infinite(&self) -> bool {
110 self.secs.is_infinite()
111 }
112
113 pub fn to_std(self) -> std::time::Duration {
115 const MAX_SAFE_INT: f64 = 9007199254740991f64;
117
118 std::time::Duration::from_secs_f64(self.secs.min(MAX_SAFE_INT))
119 }
120}
121
122impl Add<Self> for Duration {
123 type Output = Self;
124
125 fn add(mut self, rhs: Self) -> Self::Output {
126 self += rhs;
127 self
128 }
129}
130
131impl AddAssign<Self> for Duration {
132 fn add_assign(&mut self, rhs: Self) {
133 self.secs += rhs.secs;
134 }
135}
136
137impl Debug for Duration {
138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 write!(f, "\"{}\"", self)
140 }
141}
142
143impl Display for Duration {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 if self.secs.is_infinite() {
146 write!(f, "forever")
147 } else if self.secs < 2.0 {
148 write!(f, "{} ms", self.as_ms().round_to_places(3))
149 } else if self.secs < 120.0 {
150 write!(f, "{} secs", self.as_secs().round_to_places(3))
151 } else if self.secs < 7_200.0 {
152 write!(f, "{} mins", self.as_mins().round_to_places(2))
153 } else if self.secs < 172_800.0 {
154 write!(f, "{} hours", self.as_hours().round_to_places(2))
155 } else if self.secs < 604_800.0 {
156 write!(f, "{} days", self.as_days().round_to_places(2))
157 } else if self.secs < 31_557_600.0 {
158 write!(f, "{} weeks", self.as_weeks().round_to_places(1))
159 } else {
160 write!(f, "{} years", (self.secs / 31_557_600.0).round_to_places(1))
161 }
162 }
163}
164
165impl<T> Div<T> for Duration
166where
167 T: AsPrimitive<f64>,
168{
169 type Output = Self;
170
171 fn div(mut self, rhs: T) -> Self::Output {
172 self /= rhs;
173 self
174 }
175}
176
177impl<T> DivAssign<T> for Duration
178where
179 T: AsPrimitive<f64>,
180{
181 fn div_assign(&mut self, rhs: T) {
182 self.secs = f64::max(self.secs / rhs.as_(), 0.0);
183 }
184}
185
186impl Eq for Duration {}
187
188impl From<std::time::Duration> for Duration {
189 fn from(value: std::time::Duration) -> Self {
190 Self::secs(value.as_secs_f64())
191 }
192}
193
194impl From<chrono::Duration> for Duration {
195 fn from(value: chrono::Duration) -> Self {
196 value.to_std().unwrap_or_default().into()
197 }
198}
199
200impl From<Duration> for std::time::Duration {
201 fn from(value: Duration) -> Self {
202 value.to_std()
203 }
204}
205
206impl From<Duration> for chrono::Duration {
207 fn from(value: Duration) -> Self {
208 Self::from_std(value.to_std())
209 .expect("Failed to convert std::time::Duration to chrono::Duration")
210 }
211}
212
213impl FromStr for Duration {
214 type Err = ParseError;
215
216 fn from_str(s: &str) -> Result<Self, Self::Err> {
217 parse(s).map(From::from)
218 }
219}
220
221impl<T> Mul<T> for Duration
222where
223 T: AsPrimitive<f64>,
224{
225 type Output = Self;
226
227 fn mul(mut self, rhs: T) -> Self::Output {
228 self *= rhs;
229 self
230 }
231}
232
233impl<T> MulAssign<T> for Duration
234where
235 T: AsPrimitive<f64>,
236{
237 fn mul_assign(&mut self, rhs: T) {
238 self.secs = f64::max(self.secs * rhs.as_(), 0.0);
239 }
240}
241
242#[allow(clippy::derive_ord_xor_partial_ord)]
243impl Ord for Duration {
244 fn cmp(&self, other: &Self) -> cmp::Ordering {
245 self.secs.partial_cmp(&other.secs).unwrap()
246 }
247}
248
249impl Sub<Self> for Duration {
250 type Output = Self;
251
252 fn sub(mut self, rhs: Self) -> Self::Output {
253 self -= rhs;
254 self
255 }
256}
257
258impl SubAssign<Self> for Duration {
259 fn sub_assign(&mut self, rhs: Self) {
260 self.secs = f64::max(self.secs - rhs.secs, 0.0);
261 }
262}