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}