Skip to main content

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