uom/si/
time.rs

1//! Time (base unit second, s).
2
3use crate::lib::time::Duration;
4use crate::num::{FromPrimitive, ToPrimitive, Zero};
5
6quantity! {
7    /// Time (base unit second, s).
8    quantity: Time; "time";
9    /// Dimension of time, T (base unit second, s).
10    dimension: ISQ<
11        Z0,     // length
12        Z0,     // mass
13        P1,     // time
14        Z0,     // electric current
15        Z0,     // thermodynamic temperature
16        Z0,     // amount of substance
17        Z0>;    // luminous intensity
18    units {
19        @yottasecond: prefix!(yotta); "Ys", "yottasecond", "yottaseconds";
20        @zettasecond: prefix!(zetta); "Zs", "zettasecond", "zettaseconds";
21        @exasecond: prefix!(exa); "Es", "exasecond", "exaseconds";
22        @petasecond: prefix!(peta); "Ps", "petasecond", "petaseconds";
23        @terasecond: prefix!(tera); "Ts", "terasecond", "teraseconds";
24        @gigasecond: prefix!(giga); "Gs", "gigasecond", "gigaseconds";
25        @megasecond: prefix!(mega); "Ms", "megasecond", "megaseconds";
26        @kilosecond: prefix!(kilo); "ks", "kilosecond", "kiloseconds";
27        @hectosecond: prefix!(hecto); "hs", "hectosecond", "hectoseconds";
28        @decasecond: prefix!(deca); "das", "decasecond", "decaseconds";
29        /// The second is the SI unit of time. It is defined by taking the fixed numerical value of
30        /// the caesium frequency ∆*ν*<sub>Cs</sub>, the unperturbed ground-state hyperfine
31        /// transition frequency of the caesium 133 atom, to be 9 192 631 770 when expressed in the
32        /// unit Hz, which is equal to s⁻¹.
33        @second: prefix!(none); "s", "second", "seconds";
34        @decisecond: prefix!(deci); "ds", "decisecond", "deciseconds";
35        @centisecond: prefix!(centi); "cs", "centisecond", "centiseconds";
36        @millisecond: prefix!(milli); "ms", "millisecond", "milliseconds";
37        @microsecond: prefix!(micro); "µs", "microsecond", "microseconds";
38        @nanosecond: prefix!(nano); "ns", "nanosecond", "nanoseconds";
39        @picosecond: prefix!(pico); "ps", "picosecond", "picoseconds";
40        @femtosecond: prefix!(femto); "fs", "femtosecond", "femtoseconds";
41        @attosecond: prefix!(atto); "as", "attosecond", "attoseconds";
42        @zeptosecond: prefix!(zepto); "zs", "zeptosecond", "zeptoseconds";
43        @yoctosecond: prefix!(yocto); "ys", "yoctosecond", "yoctoseconds";
44
45        @second_sidereal: 9.972_696_E-1; "s (sidereal)", "second (sidereal)", "seconds (sidereal)";
46        @day: 8.64_E4; "d", "day", "days";
47        @day_sidereal: 8.616_409_E4; "d (sidereal)", "day (sidereal)", "days (sidereal)";
48        @hour: 3.6_E3; "h", "hour", "hours";
49        @hour_sidereal: 3.590_170_E3; "h (sidereal)", "hour (sidereal)", "hours (sidereal)";
50        @minute: 6.0_E1; "min", "minute", "minutes";
51        @shake: 1.0_E-8; "10.0 ns", "shake", "shakes";
52        @year: 3.1536_E7; "a", "year", "years";
53        @year_sidereal: 3.155_815_E7; "a (sidereal)", "year (sidereal)", "years (sidereal)";
54        @year_tropical: 3.155_693_E7; "a (tropical)", "year (tropical)", "years (tropical)";
55    }
56}
57
58/// An error encountered converting between `Time` and `Duration`.
59#[derive(Debug, Clone, Copy)]
60pub enum TryFromError {
61    /// The given time interval was negative, making conversion to a duration nonsensical.
62    ///
63    /// To convert a negative time interval to a duration, first use `abs` to make it positive.
64    NegativeDuration,
65
66    /// The given time interval exceeded the maximum size of a `Duration`.
67    Overflow,
68}
69
70/// Attempt to convert the given `Time` to a `Duration`.
71///
72/// For possible failure modes see [`TryFromError`][TryFromError].
73///
74/// ## Notes
75///
76/// The `Duration` to `Time` conversion is tested to be accurate to within 1 nanosecond (to allow
77/// for floating point rounding error). If greater precision is needed, consider using a different
78/// underlying storage type or avoiding the conversion altogether.
79///
80/// [TryFromError]: enum.TryFromError.html
81impl<U, V> crate::lib::convert::TryFrom<Time<U, V>> for Duration
82where
83    U: crate::si::Units<V> + ?Sized,
84    V: crate::num::Num + crate::Conversion<V> + PartialOrd + ToPrimitive,
85    second: crate::Conversion<V, T = V::T>,
86    nanosecond: crate::Conversion<V, T = V::T>,
87{
88    type Error = TryFromError;
89
90    fn try_from(time: Time<U, V>) -> Result<Self, Self::Error> {
91        if time < Time::<U, V>::zero() {
92            return Err(TryFromError::NegativeDuration);
93        }
94
95        let secs = time.get::<second>().to_u64();
96        let nanos = (time % Time::<U, V>::new::<second>(V::one())).get::<nanosecond>().to_u32();
97
98        match (secs, nanos) {
99            (Some(secs), Some(nanos)) => Ok(Self::new(secs, nanos)),
100            _ => Err(TryFromError::Overflow),
101        }
102    }
103}
104
105/// Attempt to convert the given `Duration` to a `Time`.
106///
107/// For possible failure modes, see [`TryFromError`][TryFromError].
108///
109/// ## Notes
110///
111/// The `Duration` to `Time` conversion is tested to be accurate to within 100 nanoseconds (to
112/// allow for floating point rounding error). If greater precision is needed, consider using a
113/// different underlying storage type or avoiding the conversion altogether.
114///
115/// [TryFromError]: enum.TryFromError.html
116impl<U, V> crate::lib::convert::TryFrom<Duration> for Time<U, V>
117where
118    U: crate::si::Units<V> + ?Sized,
119    V: crate::num::Num + crate::Conversion<V> + FromPrimitive,
120    second: crate::Conversion<V, T = V::T>,
121    nanosecond: crate::Conversion<V, T = V::T>,
122{
123    type Error = TryFromError;
124
125    fn try_from(duration: Duration) -> Result<Self, Self::Error> {
126        let secs = V::from_u64(duration.as_secs());
127        let nanos = V::from_u32(duration.subsec_nanos());
128
129        match (secs, nanos) {
130            (Some(secs), Some(nanos)) => {
131                Ok(Time::<U, V>::new::<second>(secs) + Time::<U, V>::new::<nanosecond>(nanos))
132            }
133            _ => Err(TryFromError::Overflow),
134        }
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    storage_types! {
141        types: PrimInt, BigInt, BigUint, Float;
142
143        use crate::ConversionFactor;
144        use crate::lib::convert::TryFrom;
145        use crate::lib::time::Duration;
146        use crate::num::{FromPrimitive, ToPrimitive, One, Zero};
147        use crate::si::quantities::*;
148        use crate::si::time::{TryFromError, second, nanosecond};
149        use crate::tests::*;
150        use quickcheck::TestResult;
151
152        quickcheck! {
153            fn duration_try_from(v: A<V>) -> bool {
154                let ns: V = <nanosecond as crate::Conversion<V>>::coefficient().value();
155                let t = Time::new::<second>((*v).clone());
156                let d = Duration::try_from(t);
157                let r = (*v).clone() % V::one();
158                let s = ((*v).clone() - r.clone()).to_u64();
159                let n = (r * (V::one() / &ns)).to_u32();
160
161                match (d, s, n) {
162                    (Ok(d), Some(s), Some(n)) => d.as_secs() == s && d.subsec_nanos() == n,
163                    (Err(TryFromError::NegativeDuration), _, _) if *v < V::zero() => true,
164                    (Err(TryFromError::Overflow), None, _) => true,
165                    (Err(TryFromError::Overflow), _, None) => true,
166                    _ => false,
167                }
168            }
169
170            fn time_try_from(v: A<V>) -> TestResult {
171                if *v < V::zero()  {
172                    return TestResult::discard();
173                }
174
175                let ns: V = <nanosecond as crate::Conversion<V>>::coefficient().value();
176                let r = (*v).clone() % V::one();
177                let s = ((*v).clone() - r.clone()).to_u64();
178                let n = (r * (V::one() / &ns)).to_u32();
179
180                return match (s, n) {
181                        (Some(s), Some(n)) => TestResult::from_bool(
182                            match (Time::try_from(Duration::new(s, n)), V::from_u64(s), V::from_u32(n)) {
183                                (Ok(t), Some(s), Some(n)) => t == Time::new::<second>(s) + Time::new::<nanosecond>(n),
184                                (Err(TryFromError::Overflow), None, _) => true,
185                                (Err(TryFromError::Overflow), _, None) => true,
186                                _ => false,
187                            }),
188                        _ => TestResult::discard(),
189                    }
190            }
191        }
192    }
193}