eva_common/
time.rs

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/// Time
19///
20/// Serialized as f64
21/// Deserialized from unsigned integers (seconds), floats, [sec, nsec] seqs
22///
23/// With "db" feature provides sqlx interfaces for Sqlite (stored as nanoseconds integer) and
24/// Postgres (stored as TIMESTAMP/TIMESTAMPTZ)
25#[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    /// # Panics
152    ///
153    /// Will panic if the system real-time clock is not available
154    /// Will panic on Windows if the clock is set before 1.1.1970
155    #[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    /// On Windows returns time since the first access
172    ///
173    /// # Panics
174    ///
175    /// Will panic if the system monotonic clock is not available
176    #[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
297/// # Panics
298///
299/// Will panic if duration in nanoseconds > u64::MAX
300impl 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
362/// # Panics
363///
364/// Will panic if duration in nanoseconds > u64::MAX
365impl 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/// Get monotonic time in seconds
448///
449/// # Panics
450///
451/// Will panic if the monotonic clock is not available
452#[inline]
453pub fn monotonic() -> u64 {
454    Time::now_monotonic().timestamp_sec()
455}
456
457/// Get monotonic time in nanoseconds
458///
459/// # Panics
460///
461/// Will panic if the monotonic clock is not available
462#[inline]
463pub fn monotonic_ns() -> u64 {
464    Time::now_monotonic().timestamp_ns()
465}
466
467/// Get current UNIX timestamp in seconds
468///
469/// # Panics
470///
471/// Will panic if the system clock is not available
472#[allow(clippy::cast_sign_loss)]
473pub fn now() -> u64 {
474    Time::now().timestamp_sec()
475}
476
477/// Get current UNIX timestamp in seconds as a float
478///
479/// # Panics
480///
481/// Will panic if the system clock is not available
482#[inline]
483pub fn now_ns_float() -> f64 {
484    Time::now().timestamp()
485}
486
487/// Get current UNIX timestamp in nanoseconds
488///
489/// # Panics
490///
491/// Will panic if the system clock is not available
492pub fn now_ns() -> u64 {
493    Time::now().timestamp_ns()
494}
495
496/// Convert f64 timestamp to nanoseconds
497#[inline]
498pub fn ts_to_ns(ts: f64) -> u64 {
499    let t = Time::from_timestamp(ts);
500    t.timestamp_ns()
501}
502
503/// Convert nanoseconds to f64 timestamp
504#[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}