1extern crate time;
5
6use self::time::{
7 error::ComponentRange, macros::format_description, Date as NaiveDate, OffsetDateTime,
8 PrimitiveDateTime, Time as NaiveTime, UtcOffset,
9};
10#[allow(deprecated)]
12use self::time::format_description::FormatItem;
13
14use crate::backend::Backend;
15use crate::deserialize::{self, FromSql};
16use crate::serialize::{self, IsNull, Output, ToSql};
17use crate::sql_types::{Date, Time, Timestamp, TimestamptzSqlite};
18use crate::sqlite::Sqlite;
19
20#[allow(deprecated)]
30const DATE_FORMAT: &[FormatItem<'_>] = format_description!("[year]-[month]-[day]");
31
32#[allow(deprecated)]
34const ENCODE_TIME_FORMAT_WHOLE_SECOND: &[FormatItem<'_>] =
35 format_description!("[hour]:[minute]:[second]");
36
37#[allow(deprecated)]
39const ENCODE_TIME_FORMAT_SUBSECOND: &[FormatItem<'_>] =
40 format_description!("[hour]:[minute]:[second].[subsecond]");
41
42#[allow(deprecated)]
44const TIME_FORMATS: [&[FormatItem<'_>]; 9] = [
45 format_description!("[hour]:[minute]:[second].[subsecond]"),
47 format_description!("[hour]:[minute]:[second]"),
48 format_description!("[hour]:[minute]"),
50 format_description!("[hour]:[minute]Z"),
51 format_description!("[hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
52 format_description!("[hour]:[minute]:[second]Z"),
53 format_description!("[hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
54 format_description!("[hour]:[minute]:[second].[subsecond]Z"),
55 format_description!(
56 "[hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"
57 ),
58];
59
60#[allow(deprecated)]
62const ENCODE_PRIMITIVE_DATETIME_FORMAT_WHOLE_SECOND: &[FormatItem<'_>] =
63 format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
64
65#[allow(deprecated)]
67const ENCODE_PRIMITIVE_DATETIME_FORMAT_SUBSECOND: &[FormatItem<'_>] =
68 format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
69
70#[allow(deprecated)]
72const ENCODE_DATETIME_FORMAT_WHOLE_SECOND: &[FormatItem<'_>] = format_description!(
73 "[year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"
74);
75
76#[allow(deprecated)]
78const ENCODE_DATETIME_FORMAT_SUBSECOND: &[FormatItem<'_>] =
79 format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]");
80
81#[allow(deprecated)]
83const PRIMITIVE_DATETIME_FORMATS: [&[FormatItem<'_>]; 18] = [
84 format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"),
86 format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
87 format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"),
88 format_description!("[year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
89 format_description!("[year]-[month]-[day] [hour]:[minute]"),
91 format_description!("[year]-[month]-[day] [hour]:[minute]Z"),
92 format_description!("[year]-[month]-[day] [hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
93 format_description!("[year]-[month]-[day] [hour]:[minute]:[second]Z"),
94 format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]Z"),
95 format_description!("[year]-[month]-[day]T[hour]:[minute]"),
96 format_description!("[year]-[month]-[day]T[hour]:[minute]Z"),
97 format_description!("[year]-[month]-[day]T[hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
98 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]"),
99 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z"),
100 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
101 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond]"),
102 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond]Z"),
103 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
104];
105
106#[allow(deprecated)]
108const DATETIME_FORMATS: [&[FormatItem<'_>]; 12] = [
109 format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
111 format_description!("[year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
112 format_description!("[year]-[month]-[day] [hour]:[minute]Z"),
114 format_description!("[year]-[month]-[day] [hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
115 format_description!("[year]-[month]-[day] [hour]:[minute]:[second]Z"),
116 format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]Z"),
117 format_description!("[year]-[month]-[day]T[hour]:[minute]Z"),
118 format_description!("[year]-[month]-[day]T[hour]:[minute][offset_hour sign:mandatory]:[offset_minute]"),
119 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z"),
120 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute]"),
121 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond]Z"),
122 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"),
123];
124
125fn naive_utc(dt: OffsetDateTime) -> PrimitiveDateTime {
126 let dt = dt.to_offset(UtcOffset::UTC);
127 PrimitiveDateTime::new(dt.date(), dt.time())
128}
129
130fn parse_julian(julian_days: f64) -> Result<PrimitiveDateTime, ComponentRange> {
131 const EPOCH_IN_JULIAN_DAYS: f64 = 2_440_587.5;
132 const SECONDS_IN_DAY: f64 = 86400.0;
133 let timestamp = (julian_days - EPOCH_IN_JULIAN_DAYS) * SECONDS_IN_DAY;
134 #[allow(clippy::cast_possible_truncation)] OffsetDateTime::from_unix_timestamp_nanos((timestamp * 1E9) as i128).map(naive_utc)
136}
137
138#[cfg(all(feature = "sqlite", feature = "time"))]
139impl FromSql<Date, Sqlite> for NaiveDate {
140 fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
141 value
142 .parse_string(|s| Self::parse(s, DATE_FORMAT))
143 .map_err(Into::into)
144 }
145}
146
147#[cfg(all(feature = "sqlite", feature = "time"))]
148impl ToSql<Date, Sqlite> for NaiveDate {
149 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
150 out.set_value(self.format(DATE_FORMAT).map_err(|err| err.to_string())?);
151 Ok(IsNull::No)
152 }
153}
154
155#[cfg(all(feature = "sqlite", feature = "time"))]
156impl FromSql<Time, Sqlite> for NaiveTime {
157 fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
158 value.parse_string(|text| {
159 for format in TIME_FORMATS {
160 if let Ok(time) = Self::parse(text, format) {
161 return Ok(time);
162 }
163 }
164
165 Err(format!("Invalid time {text}").into())
166 })
167 }
168}
169
170#[cfg(all(feature = "sqlite", feature = "time"))]
171impl ToSql<Time, Sqlite> for NaiveTime {
172 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
173 let format = if self.microsecond() == 0 {
174 ENCODE_TIME_FORMAT_WHOLE_SECOND
175 } else {
176 ENCODE_TIME_FORMAT_SUBSECOND
177 };
178 out.set_value(self.format(format).map_err(|err| err.to_string())?);
179 Ok(IsNull::No)
180 }
181}
182
183#[cfg(all(feature = "sqlite", feature = "time"))]
184impl FromSql<Timestamp, Sqlite> for PrimitiveDateTime {
185 fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
186 value.parse_string(|text| {
187 for format in PRIMITIVE_DATETIME_FORMATS {
188 if let Ok(dt) = Self::parse(text, format) {
189 return Ok(dt);
190 }
191 }
192
193 if let Ok(julian_days) = text.parse::<f64>() {
194 if let Ok(timestamp) = parse_julian(julian_days) {
195 return Ok(timestamp);
196 }
197 }
198
199 Err(format!("Invalid datetime {text}").into())
200 })
201 }
202}
203
204#[cfg(all(feature = "sqlite", feature = "time"))]
205impl ToSql<Timestamp, Sqlite> for PrimitiveDateTime {
206 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
207 let format = if self.nanosecond() == 0 {
208 ENCODE_PRIMITIVE_DATETIME_FORMAT_WHOLE_SECOND
209 } else {
210 ENCODE_PRIMITIVE_DATETIME_FORMAT_SUBSECOND
211 };
212 out.set_value(self.format(format).map_err(|err| err.to_string())?);
213 Ok(IsNull::No)
214 }
215}
216
217#[cfg(all(feature = "sqlite", feature = "time"))]
218impl FromSql<TimestamptzSqlite, Sqlite> for PrimitiveDateTime {
219 fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
220 value.parse_string(|text| {
221 for format in PRIMITIVE_DATETIME_FORMATS {
222 if let Ok(dt) = Self::parse(text, format) {
223 return Ok(dt);
224 }
225 }
226
227 if let Ok(julian_days) = text.parse::<f64>() {
228 if let Ok(timestamp) = parse_julian(julian_days) {
229 return Ok(timestamp);
230 }
231 }
232
233 Err(format!("Invalid datetime {text}").into())
234 })
235 }
236}
237
238#[cfg(all(feature = "sqlite", feature = "time"))]
239impl ToSql<TimestamptzSqlite, Sqlite> for PrimitiveDateTime {
240 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
241 let format = if self.nanosecond() == 0 {
242 ENCODE_PRIMITIVE_DATETIME_FORMAT_WHOLE_SECOND
243 } else {
244 ENCODE_PRIMITIVE_DATETIME_FORMAT_SUBSECOND
245 };
246 out.set_value(self.format(format).map_err(|err| err.to_string())?);
247 Ok(IsNull::No)
248 }
249}
250
251#[cfg(all(feature = "sqlite", feature = "time"))]
252impl FromSql<TimestamptzSqlite, Sqlite> for OffsetDateTime {
253 fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
254 if let Ok(dt) = value.parse_string(|text| {
256 for format in DATETIME_FORMATS {
257 if let Ok(dt) = OffsetDateTime::parse(text, format) {
258 return Ok(dt);
259 }
260 }
261
262 Err(())
263 }) {
264 return Ok(dt);
265 }
266
267 let primitive_date_time =
269 <PrimitiveDateTime as FromSql<TimestamptzSqlite, Sqlite>>::from_sql(value)?;
270 Ok(primitive_date_time.assume_utc())
271 }
272}
273
274#[cfg(all(feature = "sqlite", feature = "time"))]
275impl ToSql<TimestamptzSqlite, Sqlite> for OffsetDateTime {
276 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
277 let dt_utc = self.to_offset(UtcOffset::UTC);
279 let format = if self.nanosecond() == 0 {
280 ENCODE_DATETIME_FORMAT_WHOLE_SECOND
281 } else {
282 ENCODE_DATETIME_FORMAT_SUBSECOND
283 };
284 out.set_value(dt_utc.format(format).map_err(|err| err.to_string())?);
285 Ok(IsNull::No)
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 extern crate dotenvy;
292
293 use super::time::{
294 macros::{date, datetime},
295 Date as NaiveDate, Duration, OffsetDateTime, PrimitiveDateTime, Time as NaiveTime,
296 };
297
298 use super::naive_utc;
299
300 use crate::dsl::{now, sql};
301 use crate::prelude::*;
302 use crate::select;
303 use crate::sql_types::{Text, Time, Timestamp, TimestamptzSqlite};
304 use crate::test_helpers::connection;
305
306 #[declare_sql_function]
307 extern "SQL" {
308 fn datetime(x: Text) -> Timestamp;
309 fn time(x: Text) -> Time;
310 fn date(x: Text) -> Date;
311 }
312
313 #[diesel_test_helper::test]
314 fn unix_epoch_encodes_correctly() {
315 let connection = &mut connection();
316 let time = datetime!(1970-1-1 0:0:0);
317 let query = select(datetime("1970-01-01 00:00:00.000000").eq(time));
318 assert_eq!(Ok(true), query.get_result(connection));
319 }
320
321 #[diesel_test_helper::test]
322 fn unix_epoch_decodes_correctly_in_all_possible_formats() {
323 let connection = &mut connection();
324 let time = datetime!(1970-1-1 0:0:0);
325 let valid_epoch_formats = vec![
326 "1970-01-01 00:00",
327 "1970-01-01 00:00:00",
328 "1970-01-01 00:00:00.000",
329 "1970-01-01 00:00:00.000000",
330 "1970-01-01T00:00",
331 "1970-01-01T00:00:00",
332 "1970-01-01T00:00:00.000",
333 "1970-01-01T00:00:00.000000",
334 "1970-01-01 00:00Z",
335 "1970-01-01 00:00:00Z",
336 "1970-01-01 00:00:00.000Z",
337 "1970-01-01 00:00:00.000000Z",
338 "1970-01-01T00:00Z",
339 "1970-01-01T00:00:00Z",
340 "1970-01-01T00:00:00.000Z",
341 "1970-01-01T00:00:00.000000Z",
342 "1970-01-01 00:00+00:00",
343 "1970-01-01 00:00:00+00:00",
344 "1970-01-01 00:00:00.000+00:00",
345 "1970-01-01 00:00:00.000000+00:00",
346 "1970-01-01T00:00+00:00",
347 "1970-01-01T00:00:00+00:00",
348 "1970-01-01T00:00:00.000+00:00",
349 "1970-01-01T00:00:00.000000+00:00",
350 "1970-01-01 00:00+01:00",
351 "1970-01-01 00:00:00+01:00",
352 "1970-01-01 00:00:00.000+01:00",
353 "1970-01-01 00:00:00.000000+01:00",
354 "1970-01-01T00:00+01:00",
355 "1970-01-01T00:00:00+01:00",
356 "1970-01-01T00:00:00.000+01:00",
357 "1970-01-01T00:00:00.000000+01:00",
358 "1970-01-01T00:00-01:00",
359 "1970-01-01T00:00:00-01:00",
360 "1970-01-01T00:00:00.000-01:00",
361 "1970-01-01T00:00:00.000000-01:00",
362 "1970-01-01T00:00-01:00",
363 "1970-01-01T00:00:00-01:00",
364 "1970-01-01T00:00:00.000-01:00",
365 "1970-01-01T00:00:00.000000-01:00",
366 "2440587.5",
367 ];
368
369 for s in valid_epoch_formats {
370 let epoch_from_sql =
371 select(sql::<Timestamp>(&format!("'{}'", s))).get_result(connection);
372 assert_eq!(Ok(time), epoch_from_sql, "format {} failed", s);
373 }
374 }
375
376 #[diesel_test_helper::test]
377 fn times_relative_to_now_encode_correctly() {
378 let connection = &mut connection();
379 let time = naive_utc(OffsetDateTime::now_utc()) + Duration::seconds(60);
380 let query = select(now.lt(time));
381 assert_eq!(Ok(true), query.get_result(connection));
382
383 let time = naive_utc(OffsetDateTime::now_utc()) - Duration::seconds(600);
384 let query = select(now.gt(time));
385 assert_eq!(Ok(true), query.get_result(connection));
386 }
387
388 #[diesel_test_helper::test]
389 fn times_of_day_encode_correctly() {
390 let connection = &mut connection();
391
392 let midnight = NaiveTime::from_hms(0, 0, 0).unwrap();
393 let query = select(time("00:00:00").eq(midnight));
394 assert!(query.get_result::<bool>(connection).unwrap());
395
396 let noon = NaiveTime::from_hms(12, 0, 0).unwrap();
397 let query = select(time("12:00:00").eq(noon));
398 assert!(query.get_result::<bool>(connection).unwrap());
399
400 let roughly_half_past_eleven = NaiveTime::from_hms_micro(23, 37, 4, 2200).unwrap();
401 let query = select(sql::<Time>("'23:37:04.0022'").eq(roughly_half_past_eleven));
402 assert!(query.get_result::<bool>(connection).unwrap());
403 }
404
405 #[diesel_test_helper::test]
406 fn times_of_day_decode_correctly() {
407 let connection = &mut connection();
408 let midnight = NaiveTime::from_hms(0, 0, 0).unwrap();
409 let valid_midnight_formats = &[
410 "00:00",
411 "00:00:00",
412 "00:00:00.000",
413 "00:00:00.000000",
414 "00:00Z",
415 "00:00:00Z",
416 "00:00:00.000Z",
417 "00:00:00.000000Z",
418 "00:00+00:00",
419 "00:00:00+00:00",
420 "00:00:00.000+00:00",
421 "00:00:00.000000+00:00",
422 "00:00+01:00",
423 "00:00:00+01:00",
424 "00:00:00.000+01:00",
425 "00:00:00.000000+01:00",
426 "00:00-01:00",
427 "00:00:00-01:00",
428 "00:00:00.000-01:00",
429 "00:00:00.000000-01:00",
430 ];
431 for format in valid_midnight_formats {
432 let query = select(sql::<Time>(&format!("'{}'", format)));
433 assert_eq!(
434 Ok(midnight),
435 query.get_result::<NaiveTime>(connection),
436 "format {} failed",
437 format
438 );
439 }
440
441 let noon = NaiveTime::from_hms(12, 0, 0).unwrap();
442 let query = select(sql::<Time>("'12:00:00'"));
443 assert_eq!(Ok(noon), query.get_result::<NaiveTime>(connection));
444
445 let roughly_half_past_eleven = NaiveTime::from_hms_micro(23, 37, 4, 2200).unwrap();
446 let query = select(sql::<Time>("'23:37:04.002200'"));
447 assert_eq!(
448 Ok(roughly_half_past_eleven),
449 query.get_result::<NaiveTime>(connection)
450 );
451 }
452
453 #[diesel_test_helper::test]
454 fn dates_encode_correctly() {
455 let connection = &mut connection();
456 let january_first_2000 = date!(2000 - 1 - 1);
457 let query = select(date("2000-01-01").eq(january_first_2000));
458 assert!(query.get_result::<bool>(connection).unwrap());
459
460 let distant_past = date!(0 - 4 - 11);
461 let query = select(date("0000-04-11").eq(distant_past));
462 assert!(query.get_result::<bool>(connection).unwrap());
463
464 let january_first_2018 = date!(2018 - 1 - 1);
465 let query = select(date("2018-01-01").eq(january_first_2018));
466 assert!(query.get_result::<bool>(connection).unwrap());
467
468 let distant_future = date!(9999 - 1 - 8);
469 let query = select(date("9999-01-08").eq(distant_future));
470 assert!(query.get_result::<bool>(connection).unwrap());
471 }
472
473 #[diesel_test_helper::test]
474 fn dates_decode_correctly() {
475 let connection = &mut connection();
476 let january_first_2000 = date!(2000 - 1 - 1);
477 let query = select(date("2000-01-01"));
478 assert_eq!(
479 Ok(january_first_2000),
480 query.get_result::<NaiveDate>(connection)
481 );
482
483 let distant_past = date!(0 - 4 - 11);
484 let query = select(date("0000-04-11"));
485 assert_eq!(Ok(distant_past), query.get_result::<NaiveDate>(connection));
486
487 let january_first_2018 = date!(2018 - 1 - 1);
488 let query = select(date("2018-01-01"));
489 assert_eq!(
490 Ok(january_first_2018),
491 query.get_result::<NaiveDate>(connection)
492 );
493
494 let distant_future = date!(9999 - 1 - 8);
495 let query = select(date("9999-01-08"));
496 assert_eq!(
497 Ok(distant_future),
498 query.get_result::<NaiveDate>(connection)
499 );
500 }
501
502 #[diesel_test_helper::test]
503 fn datetimes_decode_correctly() {
504 let connection = &mut connection();
505 let january_first_2000 = datetime!(2000-1-1 1:1:1);
506 let query = select(datetime("2000-01-01 01:01:01.000000"));
507 assert_eq!(
508 Ok(january_first_2000),
509 query.get_result::<PrimitiveDateTime>(connection)
510 );
511
512 let distant_past = datetime!(0-4-11 2:2:2);
513 let query = select(datetime("0000-04-11 02:02:02.000000"));
514 assert_eq!(
515 Ok(distant_past),
516 query.get_result::<PrimitiveDateTime>(connection)
517 );
518
519 let january_first_2018 = date!(2018 - 1 - 1);
520 let query = select(date("2018-01-01"));
521 assert_eq!(
522 Ok(january_first_2018),
523 query.get_result::<NaiveDate>(connection)
524 );
525
526 let distant_future = datetime!(9999 - 1 - 8 23:59:59.0001);
527 let query = select(sql::<Timestamp>("'9999-01-08 23:59:59.000100'"));
528 assert_eq!(
529 Ok(distant_future),
530 query.get_result::<PrimitiveDateTime>(connection)
531 );
532 }
533
534 #[diesel_test_helper::test]
535 fn datetimes_encode_correctly() {
536 let connection = &mut connection();
537 let january_first_2000 = datetime!(2000-1-1 0:0:0);
538 let query = select(datetime("2000-01-01 00:00:00").eq(january_first_2000));
539 assert!(query.get_result::<bool>(connection).unwrap());
540
541 let distant_past = datetime!(0-4-11 20:00:20);
542 let query = select(datetime("0000-04-11 20:00:20").eq(distant_past));
543 assert!(query.get_result::<bool>(connection).unwrap());
544
545 let january_first_2018 = datetime!(2018 - 1 - 1 12:00:00.0005);
546 let query = select(sql::<Timestamp>("'2018-01-01 12:00:00.0005'").eq(january_first_2018));
547 assert!(query.get_result::<bool>(connection).unwrap());
548
549 let distant_future = datetime!(9999-1-8 0:0:0);
550 let query = select(datetime("9999-01-08 00:00:00").eq(distant_future));
551 assert!(query.get_result::<bool>(connection).unwrap());
552 }
553
554 #[diesel_test_helper::test]
555 fn insert_timestamptz_into_table_as_text() {
556 crate::table! {
557 #[allow(unused_parens)]
558 test_insert_timestamptz_into_table_as_text(id) {
559 id -> Integer,
560 timestamp_with_tz -> TimestamptzSqlite,
561 }
562 }
563 let conn = &mut connection();
564 crate::sql_query(
565 "CREATE TABLE test_insert_timestamptz_into_table_as_text(id INTEGER PRIMARY KEY, timestamp_with_tz TEXT);",
566 )
567 .execute(conn)
568 .unwrap();
569
570 let time: OffsetDateTime = datetime!(1970-1-1 0:0:0.0 utc);
571
572 crate::insert_into(test_insert_timestamptz_into_table_as_text::table)
573 .values(vec![(
574 test_insert_timestamptz_into_table_as_text::id.eq(1),
575 test_insert_timestamptz_into_table_as_text::timestamp_with_tz.eq(sql::<
576 TimestamptzSqlite,
577 >(
578 "'1970-01-01 00:00:00.000000+00:00'",
579 )),
580 )])
581 .execute(conn)
582 .unwrap();
583
584 let result = test_insert_timestamptz_into_table_as_text::table
585 .select(test_insert_timestamptz_into_table_as_text::timestamp_with_tz)
586 .get_result::<OffsetDateTime>(conn)
587 .unwrap();
588 assert_eq!(result, time);
589 }
590
591 #[diesel_test_helper::test]
592 fn can_query_timestamptz_column_with_between() {
593 crate::table! {
594 #[allow(unused_parens)]
595 test_query_timestamptz_column_with_between(id) {
596 id -> Integer,
597 timestamp_with_tz -> TimestamptzSqlite,
598 }
599 }
600 let conn = &mut connection();
601 crate::sql_query(
602 "CREATE TABLE test_query_timestamptz_column_with_between(id INTEGER PRIMARY KEY, timestamp_with_tz TEXT);",
603 )
604 .execute(conn)
605 .unwrap();
606
607 crate::insert_into(test_query_timestamptz_column_with_between::table)
608 .values(vec![
609 (
610 test_query_timestamptz_column_with_between::id.eq(1),
611 test_query_timestamptz_column_with_between::timestamp_with_tz.eq(sql::<
612 TimestamptzSqlite,
613 >(
614 "'1970-01-01 00:00:01.000000+00:00'",
615 )),
616 ),
617 (
618 test_query_timestamptz_column_with_between::id.eq(2),
619 test_query_timestamptz_column_with_between::timestamp_with_tz.eq(sql::<
620 TimestamptzSqlite,
621 >(
622 "'1970-01-01 00:00:02.000000+00:00'",
623 )),
624 ),
625 (
626 test_query_timestamptz_column_with_between::id.eq(3),
627 test_query_timestamptz_column_with_between::timestamp_with_tz.eq(sql::<
628 TimestamptzSqlite,
629 >(
630 "'1970-01-01 00:00:03.000000+00:00'",
631 )),
632 ),
633 (
634 test_query_timestamptz_column_with_between::id.eq(4),
635 test_query_timestamptz_column_with_between::timestamp_with_tz.eq(sql::<
636 TimestamptzSqlite,
637 >(
638 "'1970-01-01 00:00:04.000000+00:00'",
639 )),
640 ),
641 ])
642 .execute(conn)
643 .unwrap();
644
645 let result = test_query_timestamptz_column_with_between::table
646 .select(test_query_timestamptz_column_with_between::timestamp_with_tz)
647 .filter(
648 test_query_timestamptz_column_with_between::timestamp_with_tz
649 .gt(datetime!(1970-1-1 0:0:0.0 utc)),
650 )
651 .filter(
652 test_query_timestamptz_column_with_between::timestamp_with_tz
653 .lt(datetime!(1970-1-1 0:0:4.0 utc)),
654 )
655 .count()
656 .get_result::<_>(conn);
657 assert_eq!(result, Ok(3));
658 }
659
660 #[diesel_test_helper::test]
661 fn unix_epoch_encodes_correctly_with_timezone() {
662 let connection = &mut connection();
663 let time = datetime!(1970-1-1 0:00:00.001 -1:00);
665 let query = select(sql::<TimestamptzSqlite>("'1970-01-01 01:00:00.001+00:00'").eq(time));
666 assert!(query.get_result::<bool>(connection).unwrap());
667 }
668
669 #[diesel_test_helper::test]
670 fn unix_epoch_encodes_correctly_with_utc_timezone() {
671 let connection = &mut connection();
672 let time: OffsetDateTime = datetime!(1970-1-1 0:0:0.001 utc);
673 let query = select(sql::<TimestamptzSqlite>("'1970-01-01 00:00:00.001+00:00'").eq(time));
674 assert!(query.get_result::<bool>(connection).unwrap());
675
676 let time: OffsetDateTime = datetime!(1970-1-1 0:0:0 utc);
678 let query = select(sql::<TimestamptzSqlite>("'1970-01-01 00:00:00+00:00'").eq(time));
679 assert!(query.get_result::<bool>(connection).unwrap());
680 }
681
682 #[diesel_test_helper::test]
683 fn unix_epoch_decodes_correctly_with_utc_timezone_in_all_possible_formats() {
684 let connection = &mut connection();
685 let time: OffsetDateTime = datetime!(1970-1-1 0:0:0 utc);
686 let valid_epoch_formats = vec![
687 "1970-01-01 00:00Z",
688 "1970-01-01 00:00:00Z",
689 "1970-01-01 00:00:00.000Z",
690 "1970-01-01 00:00:00.000000Z",
691 "1970-01-01T00:00Z",
692 "1970-01-01T00:00:00Z",
693 "1970-01-01T00:00:00.000Z",
694 "1970-01-01T00:00:00.000000Z",
695 "1970-01-01 00:00+00:00",
696 "1970-01-01 00:00:00+00:00",
697 "1970-01-01 00:00:00.000+00:00",
698 "1970-01-01 00:00:00.000000+00:00",
699 "1970-01-01T00:00+00:00",
700 "1970-01-01T00:00:00+00:00",
701 "1970-01-01T00:00:00.000+00:00",
702 "1970-01-01T00:00:00.000000+00:00",
703 "2440587.5",
704 ];
705
706 for s in valid_epoch_formats {
707 let epoch_from_sql =
708 select(sql::<TimestamptzSqlite>(&format!("'{}'", s))).get_result(connection);
709 assert_eq!(Ok(time), epoch_from_sql, "format {} failed", s);
710 }
711 }
712}