Skip to main content

bma_ts/
timestamp.rs

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