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