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}