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