1use chrono::prelude::*;
17
18use crate::sql_type::FromSql;
19use crate::sql_type::IntervalDS;
20use crate::sql_type::OracleType;
21use crate::sql_type::Timestamp;
22use crate::sql_type::ToSql;
23use crate::sql_type::ToSqlNull;
24use crate::Connection;
25use crate::Error;
26use crate::Result;
27use crate::SqlValue;
28use chrono::naive::NaiveDate;
29use chrono::naive::NaiveDateTime;
30use chrono::offset::LocalResult;
31use chrono::Duration;
32
33fn fixed_offset_from_sql(ts: &Timestamp) -> Result<FixedOffset> {
34 FixedOffset::east_opt(ts.tz_offset())
35 .ok_or_else(|| Error::out_of_range(format!("invalid time zone offset: {}", ts.tz_offset())))
36}
37
38fn datetime_from_sql<Tz>(tz: &Tz, ts: &Timestamp) -> Result<DateTime<Tz>>
45where
46 Tz: TimeZone,
47{
48 date_from_sql(tz, ts)?
49 .and_hms_nano_opt(ts.hour(), ts.minute(), ts.second(), ts.nanosecond())
50 .ok_or_else(|| {
51 Error::out_of_range(format!(
52 "invalid year-month-day: {}-{}-{} {}:{}:{}.{:09}",
53 ts.year(),
54 ts.month(),
55 ts.day(),
56 ts.hour(),
57 ts.minute(),
58 ts.second(),
59 ts.nanosecond()
60 ))
61 })
62}
63
64impl FromSql for DateTime<Utc> {
65 fn from_sql(val: &SqlValue) -> Result<DateTime<Utc>> {
66 let ts = val.to_timestamp()?;
67 datetime_from_sql(&Utc, &ts)
68 }
69}
70
71impl FromSql for DateTime<Local> {
72 fn from_sql(val: &SqlValue) -> Result<DateTime<Local>> {
73 let ts = val.to_timestamp()?;
74 datetime_from_sql(&Local, &ts)
75 }
76}
77
78impl FromSql for DateTime<FixedOffset> {
79 fn from_sql(val: &SqlValue) -> Result<DateTime<FixedOffset>> {
80 let ts = val.to_timestamp()?;
81 datetime_from_sql(&fixed_offset_from_sql(&ts)?, &ts)
82 }
83}
84
85impl<Tz> ToSqlNull for DateTime<Tz>
86where
87 Tz: TimeZone,
88{
89 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
90 Ok(OracleType::TimestampTZ(9))
91 }
92}
93
94impl<Tz> ToSql for DateTime<Tz>
95where
96 Tz: TimeZone,
97{
98 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
99 Ok(OracleType::TimestampTZ(9))
100 }
101
102 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
103 let ts = Timestamp::new(
104 self.year(),
105 self.month(),
106 self.day(),
107 self.hour(),
108 self.minute(),
109 self.second(),
110 self.nanosecond(),
111 )?;
112 let ts = ts.and_tz_offset(self.offset().fix().local_minus_utc())?;
113 val.set_timestamp(&ts)
114 }
115}
116
117#[allow(deprecated)]
128fn date_from_sql<Tz>(tz: &Tz, ts: &Timestamp) -> Result<Date<Tz>>
129where
130 Tz: TimeZone,
131{
132 match tz.ymd_opt(ts.year(), ts.month(), ts.day()) {
133 LocalResult::Single(date) => Ok(date),
134 _ => Err(Error::out_of_range(format!(
135 "invalid month and/or day: {}-{}-{}",
136 ts.year(),
137 ts.month(),
138 ts.day()
139 ))),
140 }
141}
142
143#[allow(deprecated)]
144impl FromSql for Date<Utc> {
145 fn from_sql(val: &SqlValue) -> Result<Date<Utc>> {
146 let ts = val.to_timestamp()?;
147 date_from_sql(&Utc, &ts)
148 }
149}
150
151#[allow(deprecated)]
152impl FromSql for Date<Local> {
153 fn from_sql(val: &SqlValue) -> Result<Date<Local>> {
154 let ts = val.to_timestamp()?;
155 date_from_sql(&Local, &ts)
156 }
157}
158
159#[allow(deprecated)]
160impl FromSql for Date<FixedOffset> {
161 fn from_sql(val: &SqlValue) -> Result<Date<FixedOffset>> {
162 let ts = val.to_timestamp()?;
163 date_from_sql(&fixed_offset_from_sql(&ts)?, &ts)
164 }
165}
166
167#[allow(deprecated)]
168impl<Tz> ToSqlNull for Date<Tz>
169where
170 Tz: TimeZone,
171{
172 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
173 Ok(OracleType::TimestampTZ(0))
174 }
175}
176
177#[allow(deprecated)]
178impl<Tz> ToSql for Date<Tz>
179where
180 Tz: TimeZone,
181{
182 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
183 Ok(OracleType::TimestampTZ(0))
184 }
185
186 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
187 let ts = Timestamp::new(self.year(), self.month(), self.day(), 0, 0, 0, 0)?;
188 let ts = ts.and_tz_offset(self.offset().fix().local_minus_utc())?;
189 val.set_timestamp(&ts)
190 }
191}
192
193fn naive_date_time_from_sql(ts: &Timestamp) -> Result<NaiveDateTime> {
198 naive_date_from_sql(ts)?
199 .and_hms_nano_opt(ts.hour(), ts.minute(), ts.second(), ts.nanosecond())
200 .ok_or_else(|| {
201 Error::out_of_range(format!(
202 "invalid year-month-day: {}-{}-{} {}:{}:{}.{:09}",
203 ts.year(),
204 ts.month(),
205 ts.day(),
206 ts.hour(),
207 ts.minute(),
208 ts.second(),
209 ts.nanosecond()
210 ))
211 })
212}
213
214impl FromSql for NaiveDateTime {
215 fn from_sql(val: &SqlValue) -> Result<NaiveDateTime> {
216 let ts = val.to_timestamp()?;
217 naive_date_time_from_sql(&ts)
218 }
219}
220
221impl ToSqlNull for NaiveDateTime {
222 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
223 Ok(OracleType::Timestamp(9))
224 }
225}
226
227impl ToSql for NaiveDateTime {
228 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
229 Ok(OracleType::Timestamp(9))
230 }
231
232 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
233 let ts = Timestamp::new(
234 self.year(),
235 self.month(),
236 self.day(),
237 self.hour(),
238 self.minute(),
239 self.second(),
240 self.nanosecond(),
241 )?;
242 val.set_timestamp(&ts)
243 }
244}
245
246fn naive_date_from_sql(ts: &Timestamp) -> Result<NaiveDate> {
251 NaiveDate::from_ymd_opt(ts.year(), ts.month(), ts.day()).ok_or_else(|| {
252 Error::out_of_range(format!(
253 "invalid year-month-day: {}-{}-{}",
254 ts.year(),
255 ts.month(),
256 ts.day()
257 ))
258 })
259}
260
261impl FromSql for NaiveDate {
262 fn from_sql(val: &SqlValue) -> Result<NaiveDate> {
263 let ts = val.to_timestamp()?;
264 naive_date_from_sql(&ts)
265 }
266}
267
268impl ToSqlNull for NaiveDate {
269 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
270 Ok(OracleType::Timestamp(0))
271 }
272}
273
274impl ToSql for NaiveDate {
275 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
276 Ok(OracleType::Timestamp(0))
277 }
278
279 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
280 let ts = Timestamp::new(self.year(), self.month(), self.day(), 0, 0, 0, 0)?;
281 val.set_timestamp(&ts)
282 }
283}
284
285impl FromSql for Duration {
290 fn from_sql(val: &SqlValue) -> Result<Duration> {
291 let err = |it: IntervalDS| {
292 Error::out_of_range(format!(
293 "unable to convert interval day to second {} to chrono::Duration",
294 it
295 ))
296 };
297 let it = val.to_interval_ds()?;
298 let d = Duration::milliseconds(0);
299 let d = d
300 .checked_add(&Duration::days(it.days() as i64))
301 .ok_or_else(|| err(it))?;
302 let d = d
303 .checked_add(&Duration::hours(it.hours() as i64))
304 .ok_or_else(|| err(it))?;
305 let d = d
306 .checked_add(&Duration::minutes(it.minutes() as i64))
307 .ok_or_else(|| err(it))?;
308 let d = d
309 .checked_add(&Duration::seconds(it.seconds() as i64))
310 .ok_or_else(|| err(it))?;
311 let d = d
312 .checked_add(&Duration::nanoseconds(it.nanoseconds() as i64))
313 .ok_or_else(|| err(it))?;
314 Ok(d)
315 }
316}
317
318impl ToSqlNull for Duration {
319 fn oratype_for_null(_conn: &Connection) -> Result<OracleType> {
320 Ok(OracleType::IntervalDS(9, 9))
321 }
322}
323
324impl ToSql for Duration {
325 fn oratype(&self, _conn: &Connection) -> Result<OracleType> {
326 Ok(OracleType::IntervalDS(9, 9))
327 }
328
329 fn to_sql(&self, val: &mut SqlValue) -> Result<()> {
330 let secs = self.num_seconds();
331 let nsecs = (*self - Duration::seconds(secs)).num_nanoseconds().unwrap();
332 let days = secs / (24 * 60 * 60);
333 let secs = secs % (24 * 60 * 60);
334 let hours = secs / (60 * 60);
335 let secs = secs % (60 * 60);
336 let minutes = secs / 60;
337 let secs = secs % 60;
338 if days.abs() >= 1000000000 {
339 return Err(Error::out_of_range(format!("too large days: {}", self)));
340 }
341 let it = IntervalDS::new(
342 days as i32,
343 hours as i32,
344 minutes as i32,
345 secs as i32,
346 nsecs as i32,
347 )?;
348 val.set_interval_ds(&it)
349 }
350}