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