1#[cfg(feature = "serde")]
34use serde::{de, ser, Serializer};
35
36use std::{
37 fmt,
38 ops::{Add, Sub},
39 time::{Duration, SystemTime, UNIX_EPOCH},
40};
41
42#[derive(Debug, PartialEq, Copy, Clone)]
51pub struct Seconds(f64);
52
53impl fmt::Display for Seconds {
54 fn fmt(
55 &self,
56 f: &mut fmt::Formatter,
57 ) -> fmt::Result {
58 write!(f, "{}", self.0)
59 }
60}
61
62impl Seconds {
63 pub fn now() -> Self {
65 Self::from_duration(
66 SystemTime::now()
67 .duration_since(UNIX_EPOCH)
68 .unwrap_or_default(),
69 )
70 }
71
72 pub fn trunc(self) -> Self {
74 Self(self.0.trunc())
75 }
76
77 fn from_duration(dur: Duration) -> Self {
81 Seconds(dur.as_secs() as f64 + (f64::from(dur.subsec_nanos()) / 1.0e9))
82 }
83}
84
85impl Default for Seconds {
86 fn default() -> Self {
87 Seconds::now()
88 }
89}
90
91impl Into<f64> for Seconds {
92 fn into(self) -> f64 {
93 let Seconds(secs) = self;
94 secs
95 }
96}
97
98impl Add<Duration> for Seconds {
100 type Output = Seconds;
101 fn add(
102 self,
103 rhs: Duration,
104 ) -> Self::Output {
105 let lhs: Duration = self.into();
106 Seconds::from_duration(lhs + rhs)
107 }
108}
109
110impl Sub<Duration> for Seconds {
112 type Output = Seconds;
113 fn sub(
114 self,
115 rhs: Duration,
116 ) -> Self::Output {
117 let lhs: Duration = self.into();
118 Seconds::from_duration(lhs - rhs)
119 }
120}
121
122impl Into<Duration> for Seconds {
123 fn into(self) -> Duration {
124 let Seconds(secs) = self;
125 Duration::new(secs.trunc() as u64, (secs.fract() * 1.0e9) as u32)
126 }
127}
128
129#[cfg(feature = "serde")]
130struct SecondsVisitor;
131
132#[cfg(feature = "serde")]
133impl<'de> de::Visitor<'de> for SecondsVisitor {
134 type Value = Seconds;
135
136 fn expecting(
137 &self,
138 formatter: &mut fmt::Formatter,
139 ) -> fmt::Result {
140 formatter.write_str("floating point seconds")
141 }
142 fn visit_f64<E>(
143 self,
144 value: f64,
145 ) -> Result<Seconds, E>
146 where
147 E: de::Error,
148 {
149 Ok(Seconds(value))
150 }
151}
152
153#[cfg(feature = "serde")]
154impl ser::Serialize for Seconds {
155 fn serialize<S>(
156 &self,
157 serializer: S,
158 ) -> Result<S::Ok, S::Error>
159 where
160 S: Serializer,
161 {
162 let Seconds(seconds) = self;
163 serializer.serialize_f64(*seconds)
164 }
165}
166
167#[cfg(feature = "serde")]
168impl<'de> de::Deserialize<'de> for Seconds {
169 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170 where
171 D: de::Deserializer<'de>,
172 {
173 deserializer.deserialize_f64(SecondsVisitor)
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::Seconds;
180 use std::time::Duration;
181
182 #[test]
183 fn seconds_default() {
184 let (now, default) = (Seconds::default(), Seconds::now());
185 assert_eq!(now.trunc(), default.trunc());
186 }
187
188 #[test]
189 fn seconds_deref() {
190 let secs = Seconds(1_545_136_342.711_932);
191 let _f: f64 = secs.into();
192 }
193
194 #[test]
195 fn seconds_display() {
196 let secs = Seconds(1_545_136_342.711_932);
197 assert_eq!(format!("{}", secs), "1545136342.711932");
198 }
199
200 #[test]
201 fn seconds_duration_interop() {
202 let secs = Seconds(1_545_136_342.711_932);
203 let duration: Duration = secs.into();
204 assert_eq!(duration.as_secs(), 1_545_136_342);
205 }
206
207 #[test]
208 fn seconds_add_duration() {
209 let secs = Seconds(1_545_136_342.711_932);
210 assert_eq!(
211 secs + Duration::from_secs(1),
212 Seconds(1_545_136_343.711_932)
213 );
214 }
215
216 #[test]
217 fn seconds_sub_duration() {
218 let secs = Seconds(1_545_136_342.711_932);
219 assert_eq!(
220 secs - Duration::from_secs(1),
221 Seconds(1_545_136_341.711_932)
222 );
223 }
224
225 #[cfg(feature = "serde")]
226 #[test]
227 fn seconds_serialize() {
228 assert_eq!(
229 serde_json::to_string(&Seconds(1_545_136_342.711_932)).expect("failed to serialize"),
230 "1545136342.711932"
231 );
232 }
233
234 #[cfg(feature = "serde")]
235 #[test]
236 fn seconds_deserialize_floats() {
237 assert_eq!(
238 serde_json::from_slice::<Seconds>(b"1545136342.711932").expect("failed to serialize"),
239 Seconds(1_545_136_342.711_932)
240 );
241 }
242
243 #[cfg(feature = "serde")]
244 #[test]
245 fn seconds_fails_to_deserialize() {
246 match serde_json::from_slice::<Seconds>(b"{\"foo\":\"bar\"}") {
247 Err(err) => assert_eq!(
248 format!("{}", err),
249 "invalid type: map, expected floating point seconds at line 1 column 0"
250 ),
251 Ok(other) => panic!("unexpected result {}", other),
252 }
253 }
254}