af_core/time/
duration.rs

1// Copyright © 2020 Alexandra Frydl
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7pub use parse_duration::parse::Error as ParseError;
8
9use crate::math::AsPrimitive;
10use crate::prelude::*;
11use parse_duration::parse;
12
13/// A duration of time.
14///
15/// The duration is stored as a 64-bit floating point number of seconds and
16/// cannot be negative.
17#[derive(Clone, Copy, Default, PartialEq, PartialOrd)]
18pub struct Duration {
19  secs: f64,
20}
21
22impl Duration {
23  /// An infinite duration.
24  pub const INFINITE: Self = Self { secs: f64::INFINITY };
25
26  /// A zero-length duration.
27  pub const ZERO: Self = Self { secs: 0.0 };
28
29  /// Returns a duration of the given number of weeks.
30  pub fn weeks(weeks: impl AsPrimitive<f64>) -> Self {
31    Self::secs(weeks.as_() * 7.0 * 24.0 * 60.0 * 60.0)
32  }
33
34  /// Returns a duration of the given number of days.
35  pub fn days(days: impl AsPrimitive<f64>) -> Self {
36    Self::secs(days.as_() * 24.0 * 60.0 * 60.0)
37  }
38
39  /// Returns a duration of the given number of hours.
40  pub fn hours(hours: impl AsPrimitive<f64>) -> Self {
41    Self::secs(hours.as_() * 60.0 * 60.0)
42  }
43
44  /// Returns a duration of the given number of minutes.
45  pub fn mins(mins: impl AsPrimitive<f64>) -> Self {
46    Self::secs(mins.as_() * 60.0)
47  }
48
49  /// Returns a duration of the given number of seconds.
50  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  /// Returns a duration of the given number of Hz.
59  pub fn hz(hz: impl AsPrimitive<f64>) -> Self {
60    Self::secs(1.0 / hz.as_())
61  }
62
63  /// Returns a duration of the given number of milliseconds.
64  pub fn ms(ms: impl AsPrimitive<f64>) -> Duration {
65    Self::secs(ms.as_() / 1000.0)
66  }
67
68  /// Return the duration as a number of weeks.
69  pub fn as_weeks(self) -> f64 {
70    self.as_secs() / 7.0 / 24.0 / 60.0 / 60.0
71  }
72
73  /// Return the duration as a number of days.
74  pub fn as_days(self) -> f64 {
75    self.as_secs() / 24.0 / 60.0 / 60.0
76  }
77
78  /// Return the duration as a number of hours.
79  pub fn as_hours(self) -> f64 {
80    self.as_secs() / 60.0 / 60.0
81  }
82
83  /// Return the duration as a number of minutes.
84  pub fn as_mins(self) -> f64 {
85    self.as_secs() / 60.0
86  }
87
88  /// Return the duration as a number of seconds.
89  pub const fn as_secs(self) -> f64 {
90    self.secs
91  }
92
93  /// Return the duration as a number of Hz.
94  pub fn as_hz(self) -> f64 {
95    1.0 / self.secs
96  }
97
98  /// Return the duration as a number of milliseconds.
99  pub fn as_ms(self) -> f64 {
100    self.secs * 1000.0
101  }
102
103  /// Returns `true` if this duration represents a finite amount of time.
104  pub fn is_finite(&self) -> bool {
105    self.secs.is_finite()
106  }
107
108  /// Returns `true` if this duration represents an infinite amount of time.
109  pub fn is_infinite(&self) -> bool {
110    self.secs.is_infinite()
111  }
112
113  /// Converts this duration to a `std::time::Duration`.
114  pub fn to_std(self) -> std::time::Duration {
115    /// The maximum f64 value with whole number precision.
116    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}