1const FILETIME_TO_UNIX_100NS: i128 = 116_444_736_000_000_000;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
13pub struct Filetime(pub u64);
14
15impl Filetime {
16 #[must_use]
18 pub fn from_le(bytes: &[u8; 8]) -> Self {
19 Filetime(u64::from_le_bytes(*bytes))
20 }
21
22 #[must_use]
24 pub fn is_zero(&self) -> bool {
25 self.0 == 0
26 }
27
28 #[must_use]
30 pub fn to_unix_seconds(&self) -> i64 {
31 let ticks_since_unix = i128::from(self.0) - FILETIME_TO_UNIX_100NS;
32 (ticks_since_unix / 10_000_000) as i64
33 }
34
35 #[must_use]
37 pub fn to_unix_nanos(&self) -> i128 {
38 (i128::from(self.0) - FILETIME_TO_UNIX_100NS) * 100
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn zero_is_unset() {
48 assert!(Filetime(0).is_zero());
49 assert!(!Filetime(1).is_zero());
50 }
51
52 #[test]
53 fn filetime_epoch_maps_to_unix_zero() {
54 assert_eq!(Filetime(116_444_736_000_000_000).to_unix_seconds(), 0);
56 }
57
58 #[test]
59 fn known_date_converts() {
60 let ft = Filetime(129_067_776_000_000_000);
62 assert_eq!(ft.to_unix_seconds(), 1_262_304_000);
63 }
64
65 #[test]
66 fn pre_unix_epoch_is_negative() {
67 let ft = Filetime(116_444_736_000_000_000 - 10_000_000);
69 assert_eq!(ft.to_unix_seconds(), -1);
70 }
71
72 #[test]
73 fn nanos_granularity() {
74 let ft = Filetime(116_444_736_000_000_000 + 1);
76 assert_eq!(ft.to_unix_nanos(), 100);
77 }
78
79 #[test]
80 fn from_le_reads_little_endian() {
81 let ft = Filetime::from_le(&1u64.to_le_bytes());
82 assert_eq!(ft.0, 1);
83 }
84}