1use core::num::TryFromIntError;
8
9use time::{OffsetDateTime, error::ComponentRange};
10
11use super::FileTime;
12use crate::error::{FileTimeRangeError, FileTimeRangeErrorKind};
13
14impl From<FileTime> for u64 {
15 #[inline]
31 fn from(ft: FileTime) -> Self {
32 ft.to_raw()
33 }
34}
35
36impl TryFrom<FileTime> for i64 {
37 type Error = TryFromIntError;
38
39 #[inline]
68 fn try_from(ft: FileTime) -> Result<Self, Self::Error> {
69 ft.to_raw().try_into()
70 }
71}
72
73#[cfg(feature = "std")]
74impl From<FileTime> for std::time::SystemTime {
75 #[inline]
99 fn from(ft: FileTime) -> Self {
100 use std::time::Duration;
101
102 use super::FILE_TIMES_PER_SEC;
103
104 let duration = Duration::new(
105 ft.to_raw() / FILE_TIMES_PER_SEC,
106 u32::try_from((ft.to_raw() % FILE_TIMES_PER_SEC) * 100)
107 .expect("the number of nanoseconds should be in the range of `u32`"),
108 );
109 (Self::UNIX_EPOCH - (FileTime::UNIX_EPOCH - FileTime::NT_TIME_EPOCH)) + duration
110 }
111}
112
113impl TryFrom<FileTime> for OffsetDateTime {
114 type Error = ComponentRange;
115
116 #[inline]
177 fn try_from(ft: FileTime) -> Result<Self, Self::Error> {
178 Self::from_unix_timestamp_nanos(ft.to_unix_time_nanos())
179 }
180}
181
182#[cfg(feature = "chrono")]
183impl From<FileTime> for chrono::DateTime<chrono::Utc> {
184 #[inline]
204 fn from(ft: FileTime) -> Self {
205 let ut = ft.to_unix_time();
206 Self::from_timestamp(ut.0, ut.1)
207 .expect("Unix time in nanoseconds should be in the range of `DateTime<Utc>`")
208 }
209}
210
211impl From<u64> for FileTime {
212 #[inline]
231 fn from(ft: u64) -> Self {
232 Self::new(ft)
233 }
234}
235
236impl TryFrom<i64> for FileTime {
237 type Error = FileTimeRangeError;
238
239 #[inline]
268 fn try_from(ft: i64) -> Result<Self, Self::Error> {
269 ft.try_into()
270 .map_err(|_| FileTimeRangeErrorKind::Negative.into())
271 .map(Self::new)
272 }
273}
274
275#[cfg(feature = "std")]
276impl TryFrom<std::time::SystemTime> for FileTime {
277 type Error = FileTimeRangeError;
278
279 #[inline]
316 fn try_from(st: std::time::SystemTime) -> Result<Self, Self::Error> {
317 use std::time::SystemTime;
318
319 let elapsed = st
320 .duration_since(SystemTime::UNIX_EPOCH - (Self::UNIX_EPOCH - Self::NT_TIME_EPOCH))
321 .map(|d| d.as_nanos())
322 .map_err(|_| FileTimeRangeErrorKind::Negative)?;
323 let ft = u64::try_from(elapsed / 100).map_err(|_| FileTimeRangeErrorKind::Overflow)?;
324 Ok(Self::new(ft))
325 }
326}
327
328impl TryFrom<OffsetDateTime> for FileTime {
329 type Error = FileTimeRangeError;
330
331 #[inline]
381 fn try_from(dt: OffsetDateTime) -> Result<Self, Self::Error> {
382 Self::from_unix_time_nanos(dt.unix_timestamp_nanos())
383 }
384}
385
386#[cfg(feature = "chrono")]
387impl TryFrom<chrono::DateTime<chrono::Utc>> for FileTime {
388 type Error = FileTimeRangeError;
389
390 #[inline]
433 fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
434 Self::from_unix_time(dt.timestamp(), dt.timestamp_subsec_nanos())
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441
442 #[test]
443 fn from_file_time_to_u64() {
444 assert_eq!(u64::from(FileTime::NT_TIME_EPOCH), u64::MIN);
445 assert_eq!(u64::from(FileTime::UNIX_EPOCH), 116_444_736_000_000_000);
446 assert_eq!(u64::from(FileTime::SIGNED_MAX), i64::MAX as u64);
447 assert_eq!(u64::from(FileTime::MAX), u64::MAX);
448 }
449
450 #[cfg(feature = "std")]
451 #[test_strategy::proptest]
452 fn from_file_time_to_u64_roundtrip(ft: FileTime) {
453 use proptest::prop_assert_eq;
454
455 prop_assert_eq!(u64::from(ft), ft.to_raw());
456 }
457
458 #[test]
459 fn try_from_file_time_to_i64() {
460 assert_eq!(
461 i64::try_from(FileTime::NT_TIME_EPOCH).unwrap(),
462 i64::default()
463 );
464 assert_eq!(
465 i64::try_from(FileTime::UNIX_EPOCH).unwrap(),
466 116_444_736_000_000_000
467 );
468 assert_eq!(i64::try_from(FileTime::SIGNED_MAX).unwrap(), i64::MAX);
469 }
470
471 #[cfg(feature = "std")]
472 #[test_strategy::proptest]
473 fn try_from_file_time_to_i64_roundtrip(ft: FileTime) {
474 use proptest::prop_assert;
475
476 if ft <= FileTime::SIGNED_MAX {
477 prop_assert!(i64::try_from(ft).is_ok());
478 } else {
479 prop_assert!(i64::try_from(ft).is_err());
480 }
481 }
482
483 #[test]
484 fn try_from_file_time_to_i64_with_too_big_file_time() {
485 assert!(i64::try_from(FileTime::new(u64::try_from(i64::MAX).unwrap() + 1)).is_err());
486 assert!(i64::try_from(FileTime::MAX).is_err());
487 }
488
489 #[cfg(feature = "std")]
490 #[test]
491 fn from_file_time_to_system_time() {
492 use std::time::{Duration, SystemTime};
493
494 assert_eq!(
495 SystemTime::from(FileTime::NT_TIME_EPOCH),
496 SystemTime::UNIX_EPOCH - (FileTime::UNIX_EPOCH - FileTime::NT_TIME_EPOCH)
497 );
498 assert_eq!(
499 SystemTime::from(FileTime::UNIX_EPOCH),
500 SystemTime::UNIX_EPOCH
501 );
502 assert_eq!(
503 SystemTime::from(FileTime::new(2_650_467_743_999_999_999)),
504 SystemTime::UNIX_EPOCH + Duration::new(253_402_300_799, 999_999_900)
505 );
506 assert_eq!(
507 SystemTime::from(FileTime::new(2_650_467_744_000_000_000)),
508 SystemTime::UNIX_EPOCH + Duration::from_secs(253_402_300_800)
509 );
510 assert_eq!(
512 SystemTime::from(FileTime::new(9_223_372_036_854_775_807)),
513 SystemTime::UNIX_EPOCH + Duration::new(910_692_730_085, 477_580_700)
514 );
515 if !cfg!(windows) {
516 assert_eq!(
517 SystemTime::from(FileTime::MAX),
518 SystemTime::UNIX_EPOCH + Duration::new(1_833_029_933_770, 955_161_500)
519 );
520 }
521 }
522
523 #[test]
524 fn try_from_file_time_to_offset_date_time() {
525 use time::macros::datetime;
526
527 assert_eq!(
528 OffsetDateTime::try_from(FileTime::NT_TIME_EPOCH).unwrap(),
529 datetime!(1601-01-01 00:00 UTC)
530 );
531 assert_eq!(
532 OffsetDateTime::try_from(FileTime::UNIX_EPOCH).unwrap(),
533 OffsetDateTime::UNIX_EPOCH
534 );
535 assert_eq!(
536 OffsetDateTime::try_from(FileTime::new(2_650_467_743_999_999_999)).unwrap(),
537 datetime!(9999-12-31 23:59:59.999_999_900 UTC)
538 );
539 }
540
541 #[cfg(not(feature = "large-dates"))]
542 #[test]
543 fn try_from_file_time_to_offset_date_time_with_invalid_file_time() {
544 assert!(OffsetDateTime::try_from(FileTime::new(2_650_467_744_000_000_000)).is_err());
545 }
546
547 #[cfg(feature = "large-dates")]
548 #[test]
549 fn try_from_file_time_to_offset_date_time_with_large_dates() {
550 use time::macros::datetime;
551
552 assert_eq!(
553 OffsetDateTime::try_from(FileTime::new(2_650_467_744_000_000_000)).unwrap(),
554 datetime!(+10000-01-01 00:00 UTC)
555 );
556 assert_eq!(
557 OffsetDateTime::try_from(FileTime::SIGNED_MAX).unwrap(),
558 datetime!(+30828-09-14 02:48:05.477_580_700 UTC)
559 );
560 assert_eq!(
561 OffsetDateTime::try_from(FileTime::MAX).unwrap(),
562 datetime!(+60056-05-28 05:36:10.955_161_500 UTC)
563 );
564 }
565
566 #[cfg(feature = "chrono")]
567 #[test]
568 fn from_file_time_to_chrono_date_time() {
569 use chrono::{DateTime, Utc};
570
571 assert_eq!(
572 DateTime::<Utc>::from(FileTime::NT_TIME_EPOCH),
573 "1601-01-01 00:00:00 UTC".parse::<DateTime<Utc>>().unwrap()
574 );
575 assert_eq!(
576 DateTime::<Utc>::from(FileTime::UNIX_EPOCH),
577 DateTime::<Utc>::UNIX_EPOCH
578 );
579 assert_eq!(
580 DateTime::<Utc>::from(FileTime::new(2_650_467_743_999_999_999)),
581 "9999-12-31 23:59:59.999999900 UTC"
582 .parse::<DateTime<Utc>>()
583 .unwrap()
584 );
585 assert_eq!(
586 DateTime::<Utc>::from(FileTime::new(2_650_467_744_000_000_000)),
587 "+10000-01-01 00:00:00 UTC"
588 .parse::<DateTime<Utc>>()
589 .unwrap()
590 );
591 assert_eq!(
592 DateTime::<Utc>::from(FileTime::SIGNED_MAX),
593 "+30828-09-14 02:48:05.477580700 UTC"
594 .parse::<DateTime<Utc>>()
595 .unwrap()
596 );
597 assert_eq!(
598 DateTime::<Utc>::from(FileTime::MAX),
599 "+60056-05-28 05:36:10.955161500 UTC"
600 .parse::<DateTime<Utc>>()
601 .unwrap()
602 );
603 }
604
605 #[test]
606 fn from_u64_to_file_time() {
607 assert_eq!(FileTime::from(u64::MIN), FileTime::NT_TIME_EPOCH);
608 assert_eq!(
609 FileTime::from(116_444_736_000_000_000),
610 FileTime::UNIX_EPOCH
611 );
612 assert_eq!(FileTime::from(i64::MAX as u64), FileTime::SIGNED_MAX);
613 assert_eq!(FileTime::from(u64::MAX), FileTime::MAX);
614 }
615
616 #[cfg(feature = "std")]
617 #[test_strategy::proptest]
618 fn from_u64_to_file_time_roundtrip(ft: u64) {
619 use proptest::prop_assert_eq;
620
621 prop_assert_eq!(FileTime::from(ft), FileTime::new(ft));
622 }
623
624 #[test]
625 fn try_from_i64_to_file_time_before_nt_time_epoch() {
626 assert_eq!(
627 FileTime::try_from(i64::MIN).unwrap_err(),
628 FileTimeRangeErrorKind::Negative.into()
629 );
630 assert_eq!(
631 FileTime::try_from(i64::default() - 1).unwrap_err(),
632 FileTimeRangeErrorKind::Negative.into()
633 );
634 }
635
636 #[cfg(feature = "std")]
637 #[test_strategy::proptest]
638 fn try_from_i64_to_file_time_before_nt_time_epoch_roundtrip(
639 #[strategy(..i64::default())] ft: i64,
640 ) {
641 use proptest::prop_assert_eq;
642
643 prop_assert_eq!(
644 FileTime::try_from(ft).unwrap_err(),
645 FileTimeRangeErrorKind::Negative.into()
646 );
647 }
648
649 #[test]
650 fn try_from_i64_to_file_time() {
651 assert_eq!(
652 FileTime::try_from(i64::default()).unwrap(),
653 FileTime::NT_TIME_EPOCH
654 );
655 assert_eq!(
656 FileTime::try_from(116_444_736_000_000_000_i64).unwrap(),
657 FileTime::UNIX_EPOCH
658 );
659 assert_eq!(FileTime::try_from(i64::MAX).unwrap(), FileTime::SIGNED_MAX);
660 }
661
662 #[cfg(feature = "std")]
663 #[test_strategy::proptest]
664 fn try_from_i64_to_file_time_roundtrip(#[strategy(i64::default()..)] ft: i64) {
665 use proptest::prop_assert;
666
667 prop_assert!(FileTime::try_from(ft).is_ok());
668 }
669
670 #[cfg(feature = "std")]
671 #[test]
672 fn try_from_system_time_to_file_time_before_nt_time_epoch() {
673 use std::time::{Duration, SystemTime};
674
675 assert_eq!(
676 FileTime::try_from(if cfg!(windows) {
677 SystemTime::UNIX_EPOCH - Duration::from_nanos(11_644_473_600_000_000_100)
678 } else {
679 SystemTime::UNIX_EPOCH - Duration::from_nanos(11_644_473_600_000_000_001)
680 })
681 .unwrap_err(),
682 FileTimeRangeErrorKind::Negative.into()
683 );
684 }
685
686 #[cfg(feature = "std")]
687 #[test]
688 fn try_from_system_time_to_file_time() {
689 use std::time::{Duration, SystemTime};
690
691 assert_eq!(
692 FileTime::try_from(
693 SystemTime::UNIX_EPOCH - (FileTime::UNIX_EPOCH - FileTime::NT_TIME_EPOCH)
694 )
695 .unwrap(),
696 FileTime::NT_TIME_EPOCH
697 );
698 assert_eq!(
699 FileTime::try_from(SystemTime::UNIX_EPOCH).unwrap(),
700 FileTime::UNIX_EPOCH
701 );
702 assert_eq!(
703 FileTime::try_from(
704 SystemTime::UNIX_EPOCH + Duration::new(253_402_300_799, 999_999_900)
705 )
706 .unwrap(),
707 FileTime::new(2_650_467_743_999_999_999)
708 );
709 assert_eq!(
710 FileTime::try_from(SystemTime::UNIX_EPOCH + Duration::from_secs(253_402_300_800))
711 .unwrap(),
712 FileTime::new(2_650_467_744_000_000_000)
713 );
714 assert_eq!(
716 FileTime::try_from(
717 SystemTime::UNIX_EPOCH + Duration::new(910_692_730_085, 477_580_700)
718 )
719 .unwrap(),
720 FileTime::new(9_223_372_036_854_775_807)
721 );
722 if !cfg!(windows) {
723 assert_eq!(
724 FileTime::try_from(
725 SystemTime::UNIX_EPOCH + Duration::new(1_833_029_933_770, 955_161_500)
726 )
727 .unwrap(),
728 FileTime::MAX
729 );
730 }
731 }
732
733 #[cfg(feature = "std")]
734 #[test]
735 fn try_from_system_time_to_file_time_with_too_big_system_time() {
736 use std::time::{Duration, SystemTime};
737
738 if cfg!(windows) {
739 assert!(
740 SystemTime::UNIX_EPOCH
741 .checked_add(Duration::new(910_692_730_085, 477_580_800))
742 .is_none()
743 );
744 } else {
745 assert_eq!(
746 FileTime::try_from(
747 SystemTime::UNIX_EPOCH + Duration::new(1_833_029_933_770, 955_161_600)
748 )
749 .unwrap_err(),
750 FileTimeRangeErrorKind::Overflow.into()
751 );
752 }
753 }
754
755 #[test]
756 fn try_from_offset_date_time_to_file_time_before_nt_time_epoch() {
757 use time::{Duration, macros::datetime};
758
759 assert_eq!(
760 FileTime::try_from(datetime!(1601-01-01 00:00 UTC) - Duration::nanoseconds(100))
761 .unwrap_err(),
762 FileTimeRangeErrorKind::Negative.into()
763 );
764 }
765
766 #[test]
767 fn try_from_offset_date_time_to_file_time() {
768 use time::macros::datetime;
769
770 assert_eq!(
771 FileTime::try_from(datetime!(1601-01-01 00:00 UTC)).unwrap(),
772 FileTime::NT_TIME_EPOCH
773 );
774 assert_eq!(
775 FileTime::try_from(OffsetDateTime::UNIX_EPOCH).unwrap(),
776 FileTime::UNIX_EPOCH
777 );
778 assert_eq!(
779 FileTime::try_from(datetime!(9999-12-31 23:59:59.999_999_999 UTC)).unwrap(),
780 FileTime::new(2_650_467_743_999_999_999)
781 );
782 }
783
784 #[cfg(feature = "large-dates")]
785 #[test]
786 fn try_from_offset_date_time_to_file_time_with_large_dates() {
787 use time::macros::datetime;
788
789 assert_eq!(
790 FileTime::try_from(datetime!(+10000-01-01 00:00 UTC)).unwrap(),
791 FileTime::new(2_650_467_744_000_000_000)
792 );
793 assert_eq!(
794 FileTime::try_from(datetime!(+30828-09-14 02:48:05.477_580_700 UTC)).unwrap(),
795 FileTime::SIGNED_MAX
796 );
797 assert_eq!(
798 FileTime::try_from(datetime!(+60056-05-28 05:36:10.955_161_500 UTC)).unwrap(),
799 FileTime::MAX
800 );
801 }
802
803 #[cfg(feature = "large-dates")]
804 #[test]
805 fn try_from_offset_date_time_to_file_time_with_too_big_date_time() {
806 use time::{Duration, macros::datetime};
807
808 assert_eq!(
809 FileTime::try_from(
810 datetime!(+60056-05-28 05:36:10.955_161_500 UTC) + Duration::nanoseconds(100)
811 )
812 .unwrap_err(),
813 FileTimeRangeErrorKind::Overflow.into()
814 );
815 }
816
817 #[cfg(feature = "chrono")]
818 #[test]
819 fn try_from_chrono_date_time_to_file_time_before_nt_time_epoch() {
820 use chrono::{DateTime, TimeDelta, Utc};
821
822 assert_eq!(
823 FileTime::try_from(
824 "1601-01-01 00:00:00 UTC".parse::<DateTime<Utc>>().unwrap()
825 - TimeDelta::nanoseconds(100)
826 )
827 .unwrap_err(),
828 FileTimeRangeErrorKind::Negative.into()
829 );
830 }
831
832 #[cfg(feature = "chrono")]
833 #[test]
834 fn try_from_chrono_date_time_to_file_time() {
835 use chrono::{DateTime, Utc};
836
837 assert_eq!(
838 FileTime::try_from("1601-01-01 00:00:00 UTC".parse::<DateTime<Utc>>().unwrap())
839 .unwrap(),
840 FileTime::NT_TIME_EPOCH
841 );
842 assert_eq!(
843 FileTime::try_from(DateTime::<Utc>::UNIX_EPOCH).unwrap(),
844 FileTime::UNIX_EPOCH
845 );
846 assert_eq!(
847 FileTime::try_from(
848 "9999-12-31 23:59:59.999999999 UTC"
849 .parse::<DateTime<Utc>>()
850 .unwrap()
851 )
852 .unwrap(),
853 FileTime::new(2_650_467_743_999_999_999)
854 );
855 assert_eq!(
856 FileTime::try_from(
857 "+10000-01-01 00:00:00 UTC"
858 .parse::<DateTime<Utc>>()
859 .unwrap()
860 )
861 .unwrap(),
862 FileTime::new(2_650_467_744_000_000_000)
863 );
864 assert_eq!(
865 FileTime::try_from(
866 "+30828-09-14 02:48:05.477580700 UTC"
867 .parse::<DateTime<Utc>>()
868 .unwrap()
869 )
870 .unwrap(),
871 FileTime::SIGNED_MAX
872 );
873 assert_eq!(
874 FileTime::try_from(
875 "+60056-05-28 05:36:10.955161500 UTC"
876 .parse::<DateTime<Utc>>()
877 .unwrap()
878 )
879 .unwrap(),
880 FileTime::MAX
881 );
882 }
883
884 #[cfg(feature = "chrono")]
885 #[test]
886 fn try_from_chrono_date_time_to_file_time_with_too_big_date_time() {
887 use chrono::{DateTime, TimeDelta, Utc};
888
889 assert_eq!(
890 FileTime::try_from(
891 "+60056-05-28 05:36:10.955161500 UTC"
892 .parse::<DateTime<Utc>>()
893 .unwrap()
894 + TimeDelta::nanoseconds(100)
895 )
896 .unwrap_err(),
897 FileTimeRangeErrorKind::Overflow.into()
898 );
899 }
900}