sevenz_rust2/
time.rs

1/// An error that can be thrown when converting to [`NtTime`]
2#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
3pub enum NtTimeError {
4    Negative,
5    Overflow,
6}
7
8/// A type that represents a Windows file time and is used in the 7z archive format.
9///
10/// Can easily be converted to and from [`std::time::SystemTime`].
11///
12/// The feature flag `nt-time` implements conversions for [`nt_time::FileTime`].
13#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
14pub struct NtTime(pub(crate) u64);
15
16impl Default for NtTime {
17    fn default() -> Self {
18        Self::NT_TIME_EPOCH
19    }
20}
21
22impl NtTime {
23    const FILE_TIMES_PER_SEC: u64 = 10_000_000;
24
25    /// The [`NtTime`] of the  unix epoch (1970-01-01).
26    pub const UNIX_EPOCH: NtTime = NtTime::new(134774 * 86400 * Self::FILE_TIMES_PER_SEC);
27
28    /// The epoch of the [`NtTime`] (0001-01-01).
29    pub const NT_TIME_EPOCH: NtTime = NtTime::new(0);
30
31    /// Creates a new [`NtTime`] with the given file time.
32    #[must_use]
33    #[inline]
34    pub const fn new(ft: u64) -> Self {
35        Self(ft)
36    }
37
38    #[must_use]
39    #[inline]
40    pub fn now() -> Self {
41        use std::time::SystemTime;
42
43        SystemTime::now()
44            .try_into()
45            .expect("the current date and time is not a valid NtTime")
46    }
47
48    fn sub(self, rhs: Self) -> std::time::Duration {
49        let duration = self.0 - rhs.0;
50        std::time::Duration::new(
51            duration / Self::FILE_TIMES_PER_SEC,
52            u32::try_from((duration % Self::FILE_TIMES_PER_SEC) * 100)
53                .expect("the number of nanoseconds is not a valid `u32`"),
54        )
55    }
56}
57
58impl From<u64> for NtTime {
59    /// Converts the file time to a [`NtTime`].
60    #[inline]
61    fn from(file_time: u64) -> Self {
62        Self::new(file_time)
63    }
64}
65
66impl From<NtTime> for u64 {
67    /// Converts the [`NtTime`] into a file time.
68    #[inline]
69    fn from(nt_time: NtTime) -> Self {
70        nt_time.0
71    }
72}
73
74impl TryFrom<i64> for NtTime {
75    type Error = NtTimeError;
76
77    /// Converts the file time to a [`NtTime`].
78    #[inline]
79    fn try_from(file_time: i64) -> Result<Self, Self::Error> {
80        file_time
81            .try_into()
82            .map_err(|_| NtTimeError::Negative)
83            .map(Self::new)
84    }
85}
86
87impl From<NtTime> for std::time::SystemTime {
88    /// Converts a [`NtTime`] to a [`SystemTime`](std::time::SystemTime).
89    #[inline]
90    fn from(file_time: NtTime) -> Self {
91        let duration = std::time::Duration::new(
92            file_time.0 / NtTime::FILE_TIMES_PER_SEC,
93            u32::try_from((file_time.0 % NtTime::FILE_TIMES_PER_SEC) * 100)
94                .expect("the number of nanoseconds is not a valid `u32`"),
95        );
96
97        (std::time::SystemTime::UNIX_EPOCH - (NtTime::UNIX_EPOCH.sub(NtTime::NT_TIME_EPOCH)))
98            + duration
99    }
100}
101
102impl TryFrom<std::time::SystemTime> for NtTime {
103    type Error = NtTimeError;
104
105    /// Converts a [`SystemTime`](std::time::SystemTime) to a `FileTime`.
106    #[inline]
107    fn try_from(st: std::time::SystemTime) -> Result<Self, Self::Error> {
108        use std::time::SystemTime;
109
110        let elapsed = st
111            .duration_since(
112                SystemTime::UNIX_EPOCH - (NtTime::UNIX_EPOCH.sub(NtTime::NT_TIME_EPOCH)),
113            )
114            .map(|d| d.as_nanos())
115            .map_err(|_| NtTimeError::Negative)?;
116
117        let file_time = u64::try_from(elapsed / 100).map_err(|_| NtTimeError::Overflow)?;
118
119        Ok(Self::new(file_time))
120    }
121}
122
123#[cfg_attr(docsrs, doc(cfg(feature = "nt-time")))]
124#[cfg(feature = "nt-time")]
125impl From<NtTime> for nt_time::FileTime {
126    fn from(value: NtTime) -> Self {
127        Self::new(value.0)
128    }
129}
130
131#[cfg_attr(docsrs, doc(cfg(feature = "nt-time")))]
132#[cfg(feature = "nt-time")]
133impl From<nt_time::FileTime> for NtTime {
134    fn from(value: nt_time::FileTime) -> Self {
135        Self::new(value.to_raw())
136    }
137}