deser_core/ext/bridges/
chrono.rs1use ::chrono::{
2 DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, TimeDelta, Timelike, Utc,
3};
4
5use crate::error::Error;
6use crate::ext::known::{Bridge, impl_bridge, invalid, out_of_range};
7use crate::ext::{Date, Datetime, Duration, Offset, Time, Timestamp};
8
9fn date_to_known(value: NaiveDate) -> Result<Date, Error> {
10 match u16::try_from(value.year()) {
11 Ok(year) if year <= 9999 => Ok(Date {
12 year,
13 month: value.month() as u8,
14 day: value.day() as u8,
15 }),
16 _ => Err(out_of_range("year out of range")),
17 }
18}
19
20fn time_to_known(value: NaiveTime) -> Time {
21 let (second, nanosecond) = if value.nanosecond() >= 1_000_000_000 {
23 (60, value.nanosecond() - 1_000_000_000)
24 } else {
25 (value.second() as u8, value.nanosecond())
26 };
27 Time {
28 hour: value.hour() as u8,
29 minute: value.minute() as u8,
30 second,
31 nanosecond,
32 }
33}
34
35fn date_from_known(value: Date) -> Result<NaiveDate, Error> {
36 NaiveDate::from_ymd_opt(i32::from(value.year), value.month.into(), value.day.into())
37 .ok_or_else(|| invalid("invalid date"))
38}
39
40fn time_from_known(value: Time) -> Result<NaiveTime, Error> {
41 let (second, nanosecond) = if value.second == 60 {
42 (59, value.nanosecond + 1_000_000_000)
43 } else {
44 (value.second, value.nanosecond)
45 };
46 NaiveTime::from_hms_nano_opt(
47 value.hour.into(),
48 value.minute.into(),
49 second.into(),
50 nanosecond,
51 )
52 .ok_or_else(|| invalid("invalid time"))
53}
54
55fn datetime_to_known(value: NaiveDateTime) -> Result<Datetime, Error> {
56 Ok(Datetime {
57 date: Some(date_to_known(value.date())?),
58 time: Some(time_to_known(value.time())),
59 offset: None,
60 })
61}
62
63impl Bridge for NaiveDate {
64 type Known = Datetime;
65
66 const EXPECTING: &'static str = "local date";
67
68 fn to_known(&self) -> Result<Datetime, Error> {
69 date_to_known(*self).map(Datetime::from)
70 }
71
72 fn from_known(value: Datetime) -> Result<NaiveDate, Error> {
73 date_from_known(value.expect_local_date()?)
74 }
75}
76
77impl Bridge for NaiveTime {
78 type Known = Datetime;
79
80 const EXPECTING: &'static str = "local time";
81
82 fn to_known(&self) -> Result<Datetime, Error> {
83 Ok(Datetime::from(time_to_known(*self)))
84 }
85
86 fn from_known(value: Datetime) -> Result<NaiveTime, Error> {
87 time_from_known(value.expect_local_time()?)
88 }
89}
90
91impl Bridge for NaiveDateTime {
92 type Known = Datetime;
93
94 const EXPECTING: &'static str = "local date-time";
95
96 fn to_known(&self) -> Result<Datetime, Error> {
97 datetime_to_known(*self)
98 }
99
100 fn from_known(value: Datetime) -> Result<NaiveDateTime, Error> {
101 let (date, time) = value.expect_local_datetime()?;
102 Ok(NaiveDateTime::new(
103 date_from_known(date)?,
104 time_from_known(time)?,
105 ))
106 }
107}
108
109impl Bridge for DateTime<FixedOffset> {
110 type Known = Datetime;
111
112 const EXPECTING: &'static str = "offset date-time";
113
114 fn to_known(&self) -> Result<Datetime, Error> {
115 let seconds = self.offset().local_minus_utc();
116 if seconds % 60 != 0 {
117 return Err(out_of_range("offsets with seconds are not supported"));
118 }
119 let mut rv = datetime_to_known(self.naive_local())?;
120 rv.offset = Some(match seconds / 60 {
121 0 => Offset::Z,
122 minutes => Offset::Custom {
123 minutes: minutes as i16,
124 },
125 });
126 Ok(rv)
127 }
128
129 fn from_known(value: Datetime) -> Result<DateTime<FixedOffset>, Error> {
130 let (date, time, offset) = value.expect_offset_datetime()?;
131 let offset = FixedOffset::east_opt(i32::from(offset.minutes()) * 60)
132 .ok_or_else(|| invalid("invalid offset"))?;
133 NaiveDateTime::new(date_from_known(date)?, time_from_known(time)?)
134 .and_local_timezone(offset)
135 .single()
136 .ok_or_else(|| out_of_range("date-time out of range"))
137 }
138}
139
140impl Bridge for DateTime<Utc> {
141 type Known = Timestamp;
142
143 fn to_known(&self) -> Result<Timestamp, Error> {
144 Ok(Timestamp {
145 seconds: self.timestamp(),
146 nanosecond: self.timestamp_subsec_nanos().min(999_999_999),
148 })
149 }
150
151 fn from_known(value: Timestamp) -> Result<DateTime<Utc>, Error> {
152 DateTime::from_timestamp(value.seconds, value.nanosecond)
153 .ok_or_else(|| out_of_range("timestamp out of range"))
154 }
155}
156
157impl Bridge for TimeDelta {
158 type Known = Duration;
159
160 fn to_known(&self) -> Result<Duration, Error> {
161 Ok(Duration {
162 seconds: self.num_seconds(),
163 nanosecond: self.subsec_nanos(),
164 })
165 }
166
167 fn from_known(value: Duration) -> Result<TimeDelta, Error> {
168 TimeDelta::try_seconds(value.seconds)
169 .and_then(|x| x.checked_add(&TimeDelta::nanoseconds(value.nanosecond.into())))
170 .ok_or_else(|| out_of_range("duration out of range"))
171 }
172}
173
174impl_bridge!(
175 NaiveDate,
176 NaiveTime,
177 NaiveDateTime,
178 DateTime<FixedOffset>,
179 DateTime<Utc>,
180 TimeDelta,
181);