diesel/sqlite/types/date_and_time/
mod.rs1use crate::deserialize::{self, FromSql};
2use crate::serialize::{self, Output, ToSql};
3use crate::sql_types;
4use crate::sqlite::Sqlite;
5use crate::sqlite::connection::SqliteValue;
6use alloc::string::String;
7
8#[cfg(feature = "chrono")]
9mod chrono;
10#[cfg(feature = "time")]
11mod time;
12
13#[cfg(feature = "__sqlite-shared")]
14impl FromSql<sql_types::Date, Sqlite> for String {
15 fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result<Self> {
16 FromSql::<sql_types::Text, Sqlite>::from_sql(value)
17 }
18}
19
20#[cfg(feature = "__sqlite-shared")]
21impl ToSql<sql_types::Date, Sqlite> for str {
22 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
23 ToSql::<sql_types::Text, Sqlite>::to_sql(self, out)
24 }
25}
26
27#[cfg(feature = "__sqlite-shared")]
28impl ToSql<sql_types::Date, Sqlite> for String {
29 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
30 <str as ToSql<sql_types::Date, Sqlite>>::to_sql(self as &str, out)
31 }
32}
33
34#[cfg(feature = "__sqlite-shared")]
35impl FromSql<sql_types::Time, Sqlite> for String {
36 fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result<Self> {
37 FromSql::<sql_types::Text, Sqlite>::from_sql(value)
38 }
39}
40
41#[cfg(feature = "__sqlite-shared")]
42impl ToSql<sql_types::Time, Sqlite> for str {
43 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
44 ToSql::<sql_types::Text, Sqlite>::to_sql(self, out)
45 }
46}
47
48#[cfg(feature = "__sqlite-shared")]
49impl ToSql<sql_types::Time, Sqlite> for String {
50 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
51 <str as ToSql<sql_types::Time, Sqlite>>::to_sql(self as &str, out)
52 }
53}
54
55#[cfg(feature = "__sqlite-shared")]
56impl FromSql<sql_types::Timestamp, Sqlite> for String {
57 fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result<Self> {
58 FromSql::<sql_types::Text, Sqlite>::from_sql(value)
59 }
60}
61
62#[cfg(feature = "__sqlite-shared")]
63impl ToSql<sql_types::Timestamp, Sqlite> for str {
64 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
65 ToSql::<sql_types::Text, Sqlite>::to_sql(self, out)
66 }
67}
68
69#[cfg(feature = "__sqlite-shared")]
70impl ToSql<sql_types::Timestamp, Sqlite> for String {
71 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
72 <str as ToSql<sql_types::Timestamp, Sqlite>>::to_sql(self as &str, out)
73 }
74}
75
76#[cfg(feature = "__sqlite-shared")]
77impl FromSql<sql_types::TimestamptzSqlite, Sqlite> for String {
78 fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result<Self> {
79 FromSql::<sql_types::Text, Sqlite>::from_sql(value)
80 }
81}
82
83#[cfg(feature = "__sqlite-shared")]
84impl ToSql<sql_types::TimestamptzSqlite, Sqlite> for str {
85 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
86 ToSql::<sql_types::Text, Sqlite>::to_sql(self, out)
87 }
88}
89
90#[cfg(feature = "__sqlite-shared")]
91impl ToSql<sql_types::TimestamptzSqlite, Sqlite> for String {
92 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
93 <str as ToSql<sql_types::TimestamptzSqlite, Sqlite>>::to_sql(self as &str, out)
94 }
95}
96
97#[cfg(all(
98 test,
99 feature = "chrono",
100 feature = "time",
101 not(all(target_family = "wasm", target_os = "unknown"))
102))]
103#[allow(clippy::cast_possible_truncation)] mod tests {
105 extern crate chrono;
106 extern crate time;
107
108 use chrono::{
109 DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc,
110 };
111 use time::{
112 Date, OffsetDateTime, PrimitiveDateTime, Time,
113 macros::{date, datetime, offset, time},
114 };
115
116 use crate::insert_into;
117 use crate::prelude::*;
118 use crate::test_helpers::connection;
119
120 crate::table! {
121 table_timestamp_tz(id) {
122 id -> Integer,
123 timestamp_with_tz -> TimestamptzSqlite,
124 }
125 }
126 crate::table! {
127 table_timestamp(id) {
128 id -> Integer,
129 timestamp -> Timestamp
130 }
131 }
132 crate::table! {
133 table_date(id) {
134 id -> Integer,
135 date -> Date
136 }
137 }
138 crate::table! {
139 table_time(id) {
140 id -> Integer,
141 time -> Time
142 }
143 }
144
145 fn eq_date(left: Date, right: NaiveDate) -> bool {
146 left.year() == right.year()
147 && left.month() as u8 == right.month() as u8
148 && left.day() == right.day() as u8
149 }
150
151 fn eq_time(left: Time, right: NaiveTime) -> bool {
152 left.hour() == right.hour() as u8
153 && left.minute() == right.minute() as u8
154 && left.second() == right.second() as u8
155 && left.nanosecond() == right.nanosecond()
156 }
157
158 fn eq_datetime(left: PrimitiveDateTime, right: NaiveDateTime) -> bool {
159 eq_date(left.date(), right.date()) && eq_time(left.time(), right.time())
160 }
161
162 fn eq_datetime_utc(left: OffsetDateTime, right: DateTime<Utc>) -> bool {
163 left.unix_timestamp_nanos() == right.timestamp_nanos_opt().unwrap() as i128
164 }
165
166 fn eq_datetime_offset(left: OffsetDateTime, right: DateTime<FixedOffset>) -> bool {
167 left.unix_timestamp_nanos() == right.timestamp_nanos_opt().unwrap() as i128
168 }
169
170 fn create_tables(conn: &mut SqliteConnection) {
171 crate::sql_query(
172 "CREATE TABLE table_timestamp_tz(id INTEGER PRIMARY KEY, timestamp_with_tz TEXT);",
173 )
174 .execute(conn)
175 .unwrap();
176
177 crate::sql_query("CREATE TABLE table_timestamp(id INTEGER PRIMARY KEY, timestamp TEXT);")
178 .execute(conn)
179 .unwrap();
180
181 crate::sql_query("CREATE TABLE table_date(id INTEGER PRIMARY KEY, date TEXT);")
182 .execute(conn)
183 .unwrap();
184
185 crate::sql_query("CREATE TABLE table_time(id INTEGER PRIMARY KEY, time TEXT);")
186 .execute(conn)
187 .unwrap();
188 }
189
190 #[diesel_test_helper::test]
191 fn time_to_chrono_date() {
192 let conn = &mut connection();
193 create_tables(conn);
194
195 let original = date!(2000 - 1 - 1);
196
197 insert_into(table_date::table)
198 .values(vec![(table_date::id.eq(1), table_date::date.eq(original))])
199 .execute(conn)
200 .unwrap();
201
202 let translated = table_date::table
203 .select(table_date::date)
204 .get_result::<NaiveDate>(conn)
205 .unwrap();
206
207 assert!(eq_date(original, translated))
208 }
209
210 #[diesel_test_helper::test]
211 fn chrono_to_time_date() {
212 let conn = &mut connection();
213 create_tables(conn);
214
215 let original = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
216
217 insert_into(table_date::table)
218 .values(vec![(table_date::id.eq(1), table_date::date.eq(original))])
219 .execute(conn)
220 .unwrap();
221
222 let translated = table_date::table
223 .select(table_date::date)
224 .get_result::<Date>(conn)
225 .unwrap();
226
227 assert!(eq_date(translated, original))
228 }
229
230 #[diesel_test_helper::test]
231 fn time_to_chrono_time() {
232 let conn = &mut connection();
233 create_tables(conn);
234
235 let original = time!(1:1:1.001);
236
237 insert_into(table_time::table)
238 .values(vec![(table_time::id.eq(1), table_time::time.eq(original))])
239 .execute(conn)
240 .unwrap();
241
242 let translated = table_time::table
243 .select(table_time::time)
244 .get_result::<NaiveTime>(conn)
245 .unwrap();
246
247 assert!(eq_time(original, translated))
248 }
249
250 #[diesel_test_helper::test]
251 fn chrono_to_time_time() {
252 let conn = &mut connection();
253 create_tables(conn);
254
255 let original = NaiveTime::from_hms_milli_opt(1, 1, 1, 1).unwrap();
256
257 insert_into(table_time::table)
258 .values(vec![(table_time::id.eq(1), table_time::time.eq(original))])
259 .execute(conn)
260 .unwrap();
261
262 let translated = table_time::table
263 .select(table_time::time)
264 .get_result::<Time>(conn)
265 .unwrap();
266
267 assert!(eq_time(translated, original))
268 }
269
270 #[diesel_test_helper::test]
271 fn time_to_chrono_datetime() {
272 let conn = &mut connection();
273 create_tables(conn);
274
275 let original = datetime!(2000-1-1 1:1:1.001);
276
277 insert_into(table_timestamp::table)
278 .values(vec![(
279 table_timestamp::id.eq(1),
280 table_timestamp::timestamp.eq(original),
281 )])
282 .execute(conn)
283 .unwrap();
284
285 let translated = table_timestamp::table
286 .select(table_timestamp::timestamp)
287 .get_result::<NaiveDateTime>(conn)
288 .unwrap();
289
290 assert!(eq_datetime(original, translated))
291 }
292
293 #[diesel_test_helper::test]
294 fn chrono_to_time_datetime() {
295 let conn = &mut connection();
296 create_tables(conn);
297
298 let original = NaiveDate::from_ymd_opt(2000, 1, 1)
299 .unwrap()
300 .and_hms_milli_opt(1, 1, 1, 1)
301 .unwrap();
302
303 insert_into(table_timestamp::table)
304 .values(vec![(
305 table_timestamp::id.eq(1),
306 table_timestamp::timestamp.eq(original),
307 )])
308 .execute(conn)
309 .unwrap();
310
311 let translated = table_timestamp::table
312 .select(table_timestamp::timestamp)
313 .get_result::<PrimitiveDateTime>(conn)
314 .unwrap();
315
316 assert!(eq_datetime(translated, original))
317 }
318
319 #[diesel_test_helper::test]
320 fn chrono_to_time_datetime_utc() {
321 let conn = &mut connection();
322 create_tables(conn);
323
324 let original = Utc::now();
325
326 insert_into(table_timestamp_tz::table)
327 .values(vec![(
328 table_timestamp_tz::id.eq(1),
329 table_timestamp_tz::timestamp_with_tz.eq(original),
330 )])
331 .execute(conn)
332 .unwrap();
333
334 let translated = table_timestamp_tz::table
335 .select(table_timestamp_tz::timestamp_with_tz)
336 .get_result::<OffsetDateTime>(conn)
337 .unwrap();
338
339 assert!(eq_datetime_utc(translated, original))
340 }
341
342 #[diesel_test_helper::test]
343 fn time_to_chrono_datetime_utc() {
344 let conn = &mut connection();
345 create_tables(conn);
346
347 let original = OffsetDateTime::now_utc();
348
349 insert_into(table_timestamp_tz::table)
350 .values(vec![(
351 table_timestamp_tz::id.eq(1),
352 table_timestamp_tz::timestamp_with_tz.eq(original),
353 )])
354 .execute(conn)
355 .unwrap();
356
357 let translated = table_timestamp_tz::table
358 .select(table_timestamp_tz::timestamp_with_tz)
359 .get_result::<DateTime<Utc>>(conn)
360 .unwrap();
361
362 assert!(eq_datetime_utc(original, translated))
363 }
364
365 #[diesel_test_helper::test]
366 fn chrono_to_time_datetime_timezone() {
367 let conn = &mut connection();
368 create_tables(conn);
369
370 let original = Utc::now().with_timezone(&FixedOffset::east_opt(5 * 3600).unwrap());
371
372 insert_into(table_timestamp_tz::table)
373 .values(vec![(
374 table_timestamp_tz::id.eq(1),
375 table_timestamp_tz::timestamp_with_tz.eq(original),
376 )])
377 .execute(conn)
378 .unwrap();
379
380 let translated = table_timestamp_tz::table
381 .select(table_timestamp_tz::timestamp_with_tz)
382 .get_result::<OffsetDateTime>(conn)
383 .unwrap();
384
385 assert!(eq_datetime_offset(translated, original))
386 }
387
388 #[diesel_test_helper::test]
389 fn time_to_chrono_datetime_offset() {
390 let conn = &mut connection();
391 create_tables(conn);
392
393 let original = OffsetDateTime::now_utc().to_offset(offset!(+5));
394
395 insert_into(table_timestamp_tz::table)
396 .values(vec![(
397 table_timestamp_tz::id.eq(1),
398 table_timestamp_tz::timestamp_with_tz.eq(original),
399 )])
400 .execute(conn)
401 .unwrap();
402
403 let translated = table_timestamp_tz::table
404 .select(table_timestamp_tz::timestamp_with_tz)
405 .get_result::<DateTime<Utc>>(conn)
406 .unwrap();
407
408 assert!(eq_datetime_utc(original, translated))
409 }
410}