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