t2t_core/interop/
chrono.rs1use chrono::{DateTime, TimeDelta, TimeZone, Utc};
4
5use crate::{OutOfRangeError, Timedelta, Timestamp};
6
7impl From<Timestamp> for DateTime<Utc> {
9 #[inline]
10 fn from(instant: Timestamp) -> Self {
11 Self::from_timestamp_nanos(instant.0)
12 }
13}
14
15impl<Z: TimeZone> TryFrom<DateTime<Z>> for Timestamp {
17 type Error = OutOfRangeError;
18
19 #[inline]
20 fn try_from(instant: DateTime<Z>) -> Result<Self, Self::Error> {
21 instant.timestamp_nanos_opt().map(Self).ok_or(OutOfRangeError)
22 }
23}
24
25impl From<Timedelta> for TimeDelta {
27 #[inline]
28 fn from(span: Timedelta) -> Self {
29 Self::nanoseconds(span.0)
30 }
31}
32
33impl TryFrom<TimeDelta> for Timedelta {
35 type Error = OutOfRangeError;
36
37 #[inline]
38 fn try_from(span: TimeDelta) -> Result<Self, Self::Error> {
39 span.num_nanoseconds().map(Self).ok_or(OutOfRangeError)
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use chrono::{DateTime, FixedOffset, TimeDelta, Utc};
46 use rstest::rstest;
47
48 use crate::{OutOfRangeError, Timedelta, Timestamp};
49
50 #[rstest]
51 #[case::the_earliest(Timestamp::MIN)]
52 #[case::before_the_epoch(Timestamp::from_nanos(-1))]
53 #[case::a_capture(Timestamp::from_nanos(1_789_544_735_123_456_789))]
54 #[case::the_latest(Timestamp::MAX)]
55 fn an_instant_crosses_both_ways(#[case] instant: Timestamp) {
56 let theirs = DateTime::<Utc>::from(instant);
57 assert_eq!(theirs.timestamp_nanos_opt(), Some(instant.as_nanos()), "the same instant");
58 assert_eq!(Timestamp::try_from(theirs), Ok(instant), "and back");
59 }
60
61 #[test]
62 fn an_instant_in_another_zone_is_the_same_instant() {
63 let instant = Timestamp::from_nanos(1_789_544_735_123_456_789);
64 let zone = FixedOffset::east_opt(18_000).expect("five hours east is an offset");
65 let elsewhere = DateTime::<Utc>::from(instant).with_timezone(&zone);
66 assert_eq!(Timestamp::try_from(elsewhere), Ok(instant), "read in UTC");
67 }
68
69 #[test]
70 fn an_instant_past_the_range_is_refused() {
71 assert_eq!(
72 Timestamp::try_from(DateTime::<Utc>::MAX_UTC),
73 Err(OutOfRangeError),
74 "after 2262"
75 );
76 assert_eq!(
77 Timestamp::try_from(DateTime::<Utc>::MIN_UTC),
78 Err(OutOfRangeError),
79 "before 1677"
80 );
81 }
82
83 #[rstest]
84 #[case::the_longest_backwards(Timedelta::MIN)]
85 #[case::a_budget(Timedelta::from_millis(-250))]
86 #[case::the_longest(Timedelta::MAX)]
87 fn a_span_crosses_both_ways(#[case] span: Timedelta) {
88 let theirs = TimeDelta::from(span);
89 assert_eq!(theirs.num_nanoseconds(), Some(span.as_nanos()), "the same span");
90 assert_eq!(Timedelta::try_from(theirs), Ok(span), "and back");
91 }
92
93 #[test]
94 fn a_span_past_the_range_is_refused() {
95 assert_eq!(Timedelta::try_from(TimeDelta::MAX), Err(OutOfRangeError), "forwards");
96 assert_eq!(Timedelta::try_from(TimeDelta::MIN), Err(OutOfRangeError), "and backwards");
97 }
98}