1extern crate chrono;
5use self::chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
6
7use super::{PgDate, PgInterval, PgTime, PgTimestamp};
8use crate::deserialize::{self, FromSql};
9use crate::pg::{Pg, PgValue};
10use crate::serialize::{self, Output, ToSql};
11use crate::sql_types::{Date, Interval, Time, Timestamp, Timestamptz};
12
13fn pg_epoch() -> NaiveDateTime {
15 NaiveDate::from_ymd_opt(2000, 1, 1)
16 .expect("This is in supported range of chrono dates")
17 .and_hms_opt(0, 0, 0)
18 .expect("This is a valid input")
19}
20
21#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
22impl FromSql<Timestamp, Pg> for NaiveDateTime {
23 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
24 let PgTimestamp(offset) = FromSql::<Timestamp, Pg>::from_sql(bytes)?;
25 match pg_epoch().checked_add_signed(Duration::microseconds(offset)) {
26 Some(v) => Ok(v),
27 None => {
28 let message = "Tried to deserialize a timestamp that is too large for Chrono";
29 Err(message.into())
30 }
31 }
32 }
33}
34
35#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
36impl ToSql<Timestamp, Pg> for NaiveDateTime {
37 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
38 let time = match (self.signed_duration_since(pg_epoch())).num_microseconds() {
39 Some(time) => time,
40 None => {
41 let error_message =
42 format!("{self:?} as microseconds is too large to fit in an i64");
43 return Err(error_message.into());
44 }
45 };
46 ToSql::<Timestamp, Pg>::to_sql(&PgTimestamp(time), &mut out.reborrow())
47 }
48}
49
50#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
51impl FromSql<Timestamptz, Pg> for NaiveDateTime {
52 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
53 FromSql::<Timestamp, Pg>::from_sql(bytes)
54 }
55}
56
57#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
58impl ToSql<Timestamptz, Pg> for NaiveDateTime {
59 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
60 ToSql::<Timestamp, Pg>::to_sql(self, out)
61 }
62}
63
64#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
65impl FromSql<Timestamptz, Pg> for DateTime<Utc> {
66 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
67 let naive_date_time = <NaiveDateTime as FromSql<Timestamptz, Pg>>::from_sql(bytes)?;
68 Ok(Utc.from_utc_datetime(&naive_date_time))
69 }
70}
71
72#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
73impl FromSql<Timestamptz, Pg> for DateTime<Local> {
74 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
75 let naive_date_time = <NaiveDateTime as FromSql<Timestamptz, Pg>>::from_sql(bytes)?;
76 Ok(Local::from_utc_datetime(&Local, &naive_date_time))
77 }
78}
79
80#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
81impl<TZ: TimeZone> ToSql<Timestamptz, Pg> for DateTime<TZ> {
82 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
83 ToSql::<Timestamptz, Pg>::to_sql(&self.naive_utc(), &mut out.reborrow())
84 }
85}
86
87fn midnight() -> NaiveTime {
88 NaiveTime::from_hms_opt(0, 0, 0).expect("This is a valid hms spec")
89}
90
91#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
92impl ToSql<Time, Pg> for NaiveTime {
93 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
94 let duration = self.signed_duration_since(midnight());
95 match duration.num_microseconds() {
96 Some(offset) => ToSql::<Time, Pg>::to_sql(&PgTime(offset), &mut out.reborrow()),
97 None => unreachable!(),
98 }
99 }
100}
101
102#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
103impl FromSql<Time, Pg> for NaiveTime {
104 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
105 let PgTime(offset) = FromSql::<Time, Pg>::from_sql(bytes)?;
106 let duration = Duration::microseconds(offset);
107 Ok(midnight() + duration)
108 }
109}
110
111fn pg_epoch_date() -> NaiveDate {
112 NaiveDate::from_ymd_opt(2000, 1, 1).expect("This is in supported range of chrono dates")
113}
114
115#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
116impl ToSql<Date, Pg> for NaiveDate {
117 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
118 let days_since_epoch = self.signed_duration_since(pg_epoch_date()).num_days();
119 ToSql::<Date, Pg>::to_sql(&PgDate(days_since_epoch.try_into()?), &mut out.reborrow())
120 }
121}
122
123#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
124impl FromSql<Date, Pg> for NaiveDate {
125 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
126 let PgDate(offset) = FromSql::<Date, Pg>::from_sql(bytes)?;
127 #[allow(deprecated)] let duration = Duration::days(i64::from(offset));
129 match pg_epoch_date().checked_add_signed(duration) {
130 Some(date) => Ok(date),
131 None => {
132 let error_message = format!(
133 "Chrono can only represent dates up to {:?}",
134 chrono::NaiveDate::MAX
135 );
136 Err(error_message.into())
137 }
138 }
139 }
140}
141
142const DAYS_PER_MONTH: i32 = 30;
143const SECONDS_PER_DAY: i64 = 60 * 60 * 24;
144const MICROSECONDS_PER_SECOND: i64 = 1_000_000;
145
146#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
147impl ToSql<Interval, Pg> for Duration {
148 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
149 let microseconds: i64 = if let Some(v) = self.num_microseconds() {
150 v % (MICROSECONDS_PER_SECOND * SECONDS_PER_DAY)
151 } else {
152 return Err("Failed to create microseconds by overflow".into());
153 };
154 let days: i32 = self
155 .num_days()
156 .try_into()
157 .expect("Failed to get i32 days from i64");
158 let interval = PgInterval {
163 microseconds,
164 days,
165 months: 0,
166 };
167 <PgInterval as ToSql<Interval, Pg>>::to_sql(&interval, &mut out.reborrow())
168 }
169}
170
171#[cfg(all(feature = "chrono", feature = "postgres_backend"))]
172impl FromSql<Interval, Pg> for Duration {
173 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
174 let interval: PgInterval = FromSql::<Interval, Pg>::from_sql(bytes)?;
175 let days = interval.months * DAYS_PER_MONTH + interval.days;
180 Ok(Duration::days(days as i64) + Duration::microseconds(interval.microseconds))
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 extern crate chrono;
187 extern crate dotenvy;
188
189 use self::chrono::{Duration, FixedOffset, NaiveDate, NaiveTime, TimeZone, Utc};
190
191 use crate::dsl::{now, sql};
192 use crate::prelude::*;
193 use crate::select;
194 use crate::sql_types::{Date, Interval, Time, Timestamp, Timestamptz};
195 use crate::test_helpers::connection;
196
197 #[test]
198 fn unix_epoch_encodes_correctly() {
199 let connection = &mut connection();
200 let time = NaiveDate::from_ymd_opt(1970, 1, 1)
201 .unwrap()
202 .and_hms_opt(0, 0, 0)
203 .unwrap();
204 let query = select(sql::<Timestamp>("'1970-01-01'").eq(time));
205 assert!(query.get_result::<bool>(connection).unwrap());
206 }
207
208 #[test]
209 fn unix_epoch_encodes_correctly_with_utc_timezone() {
210 let connection = &mut connection();
211 let time = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).single().unwrap();
212 let query = select(sql::<Timestamptz>("'1970-01-01Z'::timestamptz").eq(time));
213 assert!(query.get_result::<bool>(connection).unwrap());
214 }
215
216 #[test]
217 fn unix_epoch_encodes_correctly_with_timezone() {
218 let connection = &mut connection();
219 let time = FixedOffset::west_opt(3600)
220 .unwrap()
221 .with_ymd_and_hms(1970, 1, 1, 0, 0, 0)
222 .single()
223 .unwrap();
224 let query = select(sql::<Timestamptz>("'1970-01-01 01:00:00Z'::timestamptz").eq(time));
225 assert!(query.get_result::<bool>(connection).unwrap());
226 }
227
228 #[test]
229 fn unix_epoch_decodes_correctly() {
230 let connection = &mut connection();
231 let time = NaiveDate::from_ymd_opt(1970, 1, 1)
232 .unwrap()
233 .and_hms_opt(0, 0, 0)
234 .unwrap();
235 let epoch_from_sql =
236 select(sql::<Timestamp>("'1970-01-01'::timestamp")).get_result(connection);
237 assert_eq!(Ok(time), epoch_from_sql);
238 }
239
240 #[test]
241 fn unix_epoch_decodes_correctly_with_timezone() {
242 let connection = &mut connection();
243 let time = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).single().unwrap();
244 let epoch_from_sql =
245 select(sql::<Timestamptz>("'1970-01-01Z'::timestamptz")).get_result(connection);
246 assert_eq!(Ok(time), epoch_from_sql);
247 }
248
249 #[test]
250 fn times_relative_to_now_encode_correctly() {
251 let connection = &mut connection();
252 let time = Utc::now().naive_utc() + Duration::try_seconds(60).unwrap();
253 let query = select(now.at_time_zone("utc").lt(time));
254 assert!(query.get_result::<bool>(connection).unwrap());
255
256 let time = Utc::now().naive_utc() - Duration::try_seconds(60).unwrap();
257 let query = select(now.at_time_zone("utc").gt(time));
258 assert!(query.get_result::<bool>(connection).unwrap());
259 }
260
261 #[test]
262 fn times_with_timezones_round_trip_after_conversion() {
263 let connection = &mut connection();
264 let time = FixedOffset::east_opt(3600)
265 .unwrap()
266 .with_ymd_and_hms(2016, 1, 2, 1, 0, 0)
267 .unwrap();
268 let expected = NaiveDate::from_ymd_opt(2016, 1, 1)
269 .unwrap()
270 .and_hms_opt(20, 0, 0)
271 .unwrap();
272 let query = select(time.into_sql::<Timestamptz>().at_time_zone("EDT"));
273 assert_eq!(Ok(expected), query.get_result(connection));
274 }
275
276 #[test]
277 fn times_of_day_encode_correctly() {
278 let connection = &mut connection();
279
280 let midnight = NaiveTime::from_hms_opt(0, 0, 0).unwrap();
281 let query = select(sql::<Time>("'00:00:00'::time").eq(midnight));
282 assert!(query.get_result::<bool>(connection).unwrap());
283
284 let noon = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
285 let query = select(sql::<Time>("'12:00:00'::time").eq(noon));
286 assert!(query.get_result::<bool>(connection).unwrap());
287
288 let roughly_half_past_eleven = NaiveTime::from_hms_micro_opt(23, 37, 4, 2200).unwrap();
289 let query = select(sql::<Time>("'23:37:04.002200'::time").eq(roughly_half_past_eleven));
290 assert!(query.get_result::<bool>(connection).unwrap());
291 }
292
293 #[test]
294 fn times_of_day_decode_correctly() {
295 let connection = &mut connection();
296 let midnight = NaiveTime::from_hms_opt(0, 0, 0).unwrap();
297 let query = select(sql::<Time>("'00:00:00'::time"));
298 assert_eq!(Ok(midnight), query.get_result::<NaiveTime>(connection));
299
300 let noon = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
301 let query = select(sql::<Time>("'12:00:00'::time"));
302 assert_eq!(Ok(noon), query.get_result::<NaiveTime>(connection));
303
304 let roughly_half_past_eleven = NaiveTime::from_hms_micro_opt(23, 37, 4, 2200).unwrap();
305 let query = select(sql::<Time>("'23:37:04.002200'::time"));
306 assert_eq!(
307 Ok(roughly_half_past_eleven),
308 query.get_result::<NaiveTime>(connection)
309 );
310 }
311
312 #[test]
313 fn dates_encode_correctly() {
314 let connection = &mut connection();
315 let january_first_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
316 let query = select(sql::<Date>("'2000-1-1'").eq(january_first_2000));
317 assert!(query.get_result::<bool>(connection).unwrap());
318
319 let distant_past = NaiveDate::from_ymd_opt(-398, 4, 11).unwrap(); let query = select(sql::<Date>("'399-4-11 BC'").eq(distant_past));
321 assert!(query.get_result::<bool>(connection).unwrap());
322
323 let julian_epoch = NaiveDate::from_ymd_opt(-4713, 11, 24).unwrap();
324 let query = select(sql::<Date>("'J0'::date").eq(julian_epoch));
325 assert!(query.get_result::<bool>(connection).unwrap());
326
327 let max_date = NaiveDate::from_ymd_opt(262142, 12, 31).unwrap();
328 let query = select(sql::<Date>("'262142-12-31'::date").eq(max_date));
329 assert!(query.get_result::<bool>(connection).unwrap());
330
331 let january_first_2018 = NaiveDate::from_ymd_opt(2018, 1, 1).unwrap();
332 let query = select(sql::<Date>("'2018-1-1'::date").eq(january_first_2018));
333 assert!(query.get_result::<bool>(connection).unwrap());
334
335 let distant_future = NaiveDate::from_ymd_opt(72_400, 1, 8).unwrap();
336 let query = select(sql::<Date>("'72400-1-8'::date").eq(distant_future));
337 assert!(query.get_result::<bool>(connection).unwrap());
338 }
339
340 #[test]
341 fn dates_decode_correctly() {
342 let connection = &mut connection();
343 let january_first_2000 = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
344 let query = select(sql::<Date>("'2000-1-1'::date"));
345 assert_eq!(
346 Ok(january_first_2000),
347 query.get_result::<NaiveDate>(connection)
348 );
349
350 let distant_past = NaiveDate::from_ymd_opt(-398, 4, 11).unwrap();
351 let query = select(sql::<Date>("'399-4-11 BC'::date"));
352 assert_eq!(Ok(distant_past), query.get_result::<NaiveDate>(connection));
353
354 let julian_epoch = NaiveDate::from_ymd_opt(-4713, 11, 24).unwrap();
355 let query = select(sql::<Date>("'J0'::date"));
356 assert_eq!(Ok(julian_epoch), query.get_result::<NaiveDate>(connection));
357
358 let max_date = NaiveDate::from_ymd_opt(262142, 12, 31).unwrap();
359 let query = select(sql::<Date>("'262142-12-31'::date"));
360 assert_eq!(Ok(max_date), query.get_result::<NaiveDate>(connection));
361
362 let january_first_2018 = NaiveDate::from_ymd_opt(2018, 1, 1).unwrap();
363 let query = select(sql::<Date>("'2018-1-1'::date"));
364 assert_eq!(
365 Ok(january_first_2018),
366 query.get_result::<NaiveDate>(connection)
367 );
368
369 let distant_future = NaiveDate::from_ymd_opt(72_400, 1, 8).unwrap();
370 let query = select(sql::<Date>("'72400-1-8'::date"));
371 assert_eq!(
372 Ok(distant_future),
373 query.get_result::<NaiveDate>(connection)
374 );
375 }
376
377 fn get_test_duration_and_literal_strings() -> (Duration, Vec<&'static str>) {
379 (
380 Duration::days(60) + Duration::minutes(1) + Duration::microseconds(123456),
381 vec![
382 "60 days 1 minute 123456 microseconds",
383 "2 months 1 minute 123456 microseconds",
384 "5184060 seconds 123456 microseconds",
385 "60 days 60123456 microseconds",
386 "59 days 24 hours 60.123456 seconds",
387 "60 0:01:00.123456",
388 "58 48:01:00.123456",
389 "P0Y2M0DT0H1M0.123456S",
390 "0-2 0:01:00.123456",
391 "P0000-02-00T00:01:00.123456",
392 "1440:01:00.123456",
393 "1 month 30 days 0.5 minutes 30.123456 seconds",
394 ],
395 )
396 }
397
398 #[test]
399 fn duration_encode_correctly() {
400 let connection = &mut connection();
401 let (duration, literal_strings) = get_test_duration_and_literal_strings();
402 for literal in literal_strings {
403 let query = select(sql::<Interval>(&format!("'{}'::interval", literal)).eq(duration));
404 assert!(query.get_result::<bool>(connection).unwrap());
405 }
406 }
407
408 #[test]
409 fn duration_decode_correctly() {
410 let connection = &mut connection();
411 let (duration, literal_strings) = get_test_duration_and_literal_strings();
412 for literal in literal_strings {
413 let query = select(sql::<Interval>(&format!("'{}'::interval", literal)));
414 assert_eq!(Ok(duration), query.get_result::<Duration>(connection));
415 }
416 }
417}