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}