nt_time/
file_time.rs

1// SPDX-FileCopyrightText: 2023 Shun Sakai
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! A [Windows file time].
6//!
7//! [Windows file time]: https://docs.microsoft.com/en-us/windows/win32/sysinfo/file-times
8
9mod cmp;
10mod consts;
11mod convert;
12mod dos_date_time;
13mod fmt;
14mod ops;
15#[cfg(feature = "rand")]
16mod rand;
17#[cfg(feature = "serde")]
18mod serde;
19mod str;
20mod unix_time;
21
22use core::mem;
23
24const FILE_TIMES_PER_SEC: u64 = 10_000_000;
25
26/// `FileTime` is a type that represents a [Windows file time].
27///
28/// This is a 64-bit unsigned integer value that represents the number of
29/// 100-nanosecond intervals that have elapsed since "1601-01-01 00:00:00 UTC",
30/// and is used as timestamps such as [NTFS] and [7z]. Windows uses a file time
31/// to record when an application creates, accesses, or writes to a file.
32///
33/// This represents the same value as the [`FILETIME`] structure of the [Win32
34/// API], which represents a 64-bit unsigned integer value.
35///
36/// <div class="warning">
37///
38/// Note that many environments, such as the Win32 API, may limit the largest
39/// value of the file time to "+30828-09-14 02:48:05.477580700 UTC", which is
40/// equal to [`i64::MAX`], the largest value of a 64-bit signed integer type
41/// when represented as an underlying integer value. This is the largest file
42/// time accepted by the [`FileTimeToSystemTime`] function of the Win32 API.
43///
44/// </div>
45///
46/// Also, the file time is sometimes represented as an [`i64`] value, such as in
47/// the [`DateTime.FromFileTimeUtc`] method and the [`DateTime.ToFileTimeUtc`]
48/// method in [.NET].
49///
50/// Therefore, if you want the process to succeed in more environments, it is
51/// generally recommended that you use [`FileTime::SIGNED_MAX`] as the largest
52/// value instead of [`FileTime::MAX`].
53///
54/// [Windows file time]: https://docs.microsoft.com/en-us/windows/win32/sysinfo/file-times
55/// [NTFS]: https://en.wikipedia.org/wiki/NTFS
56/// [7z]: https://www.7-zip.org/7z.html
57/// [`FILETIME`]: https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
58/// [Win32 API]: https://learn.microsoft.com/en-us/windows/win32/
59/// [`FileTimeToSystemTime`]: https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime
60/// [`DateTime.FromFileTimeUtc`]: https://learn.microsoft.com/en-us/dotnet/api/system.datetime.fromfiletimeutc
61/// [`DateTime.ToFileTimeUtc`]: https://learn.microsoft.com/en-us/dotnet/api/system.datetime.tofiletimeutc
62/// [.NET]: https://dotnet.microsoft.com/
63#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
64#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
65pub struct FileTime(u64);
66
67impl FileTime {
68    /// Returns the file time corresponding to "now".
69    ///
70    /// # Panics
71    ///
72    /// Panics if "now" is out of range for the file time.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// # use nt_time::FileTime;
78    /// #
79    /// let now = FileTime::now();
80    /// ```
81    #[cfg(feature = "std")]
82    #[must_use]
83    #[inline]
84    pub fn now() -> Self {
85        use std::time::SystemTime;
86
87        SystemTime::now()
88            .try_into()
89            .expect("the current date and time should be in the range of the file time")
90    }
91
92    /// Creates a new `FileTime` with the given file time.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// # use nt_time::FileTime;
98    /// #
99    /// assert_eq!(FileTime::new(u64::MIN), FileTime::NT_TIME_EPOCH);
100    /// assert_eq!(FileTime::new(116_444_736_000_000_000), FileTime::UNIX_EPOCH);
101    /// assert_eq!(FileTime::new(i64::MAX as u64), FileTime::SIGNED_MAX);
102    /// assert_eq!(FileTime::new(u64::MAX), FileTime::MAX);
103    /// ```
104    #[must_use]
105    #[inline]
106    pub const fn new(ft: u64) -> Self {
107        Self(ft)
108    }
109
110    /// Returns the contents of this `FileTime` as the underlying [`u64`] value.
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// # use nt_time::FileTime;
116    /// #
117    /// assert_eq!(FileTime::NT_TIME_EPOCH.to_raw(), u64::MIN);
118    /// assert_eq!(FileTime::UNIX_EPOCH.to_raw(), 116_444_736_000_000_000);
119    /// assert_eq!(FileTime::SIGNED_MAX.to_raw(), i64::MAX as u64);
120    /// assert_eq!(FileTime::MAX.to_raw(), u64::MAX);
121    /// ```
122    #[must_use]
123    #[inline]
124    pub const fn to_raw(self) -> u64 {
125        self.0
126    }
127
128    /// Returns the memory representation of this `FileTime` as a byte array in
129    /// big-endian (network) byte order.
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// # use nt_time::FileTime;
135    /// #
136    /// assert_eq!(FileTime::NT_TIME_EPOCH.to_be_bytes(), [u8::MIN; 8]);
137    /// assert_eq!(
138    ///     FileTime::UNIX_EPOCH.to_be_bytes(),
139    ///     [0x01, 0x9d, 0xb1, 0xde, 0xd5, 0x3e, 0x80, 0x00]
140    /// );
141    /// assert_eq!(
142    ///     FileTime::SIGNED_MAX.to_be_bytes(),
143    ///     [0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
144    /// );
145    /// assert_eq!(FileTime::MAX.to_be_bytes(), [u8::MAX; 8]);
146    /// ```
147    #[must_use]
148    #[inline]
149    pub const fn to_be_bytes(self) -> [u8; mem::size_of::<Self>()] {
150        self.to_raw().to_be_bytes()
151    }
152
153    /// Returns the memory representation of this `FileTime` as a byte array in
154    /// little-endian byte order.
155    ///
156    /// # Examples
157    ///
158    /// ```
159    /// # use nt_time::FileTime;
160    /// #
161    /// assert_eq!(FileTime::NT_TIME_EPOCH.to_le_bytes(), [u8::MIN; 8]);
162    /// assert_eq!(
163    ///     FileTime::UNIX_EPOCH.to_le_bytes(),
164    ///     [0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01]
165    /// );
166    /// assert_eq!(
167    ///     FileTime::SIGNED_MAX.to_le_bytes(),
168    ///     [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]
169    /// );
170    /// assert_eq!(FileTime::MAX.to_le_bytes(), [u8::MAX; 8]);
171    /// ```
172    #[must_use]
173    #[inline]
174    pub const fn to_le_bytes(self) -> [u8; mem::size_of::<Self>()] {
175        self.to_raw().to_le_bytes()
176    }
177
178    /// Returns the memory representation of this `FileTime` as a byte array in
179    /// native byte order.
180    ///
181    /// <div class="warning">
182    ///
183    /// As the target platform's native endianness is used, portable code should
184    /// use [`FileTime::to_be_bytes`] or [`FileTime::to_le_bytes`], as
185    /// appropriate, instead.
186    ///
187    /// </div>
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// # use nt_time::FileTime;
193    /// #
194    /// assert_eq!(FileTime::NT_TIME_EPOCH.to_ne_bytes(), [u8::MIN; 8]);
195    /// assert_eq!(
196    ///     FileTime::UNIX_EPOCH.to_ne_bytes(),
197    ///     if cfg!(target_endian = "big") {
198    ///         [0x01, 0x9d, 0xb1, 0xde, 0xd5, 0x3e, 0x80, 0x00]
199    ///     } else {
200    ///         [0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01]
201    ///     }
202    /// );
203    /// assert_eq!(
204    ///     FileTime::SIGNED_MAX.to_ne_bytes(),
205    ///     if cfg!(target_endian = "big") {
206    ///         [0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
207    ///     } else {
208    ///         [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]
209    ///     }
210    /// );
211    /// assert_eq!(FileTime::MAX.to_ne_bytes(), [u8::MAX; 8]);
212    /// ```
213    #[must_use]
214    #[inline]
215    pub const fn to_ne_bytes(self) -> [u8; mem::size_of::<Self>()] {
216        self.to_raw().to_ne_bytes()
217    }
218
219    /// Creates a native endian `FileTime` value from its representation as a
220    /// byte array in big endian.
221    ///
222    /// # Examples
223    ///
224    /// ```
225    /// # use nt_time::FileTime;
226    /// #
227    /// assert_eq!(
228    ///     FileTime::from_be_bytes([u8::MIN; 8]),
229    ///     FileTime::NT_TIME_EPOCH
230    /// );
231    /// assert_eq!(
232    ///     FileTime::from_be_bytes([0x01, 0x9d, 0xb1, 0xde, 0xd5, 0x3e, 0x80, 0x00]),
233    ///     FileTime::UNIX_EPOCH
234    /// );
235    /// assert_eq!(
236    ///     FileTime::from_be_bytes([0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
237    ///     FileTime::SIGNED_MAX
238    /// );
239    /// assert_eq!(FileTime::from_be_bytes([u8::MAX; 8]), FileTime::MAX);
240    /// ```
241    #[must_use]
242    #[inline]
243    pub const fn from_be_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
244        Self::new(u64::from_be_bytes(bytes))
245    }
246
247    /// Creates a native endian `FileTime` value from its representation as a
248    /// byte array in little endian.
249    ///
250    /// # Examples
251    ///
252    /// ```
253    /// # use nt_time::FileTime;
254    /// #
255    /// assert_eq!(
256    ///     FileTime::from_le_bytes([u8::MIN; 8]),
257    ///     FileTime::NT_TIME_EPOCH
258    /// );
259    /// assert_eq!(
260    ///     FileTime::from_le_bytes([0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01]),
261    ///     FileTime::UNIX_EPOCH
262    /// );
263    /// assert_eq!(
264    ///     FileTime::from_le_bytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]),
265    ///     FileTime::SIGNED_MAX
266    /// );
267    /// assert_eq!(FileTime::from_le_bytes([u8::MAX; 8]), FileTime::MAX);
268    /// ```
269    #[must_use]
270    #[inline]
271    pub const fn from_le_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
272        Self::new(u64::from_le_bytes(bytes))
273    }
274
275    /// Creates a native endian `FileTime` value from its memory representation
276    /// as a byte array in native endianness.
277    ///
278    /// <div class="warning">
279    ///
280    /// As the target platform's native endianness is used, portable code likely
281    /// wants to use [`FileTime::from_be_bytes`] or [`FileTime::from_le_bytes`],
282    /// as appropriate instead.
283    ///
284    /// </div>
285    ///
286    /// # Examples
287    ///
288    /// ```
289    /// # use nt_time::FileTime;
290    /// #
291    /// assert_eq!(
292    ///     FileTime::from_ne_bytes([u8::MIN; 8]),
293    ///     FileTime::NT_TIME_EPOCH
294    /// );
295    /// assert_eq!(
296    ///     FileTime::from_ne_bytes(if cfg!(target_endian = "big") {
297    ///         [0x01, 0x9d, 0xb1, 0xde, 0xd5, 0x3e, 0x80, 0x00]
298    ///     } else {
299    ///         [0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01]
300    ///     }),
301    ///     FileTime::UNIX_EPOCH
302    /// );
303    /// assert_eq!(
304    ///     FileTime::from_ne_bytes(if cfg!(target_endian = "big") {
305    ///         [0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
306    ///     } else {
307    ///         [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]
308    ///     }),
309    ///     FileTime::SIGNED_MAX
310    /// );
311    /// assert_eq!(FileTime::from_ne_bytes([u8::MAX; 8]), FileTime::MAX);
312    /// ```
313    #[must_use]
314    #[inline]
315    pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
316        Self::new(u64::from_ne_bytes(bytes))
317    }
318
319    #[allow(clippy::cast_possible_truncation)]
320    /// Returns the high-order and low-order parts of this `FileTime`.
321    ///
322    /// The first return value represents the high-order part of this
323    /// `FileTime`, and the second return value represents the low-order part of
324    /// this `FileTime`.
325    ///
326    /// The first return value corresponds to the `dwHighDateTime` member of the
327    /// [`FILETIME`] structure of the [Win32 API], and the second return value
328    /// corresponds to the `dwLowDateTime` member of the `FILETIME` structure.
329    /// The data type of these members is [`DWORD`], a 32-bit unsigned integer
330    /// type the same as [`u32`].
331    ///
332    /// # Examples
333    ///
334    /// ```
335    /// # use nt_time::FileTime;
336    /// #
337    /// assert_eq!(FileTime::NT_TIME_EPOCH.to_high_low(), (u32::MIN, u32::MIN));
338    /// assert_eq!(
339    ///     FileTime::UNIX_EPOCH.to_high_low(),
340    ///     (0x019d_b1de, 0xd53e_8000)
341    /// );
342    /// assert_eq!(
343    ///     FileTime::SIGNED_MAX.to_high_low(),
344    ///     (i32::MAX as u32, u32::MAX)
345    /// );
346    /// assert_eq!(FileTime::MAX.to_high_low(), (u32::MAX, u32::MAX));
347    /// ```
348    ///
349    /// [`FILETIME`]: https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
350    /// [Win32 API]: https://learn.microsoft.com/en-us/windows/win32/
351    /// [`DWORD`]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/262627d8-3418-4627-9218-4ffe110850b2
352    #[must_use]
353    #[inline]
354    pub const fn to_high_low(self) -> (u32, u32) {
355        let raw = self.to_raw();
356        ((raw >> u32::BITS) as u32, raw as u32)
357    }
358
359    /// Creates a `FileTime` from [`u32`] values representing the high-order and
360    /// low-order parts of the file time.
361    ///
362    /// `high` corresponds to the `dwHighDateTime` member of the [`FILETIME`]
363    /// structure of the [Win32 API], and `low` corresponds to the
364    /// `dwLowDateTime` member of the `FILETIME` structure. The data type of
365    /// these members is [`DWORD`], a 32-bit unsigned integer type the same as
366    /// [`u32`].
367    ///
368    /// # Examples
369    ///
370    /// ```
371    /// # use nt_time::FileTime;
372    /// #
373    /// assert_eq!(
374    ///     FileTime::from_high_low(u32::MIN, u32::MIN),
375    ///     FileTime::NT_TIME_EPOCH
376    /// );
377    /// assert_eq!(
378    ///     FileTime::from_high_low(0x019d_b1de, 0xd53e_8000),
379    ///     FileTime::UNIX_EPOCH
380    /// );
381    /// assert_eq!(
382    ///     FileTime::from_high_low(i32::MAX as u32, u32::MAX),
383    ///     FileTime::SIGNED_MAX
384    /// );
385    /// assert_eq!(FileTime::from_high_low(u32::MAX, u32::MAX), FileTime::MAX);
386    /// ```
387    ///
388    /// [`FILETIME`]: https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
389    /// [Win32 API]: https://learn.microsoft.com/en-us/windows/win32/
390    /// [`DWORD`]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/262627d8-3418-4627-9218-4ffe110850b2
391    #[must_use]
392    #[inline]
393    pub const fn from_high_low(high: u32, low: u32) -> Self {
394        let raw = ((high as u64) << u32::BITS) | (low as u64);
395        Self::new(raw)
396    }
397}
398
399impl Default for FileTime {
400    /// Returns the default value of "1601-01-01 00:00:00 UTC".
401    ///
402    /// Equivalent to [`FileTime::NT_TIME_EPOCH`] except that it is not callable
403    /// in const contexts.
404    ///
405    /// # Examples
406    ///
407    /// ```
408    /// # use nt_time::FileTime;
409    /// #
410    /// assert_eq!(FileTime::default(), FileTime::NT_TIME_EPOCH);
411    /// ```
412    #[inline]
413    fn default() -> Self {
414        Self::NT_TIME_EPOCH
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421
422    #[test]
423    fn size_of() {
424        assert_eq!(mem::size_of::<FileTime>(), 8);
425        assert_eq!(mem::size_of::<FileTime>(), mem::size_of::<u64>());
426    }
427
428    #[test]
429    fn clone() {
430        assert_eq!(FileTime::NT_TIME_EPOCH.clone(), FileTime::NT_TIME_EPOCH);
431    }
432
433    #[test]
434    fn copy() {
435        let a = FileTime::NT_TIME_EPOCH;
436        let b = a;
437        assert_eq!(a, b);
438    }
439
440    #[cfg(feature = "std")]
441    #[test]
442    fn hash() {
443        use std::{
444            collections::hash_map::DefaultHasher,
445            hash::{Hash, Hasher},
446        };
447
448        assert_ne!(
449            {
450                let mut hasher = DefaultHasher::new();
451                FileTime::NT_TIME_EPOCH.hash(&mut hasher);
452                hasher.finish()
453            },
454            {
455                let mut hasher = DefaultHasher::new();
456                FileTime::MAX.hash(&mut hasher);
457                hasher.finish()
458            }
459        );
460    }
461
462    #[cfg(feature = "std")]
463    #[test]
464    fn now() {
465        let now = FileTime::now();
466        // After "2023-01-01 00:00:00 UTC".
467        assert!(now >= FileTime::new(133_170_048_000_000_000));
468    }
469
470    #[test]
471    fn new() {
472        assert_eq!(FileTime::new(u64::MIN), FileTime::NT_TIME_EPOCH);
473        assert_eq!(FileTime::new(116_444_736_000_000_000), FileTime::UNIX_EPOCH);
474        assert_eq!(FileTime::new(i64::MAX as u64), FileTime::SIGNED_MAX);
475        assert_eq!(FileTime::new(u64::MAX), FileTime::MAX);
476    }
477
478    #[test]
479    const fn new_is_const_fn() {
480        const _: FileTime = FileTime::new(u64::MIN);
481    }
482
483    #[test]
484    fn to_raw() {
485        assert_eq!(FileTime::NT_TIME_EPOCH.to_raw(), u64::MIN);
486        assert_eq!(FileTime::UNIX_EPOCH.to_raw(), 116_444_736_000_000_000);
487        assert_eq!(FileTime::SIGNED_MAX.to_raw(), i64::MAX as u64);
488        assert_eq!(FileTime::MAX.to_raw(), u64::MAX);
489    }
490
491    #[test]
492    const fn to_raw_is_const_fn() {
493        const _: u64 = FileTime::NT_TIME_EPOCH.to_raw();
494    }
495
496    #[test]
497    fn to_be_bytes() {
498        assert_eq!(FileTime::NT_TIME_EPOCH.to_be_bytes(), [u8::MIN; 8]);
499        assert_eq!(
500            FileTime::UNIX_EPOCH.to_be_bytes(),
501            [0x01, 0x9d, 0xb1, 0xde, 0xd5, 0x3e, 0x80, 0x00]
502        );
503        assert_eq!(
504            FileTime::SIGNED_MAX.to_be_bytes(),
505            [0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
506        );
507        assert_eq!(FileTime::MAX.to_be_bytes(), [u8::MAX; 8]);
508    }
509
510    #[cfg(feature = "std")]
511    #[test_strategy::proptest]
512    fn to_be_bytes_roundtrip(ft: FileTime) {
513        use proptest::prop_assert_eq;
514
515        prop_assert_eq!(ft.to_be_bytes(), ft.to_raw().to_be_bytes());
516    }
517
518    #[test]
519    const fn to_be_bytes_is_const_fn() {
520        const _: [u8; 8] = FileTime::NT_TIME_EPOCH.to_be_bytes();
521    }
522
523    #[test]
524    fn to_le_bytes() {
525        assert_eq!(FileTime::NT_TIME_EPOCH.to_le_bytes(), [u8::MIN; 8]);
526        assert_eq!(
527            FileTime::UNIX_EPOCH.to_le_bytes(),
528            [0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01]
529        );
530        assert_eq!(
531            FileTime::SIGNED_MAX.to_le_bytes(),
532            [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]
533        );
534        assert_eq!(FileTime::MAX.to_le_bytes(), [u8::MAX; 8]);
535    }
536
537    #[cfg(feature = "std")]
538    #[test_strategy::proptest]
539    fn to_le_bytes_roundtrip(ft: FileTime) {
540        use proptest::prop_assert_eq;
541
542        prop_assert_eq!(ft.to_le_bytes(), ft.to_raw().to_le_bytes());
543    }
544
545    #[test]
546    const fn to_le_bytes_is_const_fn() {
547        const _: [u8; 8] = FileTime::NT_TIME_EPOCH.to_le_bytes();
548    }
549
550    #[test]
551    fn to_ne_bytes() {
552        assert_eq!(FileTime::NT_TIME_EPOCH.to_ne_bytes(), [u8::MIN; 8]);
553        assert_eq!(
554            FileTime::UNIX_EPOCH.to_ne_bytes(),
555            if cfg!(target_endian = "big") {
556                [0x01, 0x9d, 0xb1, 0xde, 0xd5, 0x3e, 0x80, 0x00]
557            } else {
558                [0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01]
559            }
560        );
561        assert_eq!(
562            FileTime::SIGNED_MAX.to_ne_bytes(),
563            if cfg!(target_endian = "big") {
564                [0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
565            } else {
566                [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]
567            }
568        );
569        assert_eq!(FileTime::MAX.to_ne_bytes(), [u8::MAX; 8]);
570    }
571
572    #[cfg(feature = "std")]
573    #[test_strategy::proptest]
574    fn to_ne_bytes_roundtrip(ft: FileTime) {
575        use proptest::prop_assert_eq;
576
577        prop_assert_eq!(ft.to_ne_bytes(), ft.to_raw().to_ne_bytes());
578    }
579
580    #[test]
581    const fn to_ne_bytes_is_const_fn() {
582        const _: [u8; 8] = FileTime::NT_TIME_EPOCH.to_ne_bytes();
583    }
584
585    #[test]
586    fn from_be_bytes() {
587        assert_eq!(
588            FileTime::from_be_bytes([u8::MIN; 8]),
589            FileTime::NT_TIME_EPOCH
590        );
591        assert_eq!(
592            FileTime::from_be_bytes([0x01, 0x9d, 0xb1, 0xde, 0xd5, 0x3e, 0x80, 0x00]),
593            FileTime::UNIX_EPOCH
594        );
595        assert_eq!(
596            FileTime::from_be_bytes([0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
597            FileTime::SIGNED_MAX
598        );
599        assert_eq!(FileTime::from_be_bytes([u8::MAX; 8]), FileTime::MAX);
600    }
601
602    #[cfg(feature = "std")]
603    #[test_strategy::proptest]
604    fn from_be_bytes_roundtrip(bytes: [u8; mem::size_of::<FileTime>()]) {
605        use proptest::prop_assert_eq;
606
607        prop_assert_eq!(
608            FileTime::from_be_bytes(bytes),
609            FileTime::new(u64::from_be_bytes(bytes))
610        );
611    }
612
613    #[test]
614    const fn from_be_bytes_is_const_fn() {
615        const _: FileTime = FileTime::from_be_bytes([u8::MIN; 8]);
616    }
617
618    #[test]
619    fn from_le_bytes() {
620        assert_eq!(
621            FileTime::from_le_bytes([u8::MIN; 8]),
622            FileTime::NT_TIME_EPOCH
623        );
624        assert_eq!(
625            FileTime::from_le_bytes([0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01]),
626            FileTime::UNIX_EPOCH
627        );
628        assert_eq!(
629            FileTime::from_le_bytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]),
630            FileTime::SIGNED_MAX
631        );
632        assert_eq!(FileTime::from_le_bytes([u8::MAX; 8]), FileTime::MAX);
633    }
634
635    #[cfg(feature = "std")]
636    #[test_strategy::proptest]
637    fn from_le_bytes_roundtrip(bytes: [u8; mem::size_of::<FileTime>()]) {
638        use proptest::prop_assert_eq;
639
640        prop_assert_eq!(
641            FileTime::from_le_bytes(bytes),
642            FileTime::new(u64::from_le_bytes(bytes))
643        );
644    }
645
646    #[test]
647    const fn from_le_bytes_is_const_fn() {
648        const _: FileTime = FileTime::from_le_bytes([u8::MIN; 8]);
649    }
650
651    #[test]
652    fn from_ne_bytes() {
653        assert_eq!(
654            FileTime::from_ne_bytes([u8::MIN; 8]),
655            FileTime::NT_TIME_EPOCH
656        );
657        assert_eq!(
658            FileTime::from_ne_bytes(if cfg!(target_endian = "big") {
659                [0x01, 0x9d, 0xb1, 0xde, 0xd5, 0x3e, 0x80, 0x00]
660            } else {
661                [0x00, 0x80, 0x3e, 0xd5, 0xde, 0xb1, 0x9d, 0x01]
662            }),
663            FileTime::UNIX_EPOCH
664        );
665        assert_eq!(
666            FileTime::from_ne_bytes(if cfg!(target_endian = "big") {
667                [0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
668            } else {
669                [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]
670            }),
671            FileTime::SIGNED_MAX
672        );
673        assert_eq!(FileTime::from_ne_bytes([u8::MAX; 8]), FileTime::MAX);
674    }
675
676    #[cfg(feature = "std")]
677    #[test_strategy::proptest]
678    fn from_ne_bytes_roundtrip(bytes: [u8; mem::size_of::<FileTime>()]) {
679        use proptest::prop_assert_eq;
680
681        prop_assert_eq!(
682            FileTime::from_ne_bytes(bytes),
683            FileTime::new(u64::from_ne_bytes(bytes))
684        );
685    }
686
687    #[test]
688    const fn from_ne_bytes_is_const_fn() {
689        const _: FileTime = FileTime::from_ne_bytes([u8::MIN; 8]);
690    }
691
692    #[test]
693    fn to_high_low() {
694        assert_eq!(FileTime::NT_TIME_EPOCH.to_high_low(), (u32::MIN, u32::MIN));
695        assert_eq!(
696            FileTime::UNIX_EPOCH.to_high_low(),
697            (0x019d_b1de, 0xd53e_8000)
698        );
699        assert_eq!(
700            FileTime::SIGNED_MAX.to_high_low(),
701            (i32::MAX as u32, u32::MAX)
702        );
703        assert_eq!(FileTime::MAX.to_high_low(), (u32::MAX, u32::MAX));
704    }
705
706    #[cfg(feature = "std")]
707    #[test_strategy::proptest]
708    fn to_high_low_roundtrip(ft: FileTime) {
709        use proptest::prop_assert_eq;
710
711        let raw = ft.to_raw();
712        prop_assert_eq!(ft.to_high_low(), ((raw >> u32::BITS) as u32, raw as u32));
713    }
714
715    #[test]
716    const fn to_high_low_is_const_fn() {
717        const _: (u32, u32) = FileTime::NT_TIME_EPOCH.to_high_low();
718    }
719
720    #[test]
721    fn from_high_low() {
722        assert_eq!(
723            FileTime::from_high_low(u32::MIN, u32::MIN),
724            FileTime::NT_TIME_EPOCH
725        );
726        assert_eq!(
727            FileTime::from_high_low(0x019d_b1de, 0xd53e_8000),
728            FileTime::UNIX_EPOCH
729        );
730        assert_eq!(
731            FileTime::from_high_low(i32::MAX as u32, u32::MAX),
732            FileTime::SIGNED_MAX
733        );
734        assert_eq!(FileTime::from_high_low(u32::MAX, u32::MAX), FileTime::MAX);
735    }
736
737    #[cfg(feature = "std")]
738    #[test_strategy::proptest]
739    fn from_high_low_roundtrip(high: u32, low: u32) {
740        use proptest::prop_assert_eq;
741
742        let raw = (u64::from(high) << u32::BITS) | u64::from(low);
743        prop_assert_eq!(FileTime::from_high_low(high, low), FileTime::new(raw));
744    }
745
746    #[test]
747    const fn from_high_low_is_const_fn() {
748        const _: FileTime = FileTime::from_high_low(u32::MIN, u32::MIN);
749    }
750
751    #[test]
752    fn default() {
753        assert_eq!(FileTime::default(), FileTime::NT_TIME_EPOCH);
754    }
755}