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    /// Returns the current system time as an [`NtTime`].
39    #[must_use]
40    #[inline]
41    pub fn now() -> Self {
42        use std::time::SystemTime;
43
44        SystemTime::now()
45            .try_into()
46            .expect("the current date and time is not a valid NtTime")
47    }
48
49    fn sub(self, rhs: Self) -> std::time::Duration {
50        let duration = self.0 - rhs.0;
51        std::time::Duration::new(
52            duration / Self::FILE_TIMES_PER_SEC,
53            u32::try_from((duration % Self::FILE_TIMES_PER_SEC) * 100)
54                .expect("the number of nanoseconds is not a valid `u32`"),
55        )
56    }
57}
58
59impl From<u64> for NtTime {
60    /// Converts the file time to a [`NtTime`].
61    #[inline]
62    fn from(file_time: u64) -> Self {
63        Self::new(file_time)
64    }
65}
66
67impl From<NtTime> for u64 {
68    /// Converts the [`NtTime`] into a file time.
69    #[inline]
70    fn from(nt_time: NtTime) -> Self {
71        nt_time.0
72    }
73}
74
75impl TryFrom<i64> for NtTime {
76    type Error = NtTimeError;
77
78    /// Converts the file time to a [`NtTime`].
79    #[inline]
80    fn try_from(file_time: i64) -> Result<Self, Self::Error> {
81        file_time
82            .try_into()
83            .map_err(|_| NtTimeError::Negative)
84            .map(Self::new)
85    }
86}
87
88impl From<NtTime> for std::time::SystemTime {
89    /// Converts a [`NtTime`] to a [`SystemTime`](std::time::SystemTime).
90    #[inline]
91    fn from(file_time: NtTime) -> Self {
92        let duration = std::time::Duration::new(
93            file_time.0 / NtTime::FILE_TIMES_PER_SEC,
94            u32::try_from((file_time.0 % NtTime::FILE_TIMES_PER_SEC) * 100)
95                .expect("the number of nanoseconds is not a valid `u32`"),
96        );
97
98        (std::time::SystemTime::UNIX_EPOCH - (NtTime::UNIX_EPOCH.sub(NtTime::NT_TIME_EPOCH)))
99            + duration
100    }
101}
102
103impl TryFrom<std::time::SystemTime> for NtTime {
104    type Error = NtTimeError;
105
106    /// Converts a [`SystemTime`](std::time::SystemTime) to a `FileTime`.
107    #[inline]
108    fn try_from(st: std::time::SystemTime) -> Result<Self, Self::Error> {
109        use std::time::SystemTime;
110
111        let elapsed = st
112            .duration_since(
113                SystemTime::UNIX_EPOCH - (NtTime::UNIX_EPOCH.sub(NtTime::NT_TIME_EPOCH)),
114            )
115            .map(|d| d.as_nanos())
116            .map_err(|_| NtTimeError::Negative)?;
117
118        let file_time = u64::try_from(elapsed / 100).map_err(|_| NtTimeError::Overflow)?;
119
120        Ok(Self::new(file_time))
121    }
122}
123
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(feature = "nt-time")]
132impl From<nt_time::FileTime> for NtTime {
133    fn from(value: nt_time::FileTime) -> Self {
134        Self::new(value.to_raw())
135    }
136}