bma_ts/
timestamp.rs

1use std::time::{Duration, SystemTime, UNIX_EPOCH};
2
3use crate::Error;
4
5#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
6pub struct Timestamp(pub(crate) Duration);
7
8const ANSI_EPOCH_DIFF_NANOS: u64 = 11_644_473_600_000_000_000;
9
10impl Timestamp {
11    /// # Panics
12    ///
13    /// Will panic if the system time is below 1.01.1970
14    pub fn now() -> Self {
15        Self(
16            SystemTime::now()
17                .duration_since(UNIX_EPOCH)
18                .expect("System time is below UNIX EPOCH"),
19        )
20    }
21    pub fn now_rounded() -> Self {
22        let t = Self::now();
23        Self(Duration::from_secs(t.0.as_secs()))
24    }
25    #[inline]
26    pub fn elapsed(self) -> Result<Duration, Error> {
27        let now = Timestamp::now();
28        if now >= self {
29            Ok(now.0 - self.0)
30        } else {
31            Err(Error::TimeWentBackward)
32        }
33    }
34    #[inline]
35    pub fn duration_since(self, earlier: Self) -> Result<Duration, Error> {
36        if self >= earlier {
37            Ok(self.0 - earlier.0)
38        } else {
39            Err(Error::TimeWentBackward)
40        }
41    }
42    /// Converts between ANSI (Windows, 1601-01-01) and UNIX (1970-01-01) timestamps
43    /// The source timestamp MUST be in nanoseconds (for Windows timestamp - multiply the source by
44    /// 100)
45    ///
46    /// # Example
47    ///
48    /// ```rust
49    /// use bma_ts::Timestamp;
50    ///
51    /// let windows_timestamp = 133_575_013_473_650_000;
52    /// let unix_timestamp = Timestamp::from_nanos(windows_timestamp * 100)
53    ///     .try_from_ansi_to_unix().unwrap();
54    /// assert_eq!(unix_timestamp.as_nanos(), 1_713_027_747_365_000_000);
55    /// ```
56    pub fn try_from_ansi_to_unix(self) -> Result<Self, Error> {
57        Ok(Self(Duration::from_nanos(
58            u64::try_from(self.0.as_nanos())?
59                .checked_sub(ANSI_EPOCH_DIFF_NANOS)
60                .ok_or_else(|| Error::Convert("Failed to convert from ANSI to UNIX".to_string()))?,
61        )))
62    }
63    /// Converts between UNIX (1970-01-01) and ANSI (Windows, 1601-01-01) timestamps
64    ///
65    /// The result timestamp is in nanoseconds (for Windows timestamp - divide the target by 100)
66    pub fn try_from_unix_to_ansi(self) -> Result<Self, Error> {
67        Ok(Self(Duration::from_nanos(
68            u64::try_from(self.0.as_nanos())?
69                .checked_add(ANSI_EPOCH_DIFF_NANOS)
70                .ok_or_else(|| Error::Convert("Failed to convert from UNIX to ANSI".to_string()))?,
71        )))
72    }
73}