smb_dtyp/binrw_util/
file_time.rs1use std::fmt::Display;
5use std::ops::Deref;
6use std::time::{Duration, SystemTime};
7
8use binrw::prelude::*;
9use time::PrimitiveDateTime;
10use time::macros::datetime;
11
12#[derive(BinRead, BinWrite, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
13pub struct FileTime {
14 value: u64,
16}
17
18impl FileTime {
19 const EPOCH: PrimitiveDateTime = datetime!(1601-01-01 00:00:00);
20 const SCALE_VALUE_TO_NANOS: u64 = 100;
21 const SCALE_VALUE_TO_SECS: u64 = 1_000_000_000 / Self::SCALE_VALUE_TO_NANOS;
22
23 pub fn date_time(&self) -> PrimitiveDateTime {
27 let duration = core::time::Duration::from_nanos(self.value * Self::SCALE_VALUE_TO_NANOS);
28 Self::EPOCH + duration
29 }
30
31 pub const ZERO: FileTime = FileTime { value: 0 };
33
34 pub fn is_zero(&self) -> bool {
38 self.value == 0
39 }
40
41 pub fn since_epoch(&self) -> Duration {
45 let secs = self.value / Self::SCALE_VALUE_TO_SECS;
46 let nanos = self.value % Self::SCALE_VALUE_TO_SECS * Self::SCALE_VALUE_TO_NANOS;
47 Duration::new(secs, nanos as u32)
48 }
49}
50
51impl Display for FileTime {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 self.date_time().fmt(f)
54 }
55}
56
57impl std::fmt::Debug for FileTime {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 f.debug_tuple("FileTime").field(&self.date_time()).finish()
60 }
61}
62
63impl From<u64> for FileTime {
64 fn from(value: u64) -> Self {
65 Self { value }
66 }
67}
68
69impl From<PrimitiveDateTime> for FileTime {
70 fn from(dt: PrimitiveDateTime) -> Self {
71 let duration = dt - Self::EPOCH;
72 Self {
73 value: duration.whole_nanoseconds() as u64 / Self::SCALE_VALUE_TO_NANOS,
74 }
75 }
76}
77
78impl From<FileTime> for PrimitiveDateTime {
79 fn from(val: FileTime) -> Self {
80 val.date_time()
81 }
82}
83
84impl Deref for FileTime {
85 type Target = u64;
86
87 fn deref(&self) -> &Self::Target {
88 &self.value
89 }
90}
91
92impl From<FileTime> for SystemTime {
93 fn from(src: FileTime) -> SystemTime {
94 let epoch = SystemTime::from(FileTime::EPOCH.as_utc());
95 epoch + src.since_epoch()
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use time::macros::datetime;
103
104 const TEST_VAL1_U64: u64 = 133818609802776324;
105 const TEST_VAL1_DT: PrimitiveDateTime = datetime!(2025-01-20 15:36:20.277632400);
106
107 #[test]
108 pub fn test_file_time_from_u64_correct() {
109 assert_eq!(FileTime::from(TEST_VAL1_U64).date_time(), TEST_VAL1_DT);
110 let result: SystemTime = FileTime::from(TEST_VAL1_U64).into();
111 assert_eq!(time::UtcDateTime::from(result), TEST_VAL1_DT.as_utc());
112 }
113
114 #[test]
115 pub fn test_file_time_from_datetime_correct() {
116 assert_eq!(*FileTime::from(TEST_VAL1_DT), TEST_VAL1_U64)
117 }
118
119 #[test]
120 pub fn test_zero_file_time() {
121 let ft = FileTime::ZERO;
122 assert!(ft.is_zero());
123 assert_eq!(ft.date_time(), FileTime::EPOCH);
124 }
125}