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 define_sql_function!(fn datetime(x: Text) -> Timestamp);
307 define_sql_function!(fn time(x: Text) -> Time);
308 define_sql_function!(fn date(x: Text) -> Date);
309
310 #[test]
311 fn unix_epoch_encodes_correctly() {
312 let connection = &mut connection();
313 let time = datetime!(1970-1-1 0:0:0);
314 let query = select(datetime("1970-01-01 00:00:00.000000").eq(time));
315 assert_eq!(Ok(true), query.get_result(connection));
316 }
317
318 #[test]
319 fn unix_epoch_decodes_correctly_in_all_possible_formats() {
320 let connection = &mut connection();
321 let time = datetime!(1970-1-1 0:0:0);
322 let valid_epoch_formats = vec![
323 "1970-01-01 00:00",
324 "1970-01-01 00:00:00",
325 "1970-01-01 00:00:00.000",
326 "1970-01-01 00:00:00.000000",
327 "1970-01-01T00:00",
328 "1970-01-01T00:00:00",
329 "1970-01-01T00:00:00.000",
330 "1970-01-01T00:00:00.000000",
331 "1970-01-01 00:00Z",
332 "1970-01-01 00:00:00Z",
333 "1970-01-01 00:00:00.000Z",
334 "1970-01-01 00:00:00.000000Z",
335 "1970-01-01T00:00Z",
336 "1970-01-01T00:00:00Z",
337 "1970-01-01T00:00:00.000Z",
338 "1970-01-01T00:00:00.000000Z",
339 "1970-01-01 00:00+00:00",
340 "1970-01-01 00:00:00+00:00",
341 "1970-01-01 00:00:00.000+00:00",
342 "1970-01-01 00:00:00.000000+00:00",
343 "1970-01-01T00:00+00:00",
344 "1970-01-01T00:00:00+00:00",
345 "1970-01-01T00:00:00.000+00:00",
346 "1970-01-01T00:00:00.000000+00:00",
347 "1970-01-01 00:00+01:00",
348 "1970-01-01 00:00:00+01:00",
349 "1970-01-01 00:00:00.000+01:00",
350 "1970-01-01 00:00:00.000000+01:00",
351 "1970-01-01T00:00+01:00",
352 "1970-01-01T00:00:00+01:00",
353 "1970-01-01T00:00:00.000+01:00",
354 "1970-01-01T00:00:00.000000+01:00",
355 "1970-01-01T00:00-01:00",
356 "1970-01-01T00:00:00-01:00",
357 "1970-01-01T00:00:00.000-01:00",
358 "1970-01-01T00:00:00.000000-01:00",
359 "1970-01-01T00:00-01:00",
360 "1970-01-01T00:00:00-01:00",
361 "1970-01-01T00:00:00.000-01:00",
362 "1970-01-01T00:00:00.000000-01:00",
363 "2440587.5",
364 ];
365
366 for s in valid_epoch_formats {
367 let epoch_from_sql = select(sql::<Timestamp>(&format!("'{s}'"))).get_result(connection);
368 assert_eq!(Ok(time), epoch_from_sql, "format {s} failed");
369 }
370 }
371
372 #[test]
373 fn times_relative_to_now_encode_correctly() {
374 let connection = &mut connection();
375 let time = naive_utc(OffsetDateTime::now_utc()) + Duration::seconds(60);
376 let query = select(now.lt(time));
377 assert_eq!(Ok(true), query.get_result(connection));
378
379 let time = naive_utc(OffsetDateTime::now_utc()) - Duration::seconds(600);
380 let query = select(now.gt(time));
381 assert_eq!(Ok(true), query.get_result(connection));
382 }
383
384 #[test]
385 fn times_of_day_encode_correctly() {
386 let connection = &mut connection();
387
388 let midnight = NaiveTime::from_hms(0, 0, 0).unwrap();
389 let query = select(time("00:00:00").eq(midnight));
390 assert!(query.get_result::<bool>(connection).unwrap());
391
392 let noon = NaiveTime::from_hms(12, 0, 0).unwrap();
393 let query = select(time("12:00:00").eq(noon));
394 assert!(query.get_result::<bool>(connection).unwrap());
395
396 let roughly_half_past_eleven = NaiveTime::from_hms_micro(23, 37, 4, 2200).unwrap();
397 let query = select(sql::<Time>("'23:37:04.0022'").eq(roughly_half_past_eleven));
398 assert!(query.get_result::<bool>(connection).unwrap());
399 }
400
401 #[test]
402 fn times_of_day_decode_correctly() {
403 let connection = &mut connection();
404 let midnight = NaiveTime::from_hms(0, 0, 0).unwrap();
405 let valid_midnight_formats = &[
406 "00:00",
407 "00:00:00",
408 "00:00:00.000",
409 "00:00:00.000000",
410 "00:00Z",
411 "00:00:00Z",
412 "00:00:00.000Z",
413 "00:00:00.000000Z",
414 "00:00+00:00",
415 "00:00:00+00:00",
416 "00:00:00.000+00:00",
417 "00:00:00.000000+00:00",
418 "00:00+01:00",
419 "00:00:00+01:00",
420 "00:00:00.000+01:00",
421 "00:00:00.000000+01: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 ];
427 for format in valid_midnight_formats {
428 let query = select(sql::<Time>(&format!("'{format}'")));
429 assert_eq!(
430 Ok(midnight),
431 query.get_result::<NaiveTime>(connection),
432 "format {format} failed"
433 );
434 }
435
436 let noon = NaiveTime::from_hms(12, 0, 0).unwrap();
437 let query = select(sql::<Time>("'12:00:00'"));
438 assert_eq!(Ok(noon), query.get_result::<NaiveTime>(connection));
439
440 let roughly_half_past_eleven = NaiveTime::from_hms_micro(23, 37, 4, 2200).unwrap();
441 let query = select(sql::<Time>("'23:37:04.002200'"));
442 assert_eq!(
443 Ok(roughly_half_past_eleven),
444 query.get_result::<NaiveTime>(connection)
445 );
446 }
447
448 #[test]
449 fn dates_encode_correctly() {
450 let connection = &mut connection();
451 let january_first_2000 = date!(2000 - 1 - 1);
452 let query = select(date("2000-01-01").eq(january_first_2000));
453 assert!(query.get_result::<bool>(connection).unwrap());
454
455 let distant_past = date!(0 - 4 - 11);
456 let query = select(date("0000-04-11").eq(distant_past));
457 assert!(query.get_result::<bool>(connection).unwrap());
458
459 let january_first_2018 = date!(2018 - 1 - 1);
460 let query = select(date("2018-01-01").eq(january_first_2018));
461 assert!(query.get_result::<bool>(connection).unwrap());
462
463 let distant_future = date!(9999 - 1 - 8);
464 let query = select(date("9999-01-08").eq(distant_future));
465 assert!(query.get_result::<bool>(connection).unwrap());
466 }
467
468 #[test]
469 fn dates_decode_correctly() {
470 let connection = &mut connection();
471 let january_first_2000 = date!(2000 - 1 - 1);
472 let query = select(date("2000-01-01"));
473 assert_eq!(
474 Ok(january_first_2000),
475 query.get_result::<NaiveDate>(connection)
476 );
477
478 let distant_past = date!(0 - 4 - 11);
479 let query = select(date("0000-04-11"));
480 assert_eq!(Ok(distant_past), query.get_result::<NaiveDate>(connection));
481
482 let january_first_2018 = date!(2018 - 1 - 1);
483 let query = select(date("2018-01-01"));
484 assert_eq!(
485 Ok(january_first_2018),
486 query.get_result::<NaiveDate>(connection)
487 );
488
489 let distant_future = date!(9999 - 1 - 8);
490 let query = select(date("9999-01-08"));
491 assert_eq!(
492 Ok(distant_future),
493 query.get_result::<NaiveDate>(connection)
494 );
495 }
496
497 #[test]
498 fn datetimes_decode_correctly() {
499 let connection = &mut connection();
500 let january_first_2000 = datetime!(2000-1-1 1:1:1);
501 let query = select(datetime("2000-01-01 01:01:01.000000"));
502 assert_eq!(
503 Ok(january_first_2000),
504 query.get_result::<PrimitiveDateTime>(connection)
505 );
506
507 let distant_past = datetime!(0-4-11 2:2:2);
508 let query = select(datetime("0000-04-11 02:02:02.000000"));
509 assert_eq!(
510 Ok(distant_past),
511 query.get_result::<PrimitiveDateTime>(connection)
512 );
513
514 let january_first_2018 = date!(2018 - 1 - 1);
515 let query = select(date("2018-01-01"));
516 assert_eq!(
517 Ok(january_first_2018),
518 query.get_result::<NaiveDate>(connection)
519 );
520
521 let distant_future = datetime!(9999 - 1 - 8 23:59:59.0001);
522 let query = select(sql::<Timestamp>("'9999-01-08 23:59:59.000100'"));
523 assert_eq!(
524 Ok(distant_future),
525 query.get_result::<PrimitiveDateTime>(connection)
526 );
527 }
528
529 #[test]
530 fn datetimes_encode_correctly() {
531 let connection = &mut connection();
532 let january_first_2000 = datetime!(2000-1-1 0:0:0);
533 let query = select(datetime("2000-01-01 00:00:00").eq(january_first_2000));
534 assert!(query.get_result::<bool>(connection).unwrap());
535
536 let distant_past = datetime!(0-4-11 20:00:20);
537 let query = select(datetime("0000-04-11 20:00:20").eq(distant_past));
538 assert!(query.get_result::<bool>(connection).unwrap());
539
540 let january_first_2018 = datetime!(2018 - 1 - 1 12:00:00.0005);
541 let query = select(sql::<Timestamp>("'2018-01-01 12:00:00.0005'").eq(january_first_2018));
542 assert!(query.get_result::<bool>(connection).unwrap());
543
544 let distant_future = datetime!(9999-1-8 0:0:0);
545 let query = select(datetime("9999-01-08 00:00:00").eq(distant_future));
546 assert!(query.get_result::<bool>(connection).unwrap());
547 }
548
549 #[test]
550 fn insert_timestamptz_into_table_as_text() {
551 crate::table! {
552 #[allow(unused_parens)]
553 test_insert_timestamptz_into_table_as_text(id) {
554 id -> Integer,
555 timestamp_with_tz -> TimestamptzSqlite,
556 }
557 }
558 let conn = &mut connection();
559 crate::sql_query(
560 "CREATE TABLE test_insert_timestamptz_into_table_as_text(id INTEGER PRIMARY KEY, timestamp_with_tz TEXT);",
561 )
562 .execute(conn)
563 .unwrap();
564
565 let time: OffsetDateTime = datetime!(1970-1-1 0:0:0.0 utc);
566
567 crate::insert_into(test_insert_timestamptz_into_table_as_text::table)
568 .values(vec![(
569 test_insert_timestamptz_into_table_as_text::id.eq(1),
570 test_insert_timestamptz_into_table_as_text::timestamp_with_tz.eq(sql::<
571 TimestamptzSqlite,
572 >(
573 "'1970-01-01 00:00:00.000000+00:00'",
574 )),
575 )])
576 .execute(conn)
577 .unwrap();
578
579 let result = test_insert_timestamptz_into_table_as_text::table
580 .select(test_insert_timestamptz_into_table_as_text::timestamp_with_tz)
581 .get_result::<OffsetDateTime>(conn)
582 .unwrap();
583 assert_eq!(result, time);
584 }
585
586 #[test]
587 fn can_query_timestamptz_column_with_between() {
588 crate::table! {
589 #[allow(unused_parens)]
590 test_query_timestamptz_column_with_between(id) {
591 id -> Integer,
592 timestamp_with_tz -> TimestamptzSqlite,
593 }
594 }
595 let conn = &mut connection();
596 crate::sql_query(
597 "CREATE TABLE test_query_timestamptz_column_with_between(id INTEGER PRIMARY KEY, timestamp_with_tz TEXT);",
598 )
599 .execute(conn)
600 .unwrap();
601
602 crate::insert_into(test_query_timestamptz_column_with_between::table)
603 .values(vec![
604 (
605 test_query_timestamptz_column_with_between::id.eq(1),
606 test_query_timestamptz_column_with_between::timestamp_with_tz.eq(sql::<
607 TimestamptzSqlite,
608 >(
609 "'1970-01-01 00:00:01.000000+00:00'",
610 )),
611 ),
612 (
613 test_query_timestamptz_column_with_between::id.eq(2),
614 test_query_timestamptz_column_with_between::timestamp_with_tz.eq(sql::<
615 TimestamptzSqlite,
616 >(
617 "'1970-01-01 00:00:02.000000+00:00'",
618 )),
619 ),
620 (
621 test_query_timestamptz_column_with_between::id.eq(3),
622 test_query_timestamptz_column_with_between::timestamp_with_tz.eq(sql::<
623 TimestamptzSqlite,
624 >(
625 "'1970-01-01 00:00:03.000000+00:00'",
626 )),
627 ),
628 (
629 test_query_timestamptz_column_with_between::id.eq(4),
630 test_query_timestamptz_column_with_between::timestamp_with_tz.eq(sql::<
631 TimestamptzSqlite,
632 >(
633 "'1970-01-01 00:00:04.000000+00:00'",
634 )),
635 ),
636 ])
637 .execute(conn)
638 .unwrap();
639
640 let result = test_query_timestamptz_column_with_between::table
641 .select(test_query_timestamptz_column_with_between::timestamp_with_tz)
642 .filter(
643 test_query_timestamptz_column_with_between::timestamp_with_tz
644 .gt(datetime!(1970-1-1 0:0:0.0 utc)),
645 )
646 .filter(
647 test_query_timestamptz_column_with_between::timestamp_with_tz
648 .lt(datetime!(1970-1-1 0:0:4.0 utc)),
649 )
650 .count()
651 .get_result::<_>(conn);
652 assert_eq!(result, Ok(3));
653 }
654
655 #[test]
656 fn unix_epoch_encodes_correctly_with_timezone() {
657 let connection = &mut connection();
658 let time = datetime!(1970-1-1 0:00:00.001 -1:00);
660 let query = select(sql::<TimestamptzSqlite>("'1970-01-01 01:00:00.001+00:00'").eq(time));
661 assert!(query.get_result::<bool>(connection).unwrap());
662 }
663
664 #[test]
665 fn unix_epoch_encodes_correctly_with_utc_timezone() {
666 let connection = &mut connection();
667 let time: OffsetDateTime = datetime!(1970-1-1 0:0:0.001 utc);
668 let query = select(sql::<TimestamptzSqlite>("'1970-01-01 00:00:00.001+00:00'").eq(time));
669 assert!(query.get_result::<bool>(connection).unwrap());
670
671 let time: OffsetDateTime = datetime!(1970-1-1 0:0:0 utc);
673 let query = select(sql::<TimestamptzSqlite>("'1970-01-01 00:00:00+00:00'").eq(time));
674 assert!(query.get_result::<bool>(connection).unwrap());
675 }
676
677 #[test]
678 fn unix_epoch_decodes_correctly_with_utc_timezone_in_all_possible_formats() {
679 let connection = &mut connection();
680 let time: OffsetDateTime = datetime!(1970-1-1 0:0:0 utc);
681 let valid_epoch_formats = vec![
682 "1970-01-01 00:00Z",
683 "1970-01-01 00:00:00Z",
684 "1970-01-01 00:00:00.000Z",
685 "1970-01-01 00:00:00.000000Z",
686 "1970-01-01T00:00Z",
687 "1970-01-01T00:00:00Z",
688 "1970-01-01T00:00:00.000Z",
689 "1970-01-01T00:00:00.000000Z",
690 "1970-01-01 00:00+00:00",
691 "1970-01-01 00:00:00+00:00",
692 "1970-01-01 00:00:00.000+00:00",
693 "1970-01-01 00:00:00.000000+00:00",
694 "1970-01-01T00:00+00:00",
695 "1970-01-01T00:00:00+00:00",
696 "1970-01-01T00:00:00.000+00:00",
697 "1970-01-01T00:00:00.000000+00:00",
698 "2440587.5",
699 ];
700
701 for s in valid_epoch_formats {
702 let epoch_from_sql =
703 select(sql::<TimestamptzSqlite>(&format!("'{s}'"))).get_result(connection);
704 assert_eq!(Ok(time), epoch_from_sql, "format {s} failed");
705 }
706 }
707}