1#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
3pub enum NtTimeError {
4 Negative,
5 Overflow,
6}
7
8#[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 pub const UNIX_EPOCH: NtTime = NtTime::new(134774 * 86400 * Self::FILE_TIMES_PER_SEC);
27
28 pub const NT_TIME_EPOCH: NtTime = NtTime::new(0);
30
31 #[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 #[inline]
61 fn from(file_time: u64) -> Self {
62 Self::new(file_time)
63 }
64}
65
66impl From<NtTime> for u64 {
67 #[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 #[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 #[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 #[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}