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;