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