1use crate::{EResult, Error, Value};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::fmt;
4use std::str::FromStr;
5use std::time::{Duration, Instant};
6use std::time::{SystemTime, UNIX_EPOCH};
7
8#[cfg(target_os = "windows")]
9static STARTED_AT: once_cell::sync::Lazy<Instant> = once_cell::sync::Lazy::new(|| Instant::now());
10
11pub fn serialize_time_now<S>(_value: &(), serializer: S) -> Result<S::Ok, S::Error>
12where
13 S: Serializer,
14{
15 serializer.serialize_f64(Time::now().into())
16}
17
18#[derive(Debug, Copy, Clone, Eq, PartialEq)]
26pub struct Time {
27 sec: u64,
28 nsec: u64,
29}
30
31impl FromStr for Time {
32 type Err = Error;
33 fn from_str(s: &str) -> EResult<Self> {
34 if let Ok(v) = s.parse::<f64>() {
35 Ok(v.into())
36 } else {
37 Ok(dateparser::parse(s).map_err(Error::invalid_data)?.into())
38 }
39 }
40}
41
42#[allow(clippy::module_name_repetitions)]
43pub fn deserialize_time<'de, D>(deserializer: D) -> Result<Time, D::Error>
44where
45 D: Deserializer<'de>,
46{
47 Ok(Time::from_timestamp(f64::deserialize(deserializer)?))
48}
49
50pub fn serialize_uptime<S>(value: &Instant, serializer: S) -> Result<S::Ok, S::Error>
51where
52 S: Serializer,
53{
54 serializer.serialize_f64(value.elapsed().as_secs_f64())
55}
56
57impl Serialize for Time {
58 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59 where
60 S: Serializer,
61 {
62 serializer.serialize_f64(self.timestamp())
63 }
64}
65
66struct TimeVisitor;
67
68impl<'de> serde::de::Visitor<'de> for TimeVisitor {
69 type Value = Time;
70
71 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
72 formatter.write_str("a string, float, an unsigned integer, or a 2-element array")
73 }
74
75 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
76 where
77 E: serde::de::Error,
78 {
79 Ok(Time {
80 sec: value,
81 nsec: 0,
82 })
83 }
84
85 fn visit_f32<E>(self, value: f32) -> Result<Self::Value, E>
86 where
87 E: serde::de::Error,
88 {
89 Ok(value.into())
90 }
91
92 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
93 where
94 E: serde::de::Error,
95 {
96 Ok(value.into())
97 }
98
99 fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
100 where
101 V: serde::de::SeqAccess<'de>,
102 {
103 let s: u64 = seq
104 .next_element()?
105 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
106 let ns: u64 = seq
107 .next_element()?
108 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
109 Ok(Time { sec: s, nsec: ns })
110 }
111 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
112 where
113 E: serde::de::Error,
114 {
115 value
116 .parse()
117 .map_err(|_| serde::de::Error::custom("invalid time string"))
118 }
119 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
120 where
121 E: serde::de::Error,
122 {
123 value
124 .parse()
125 .map_err(|_| serde::de::Error::custom("invalid time string"))
126 }
127}
128
129impl<'de> Deserialize<'de> for Time {
130 fn deserialize<D>(deserializer: D) -> Result<Time, D::Error>
131 where
132 D: Deserializer<'de>,
133 {
134 deserializer.deserialize_any(TimeVisitor)
135 }
136}
137
138impl Default for Time {
139 #[inline]
140 fn default() -> Self {
141 Self::now()
142 }
143}
144
145impl Time {
146 #[inline]
147 #[allow(clippy::similar_names)]
148 pub fn new(sec: u64, nsec: u64) -> Self {
149 Self { sec, nsec }
150 }
151 #[allow(clippy::cast_sign_loss)]
156 #[cfg(not(target_os = "windows"))]
157 #[inline]
158 pub fn now() -> Self {
159 let t = nix::time::clock_gettime(nix::time::ClockId::CLOCK_REALTIME).unwrap();
160 Self {
161 sec: t.tv_sec() as u64,
162 nsec: t.tv_nsec() as u64,
163 }
164 }
165 #[cfg(target_os = "windows")]
166 #[inline]
167 pub fn now() -> Self {
168 let t = SystemTime::now();
169 t.try_into().unwrap()
170 }
171 #[inline]
177 #[allow(clippy::cast_sign_loss)]
178 #[cfg(not(target_os = "windows"))]
179 pub fn now_monotonic() -> Self {
180 let t = nix::time::clock_gettime(nix::time::ClockId::CLOCK_MONOTONIC).unwrap();
181 Self {
182 sec: t.tv_sec() as u64,
183 nsec: t.tv_nsec() as u64,
184 }
185 }
186 #[cfg(target_os = "windows")]
187 #[inline]
188 pub fn now_monotonic() -> Self {
189 STARTED_AT.elapsed().into()
190 }
191 #[inline]
192 pub fn from_timestamp_ns(timestamp_ns: u64) -> Self {
193 Self {
194 sec: timestamp_ns / 1_000_000_000,
195 nsec: timestamp_ns % 1_000_000_000,
196 }
197 }
198 #[inline]
199 pub fn from_timestamp_us(timestamp_us: u64) -> Self {
200 Self {
201 sec: timestamp_us / 1_000_000,
202 nsec: timestamp_us % 1_000_000 * 1_000,
203 }
204 }
205 #[inline]
206 pub fn from_timestamp_ms(timestamp_ms: u64) -> Self {
207 Self {
208 sec: timestamp_ms / 1_000,
209 nsec: timestamp_ms % 1_000 * 1_000_000,
210 }
211 }
212 #[allow(clippy::cast_sign_loss)]
213 #[allow(clippy::cast_possible_truncation)]
214 #[inline]
215 pub fn from_timestamp(timestamp: f64) -> Self {
216 Self {
217 sec: timestamp.trunc() as u64,
218 nsec: (timestamp.fract() * 1_000_000_000_f64) as u64,
219 }
220 }
221 #[allow(clippy::cast_precision_loss)]
222 #[inline]
223 pub fn timestamp(&self) -> f64 {
224 self.sec as f64 + self.nsec as f64 / 1_000_000_000.0
225 }
226 #[inline]
227 pub fn timestamp_sec(&self) -> u64 {
228 self.sec
229 }
230 #[inline]
231 pub fn timestamp_ns(&self) -> u64 {
232 self.sec * 1_000_000_000 + self.nsec
233 }
234 #[inline]
235 pub fn timestamp_us(&self) -> u64 {
236 self.sec * 1_000_000 + self.nsec / 1_000
237 }
238 #[inline]
239 pub fn timestamp_ms(&self) -> u64 {
240 self.sec * 1_000 + self.nsec / 1_000_000
241 }
242}
243
244impl From<Time> for Value {
245 #[inline]
246 fn from(t: Time) -> Value {
247 Value::F64(t.timestamp())
248 }
249}
250
251impl From<Time> for f64 {
252 #[inline]
253 fn from(t: Time) -> f64 {
254 t.timestamp()
255 }
256}
257
258impl From<f64> for Time {
259 #[inline]
260 fn from(v: f64) -> Time {
261 Time::from_timestamp(v)
262 }
263}
264
265impl From<f32> for Time {
266 #[inline]
267 fn from(v: f32) -> Time {
268 Time::from_timestamp(v.into())
269 }
270}
271
272impl TryFrom<SystemTime> for Time {
273 type Error = Error;
274 #[inline]
275 fn try_from(t: SystemTime) -> EResult<Self> {
276 Ok(t.duration_since(UNIX_EPOCH)
277 .map_err(|_| Error::core("systime before UNIX EPOCH"))?
278 .into())
279 }
280}
281
282impl fmt::Display for Time {
283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284 write!(f, "{}", self.timestamp())
285 }
286}
287
288impl From<Duration> for Time {
289 fn from(v: Duration) -> Self {
290 Self {
291 sec: v.as_secs(),
292 nsec: u64::from(v.subsec_nanos()),
293 }
294 }
295}
296
297impl core::ops::Add<Duration> for Time {
301 type Output = Time;
302 fn add(self, dur: Duration) -> Time {
303 let t_ns = self.timestamp_ns() + u64::try_from(dur.as_nanos()).unwrap();
304 Time::from_timestamp_ns(t_ns)
305 }
306}
307
308impl core::ops::Add<f64> for Time {
309 type Output = Time;
310 fn add(self, value: f64) -> Time {
311 Time::from_timestamp(self.timestamp() + value)
312 }
313}
314
315impl core::ops::Sub<f64> for Time {
316 type Output = Time;
317 fn sub(self, value: f64) -> Time {
318 Time::from_timestamp(self.timestamp() - value)
319 }
320}
321
322impl core::ops::Add<u64> for Time {
323 type Output = Time;
324 fn add(self, value: u64) -> Time {
325 Time {
326 sec: self.sec + value,
327 nsec: self.nsec,
328 }
329 }
330}
331
332impl core::ops::Sub<u64> for Time {
333 type Output = Time;
334 fn sub(self, value: u64) -> Time {
335 Time {
336 sec: self.sec - value,
337 nsec: self.nsec,
338 }
339 }
340}
341
342impl core::ops::Add<u32> for Time {
343 type Output = Time;
344 fn add(self, value: u32) -> Time {
345 Time {
346 sec: self.sec + u64::from(value),
347 nsec: self.nsec,
348 }
349 }
350}
351
352impl core::ops::Sub<u32> for Time {
353 type Output = Time;
354 fn sub(self, value: u32) -> Time {
355 Time {
356 sec: self.sec - u64::from(value),
357 nsec: self.nsec,
358 }
359 }
360}
361
362impl core::ops::Sub<Duration> for Time {
366 type Output = Time;
367 fn sub(self, dur: Duration) -> Time {
368 let t_ns = self.timestamp_ns() - u64::try_from(dur.as_nanos()).unwrap();
369 Time::from_timestamp_ns(t_ns)
370 }
371}
372
373mod convert_chrono {
374 use super::Time;
375 use crate::{EResult, Error};
376 use chrono::{DateTime, Local, NaiveDateTime, Utc};
377
378 impl TryFrom<Time> for NaiveDateTime {
379 type Error = Error;
380 #[inline]
381 fn try_from(t: Time) -> EResult<Self> {
382 let dt = DateTime::from_timestamp(i64::try_from(t.sec)?, u32::try_from(t.nsec)?)
383 .ok_or_else(|| Error::invalid_data("unable to convert timestamp"))?;
384 Ok(dt.naive_local())
385 }
386 }
387 impl TryFrom<Time> for DateTime<Utc> {
388 type Error = Error;
389 fn try_from(t: Time) -> EResult<Self> {
390 let nt = NaiveDateTime::try_from(t)?;
391 let dt_utc = DateTime::<Utc>::from_naive_utc_and_offset(nt, Utc);
392 Ok(dt_utc)
393 }
394 }
395 impl TryFrom<Time> for DateTime<Local> {
396 type Error = Error;
397 fn try_from(t: Time) -> EResult<Self> {
398 let dt_utc = DateTime::<Utc>::try_from(t)?;
399 Ok(DateTime::from(dt_utc))
400 }
401 }
402
403 impl From<NaiveDateTime> for Time {
404 #[allow(deprecated)]
405 fn from(datetime: NaiveDateTime) -> Self {
406 Time {
407 sec: u64::try_from(datetime.and_utc().timestamp()).unwrap_or_default(),
408 nsec: u64::from(datetime.timestamp_subsec_nanos()),
409 }
410 }
411 }
412
413 impl From<DateTime<Utc>> for Time {
414 fn from(datetime: DateTime<Utc>) -> Self {
415 Time {
416 sec: u64::try_from(datetime.timestamp()).unwrap_or_default(),
417 nsec: u64::from(datetime.timestamp_subsec_nanos()),
418 }
419 }
420 }
421
422 impl From<DateTime<Local>> for Time {
423 fn from(datetime: DateTime<Local>) -> Self {
424 Time {
425 sec: u64::try_from(datetime.timestamp()).unwrap_or_default(),
426 nsec: u64::from(datetime.timestamp_subsec_nanos()),
427 }
428 }
429 }
430
431 impl Time {
432 #[inline]
433 pub fn try_into_naivedatetime(self) -> EResult<NaiveDateTime> {
434 self.try_into()
435 }
436 #[inline]
437 pub fn try_into_datetime_local(self) -> EResult<DateTime<Local>> {
438 self.try_into()
439 }
440 #[inline]
441 pub fn try_into_datetime_utc(self) -> EResult<DateTime<Utc>> {
442 self.try_into()
443 }
444 }
445}
446
447#[inline]
453pub fn monotonic() -> u64 {
454 Time::now_monotonic().timestamp_sec()
455}
456
457#[inline]
463pub fn monotonic_ns() -> u64 {
464 Time::now_monotonic().timestamp_ns()
465}
466
467#[allow(clippy::cast_sign_loss)]
473pub fn now() -> u64 {
474 Time::now().timestamp_sec()
475}
476
477#[inline]
483pub fn now_ns_float() -> f64 {
484 Time::now().timestamp()
485}
486
487pub fn now_ns() -> u64 {
493 Time::now().timestamp_ns()
494}
495
496#[inline]
498pub fn ts_to_ns(ts: f64) -> u64 {
499 let t = Time::from_timestamp(ts);
500 t.timestamp_ns()
501}
502
503#[inline]
505pub fn ts_from_ns(ts: u64) -> f64 {
506 let t = Time::from_timestamp_ns(ts);
507 t.timestamp()
508}
509
510#[cfg(test)]
511#[allow(clippy::float_cmp)]
512mod tests {
513 use super::Time;
514 #[test]
515 fn test_time() {
516 let timestamp = 1_632_093_707.189_334_9;
517 let time = Time::from_timestamp(timestamp);
518 assert_eq!(time.timestamp(), timestamp);
519 assert_eq!(time.timestamp_ns(), 1_632_093_707_189_334_869);
520 let timestamp_nanos = 1_632_093_707_123_456_789;
521 let time = Time::from_timestamp_ns(timestamp_nanos);
522 assert_eq!(time.timestamp_ns(), timestamp_nanos);
523 assert_eq!(time.timestamp(), 1_632_093_707.123_456_7);
524 assert_eq!(time.timestamp_ms(), timestamp_nanos / 1_000_000);
525 assert_eq!(time.timestamp_us(), timestamp_nanos / 1_000);
526 let timestamp_micros = 1_632_093_707_123_456;
527 let time = Time::from_timestamp_us(timestamp_micros);
528 assert_eq!(time.timestamp(), 1_632_093_707.123_456);
529 assert_eq!(time.timestamp_ms(), timestamp_micros / 1_000);
530 assert_eq!(time.timestamp_us(), timestamp_micros);
531 assert_eq!(time.timestamp_ns(), timestamp_micros * 1_000);
532 let timestamp_millis = 1_632_093_707_123;
533 let time = Time::from_timestamp_ms(timestamp_millis);
534 assert_eq!(time.timestamp(), 1_632_093_707.123);
535 assert_eq!(time.timestamp_ms(), timestamp_millis);
536 assert_eq!(time.timestamp_us(), timestamp_millis * 1_000);
537 assert_eq!(time.timestamp_ns(), timestamp_millis * 1_000_000);
538 }
539}