Skip to main content

windows_file_enumeration_sys/
timestamp.rs

1// Copyright (c) 2026 Mike Grier
2//! Native Windows timestamps, kept native.
3//!
4//! A directory record reports each of its four times as a signed count of
5//! 100-nanosecond intervals since 1601-01-01 UTC. [`WindowsFileTimestamp`] is
6//! that count and nothing else: no epoch shift, no saturation, no timezone, and
7//! no reinterpretation of a zero or negative value.
8//!
9//! Converting eagerly to a Unix epoch would be lossy in three directions at
10//! once -- range, precision, and sentinel meaning -- and the loss would be
11//! unrecoverable by the time a caller saw it. A caller that wants civil time
12//! converts at the point it knows which of those trade-offs it can accept.
13
14use std::fmt;
15
16use windows_sys::Win32::Foundation::FILETIME;
17
18/// A Windows file time: signed 100-nanosecond ticks since 1601-01-01 UTC.
19///
20/// Ordering is ordering of the raw tick count, which is what makes a timestamp
21/// comparison in a query mean the same thing as a comparison of the underlying
22/// record fields. Filesystem sentinels participate as their raw values: a `0`
23/// change time on a filesystem that does not track one compares as less than
24/// every real time, and is not silently promoted to "unknown".
25#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub struct WindowsFileTimestamp(i64);
27
28impl WindowsFileTimestamp {
29    /// The zero tick count, which is 1601-01-01 UTC and the value filesystems
30    /// commonly report for a time they do not track.
31    pub const ZERO: Self = Self(0);
32
33    /// Wrap a raw tick count.
34    #[must_use]
35    pub const fn from_ticks(ticks: i64) -> Self {
36        Self(ticks)
37    }
38
39    /// The raw tick count.
40    #[must_use]
41    pub const fn ticks(self) -> i64 {
42        self.0
43    }
44
45    /// Convert from the Microsoft two-word [`FILETIME`] representation.
46    ///
47    /// Directory records carry these times as a single `i64` already, so this
48    /// exists for interoperation with the many Win32 APIs that hand out a
49    /// `FILETIME` instead -- not because the crate stores one.
50    #[must_use]
51    pub const fn from_filetime(time: FILETIME) -> Self {
52        let ticks = ((time.dwHighDateTime as u64) << 32) | (time.dwLowDateTime as u64);
53        Self(ticks as i64)
54    }
55
56    /// Convert to the Microsoft two-word [`FILETIME`] representation.
57    #[must_use]
58    pub const fn to_filetime(self) -> FILETIME {
59        let ticks = self.0 as u64;
60        FILETIME {
61            dwLowDateTime: ticks as u32,
62            dwHighDateTime: (ticks >> 32) as u32,
63        }
64    }
65}
66
67impl fmt::Display for WindowsFileTimestamp {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        write!(f, "{} ticks", self.0)
70    }
71}
72
73impl From<i64> for WindowsFileTimestamp {
74    fn from(ticks: i64) -> Self {
75        Self(ticks)
76    }
77}
78
79impl From<WindowsFileTimestamp> for i64 {
80    fn from(timestamp: WindowsFileTimestamp) -> Self {
81        timestamp.0
82    }
83}
84
85#[cfg(test)]
86mod tests;