mag/time/
mod.rs

1// time.rs
2//
3// Copyright (C) 2019-2021  Minnesota Department of Transportation
4// Copyright (C) 2019-2022  Douglas P Lau
5//
6//! Units of time.
7//!
8//! Each unit is defined relative to seconds with a conversion factor.  They
9//! can be used to conveniently create [Period] and [Frequency] structs.
10//!
11//! ## Example
12//!
13//! ```rust
14//! use mag::time::{s, ms, ns};
15//!
16//! let a = 22.8 * s; // Period<s>
17//! let b = 50.6 * ms; // Period<ms>
18//! let c = 60.0 / s; // Frequency<s>
19//! let d = 3.1234 / ns; // Frequency<ns>
20//!
21//! assert_eq!(a.to_string(), "22.8 s");
22//! assert_eq!(b.to_string(), "50.6 ms");
23//! assert_eq!(c.to_string(), "60 ㎐");
24//! assert_eq!(format!("{:.2}", d), "3.12 ㎓");
25//! ```
26//! [Frequency]: ../struct.Frequency.html
27//! [Period]: ../struct.Period.html
28//!
29pub(crate) mod timepriv;
30
31/// Unit definition for time
32pub trait Unit {
33    /// Unit label
34    const LABEL: &'static str;
35
36    /// Inverse unit label
37    const INVERSE: &'static str;
38
39    /// Multiplication factor to convert to seconds
40    const S_FACTOR: f64;
41
42    /// Multiplication factor to convert to another unit
43    fn factor<T: Unit>() -> f64 {
44        Self::S_FACTOR / T::S_FACTOR
45    }
46}
47
48/// Define a custom [unit] of [time]
49///
50/// * `unit` Unit struct name
51/// * `label` Standard unit label
52/// * `inverse` Inverse time unit (frequency)
53/// * `s_factor` Factor to convert to seconds
54///
55/// # Example: Fortnight
56/// ```rust
57/// use mag::{time_unit, time::h};
58///
59/// time_unit!(
60///     Fortnight,
61///     "fortnight",
62///     "/fortnight",
63///     14.0 * 24.0 * 60.0 * 60.0
64/// );
65///
66/// let f = 1 * Fortnight;
67/// assert_eq!(f.to::<h>(), 24 * 14 * h);
68/// ```
69///
70/// [time]: struct.Period.html
71/// [unit]: time/trait.Unit.html
72#[macro_export]
73macro_rules! time_unit {
74    (
75        $(#[$doc:meta])* $unit:ident,
76        $label:expr,
77        $inverse:expr,
78        $s_factor:expr
79    ) => {
80        $(#[$doc])*
81        #[allow(non_camel_case_types)]
82        #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
83        pub struct $unit;
84
85        impl $crate::time::Unit for $unit {
86            const LABEL: &'static str = $label;
87            const INVERSE: &'static str = $inverse;
88            const S_FACTOR: f64 = $s_factor;
89        }
90
91        // f64 * <unit> => Period
92        impl core::ops::Mul<$unit> for f64 {
93            type Output = $crate::Period<$unit>;
94            fn mul(self, _other: $unit) -> Self::Output {
95                $crate::Period::new(self)
96            }
97        }
98
99        // i32 * <unit> => Period
100        impl core::ops::Mul<$unit> for i32 {
101            type Output = $crate::Period<$unit>;
102            fn mul(self, _other: $unit) -> Self::Output {
103                $crate::Period::new(f64::from(self))
104            }
105        }
106
107        // f64 / <unit> => Frequency
108        impl core::ops::Div<$unit> for f64 {
109            type Output = $crate::Frequency<$unit>;
110            fn div(self, _other: $unit) -> Self::Output {
111                $crate::Frequency::new(self)
112            }
113        }
114
115        // i32 / <unit> => Frequency
116        impl core::ops::Div<$unit> for i32 {
117            type Output = $crate::Frequency<$unit>;
118            fn div(self, _other: $unit) -> Self::Output {
119                $crate::Frequency::new(f64::from(self))
120            }
121        }
122
123        // Length / <unit> => Speed
124        impl<L> core::ops::Div<$unit> for $crate::Length<L>
125        where
126            L: $crate::length::Unit
127        {
128            type Output = $crate::Speed<L, $unit>;
129            fn div(self, _unit: $unit) -> Self::Output {
130                $crate::Speed::new(self.quantity)
131            }
132        }
133    };
134}
135
136time_unit!(
137    /** Gigasecond */
138    Gs,
139    "Gs",
140    "nHz",
141    1_000_000_000.0
142);
143
144time_unit!(
145    /** Megasecond */
146    Ms,
147    "Ms",
148    "μHz",
149    1_000_000.0
150);
151
152time_unit!(
153    /** Kilosecond */
154    Ks,
155    "Ks",
156    "mHz",
157    1_000.0
158);
159
160time_unit!(
161    /** Week */
162    wk,
163    "wk",
164    "/wk",
165    7.0 * 24.0 * 60.0 * 60.0
166);
167
168time_unit!(
169    /** Day */
170    d,
171    "d",
172    "/d",
173    24.0 * 60.0 * 60.0
174);
175
176time_unit!(
177    /** Hour */
178    h,
179    "h",
180    "/h",
181    60.0 * 60.0
182);
183
184time_unit!(
185    /** Minute */
186    min,
187    "min",
188    "/min",
189    60.0
190);
191
192time_unit!(
193    /** Second */
194    s,
195    "s",
196    "㎐",
197    1.0
198);
199
200time_unit!(
201    /** Decisecond */
202    ds,
203    "ds",
204    "daHz",
205    0.1
206);
207
208time_unit!(
209    /** Millisecond */
210    ms,
211    "ms",
212    "㎑",
213    0.001
214);
215
216time_unit!(
217    /** Microsecond */
218    us,
219    "μs",
220    "㎒",
221    0.000_001
222);
223
224time_unit!(
225    /** Nanosecond */
226    ns,
227    "ns",
228    "㎓",
229    0.000_000_001
230);
231
232time_unit!(
233    /** Picosecond */
234    ps,
235    "ps",
236    "㎔",
237    0.000_000_000_001
238);
239
240#[cfg(test)]
241mod test {
242    extern crate alloc;
243
244    use super::super::Frequency;
245    use super::*;
246    use alloc::{format, string::ToString};
247
248    #[test]
249    fn time_display() {
250        assert_eq!((23.7 * s).to_string(), "23.7 s");
251        assert_eq!((3.25 * h).to_string(), "3.25 h");
252        assert_eq!((50.0 / s).to_string(), "50 ㎐");
253        assert_eq!((2.0 / d).to_string(), "2 /d");
254        assert_eq!(format!("{:.1}", 333.3333 / us), "333.3 ㎒");
255    }
256
257    #[test]
258    fn time_to() {
259        assert_eq!((4.75 * h).to(), 285.0 * min);
260        assert_eq!((2.5 * s).to(), 2_500.0 * ms);
261        assert_eq!((1_000.0 / s).to(), 1.0 / ms);
262        assert_eq!((300.0 / ms).to(), 0.3 / us);
263    }
264
265    #[test]
266    fn time_add() {
267        assert_eq!(3.5 * d + 1.25 * d, 4.75 * d);
268        assert_eq!(1.0 * wk + 2.1 * wk, 3.1 * wk);
269        assert_eq!(5.0 / ns + 4.0 / ns, 9.0 / ns);
270    }
271
272    #[test]
273    fn time_sub() {
274        assert_eq!(567.8 * us - 123.4 * us, 444.4 * us);
275        assert_eq!(23.0 / ms - 12.0 / ms, 11.0 / ms);
276    }
277
278    #[test]
279    fn time_mul() {
280        assert_eq!((6.5 * ns) * 12.0, 78.0 * ns);
281        assert_eq!(4.0 * (1.5 * h), 6.0 * h);
282        assert_eq!(2.5 / ds * 2.0, 5.0 / ds);
283    }
284
285    #[test]
286    fn time_div() {
287        assert_eq!(5. / h, Frequency::<h>::new(5.0));
288        assert_eq!(60.0 / s, Frequency::<s>::new(60.0));
289        assert_eq!(1.0 / (1.0 * s), 1.0 / s);
290        assert_eq!(2.0 / (1.0 / min), 2.0 * min);
291    }
292}