1use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
16use crate::{Error, Result};
17use time::format_description::FormatItem;
18use time::macros::format_description;
19use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
20
21const OFFSET_DATE_TIME_ENCODING: &[FormatItem<'_>] = format_description!(
22 version = 2,
23 "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute]"
24);
25const PRIMITIVE_DATE_TIME_ENCODING: &[FormatItem<'_>] = format_description!(
26 version = 2,
27 "[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"
28);
29const TIME_ENCODING: &[FormatItem<'_>] =
30 format_description!(version = 2, "[hour]:[minute]:[second].[subsecond]");
31
32const DATE_FORMAT: &[FormatItem<'_>] = format_description!(version = 2, "[year]-[month]-[day]");
33const TIME_FORMAT: &[FormatItem<'_>] = format_description!(
34 version = 2,
35 "[hour]:[minute][optional [:[second][optional [.[subsecond]]]]]"
36);
37const PRIMITIVE_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
38 version = 2,
39 "[year]-[month]-[day][first [ ][T]][hour]:[minute][optional [:[second][optional [.[subsecond]]]]]"
40);
41const UTC_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
42 version = 2,
43 "[year]-[month]-[day][first [ ][T]][hour]:[minute][optional [:[second][optional [.[subsecond]]]]][optional [Z]]"
44);
45const OFFSET_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
46 version = 2,
47 "[year]-[month]-[day][first [ ][T]][hour]:[minute][optional [:[second][optional [.[subsecond]]]]][offset_hour sign:mandatory]:[offset_minute]"
48);
49const LEGACY_DATE_TIME_FORMAT: &[FormatItem<'_>] = format_description!(
50 version = 2,
51 "[year]-[month]-[day] [hour]:[minute]:[second]:[subsecond] [offset_hour sign:mandatory]:[offset_minute]"
52);
53
54impl ToSql for OffsetDateTime {
56 #[inline]
57 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
58 let time_string = self
59 .format(&OFFSET_DATE_TIME_ENCODING)
60 .map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
61 Ok(ToSqlOutput::from(time_string))
62 }
63}
64
65impl FromSql for OffsetDateTime {
68 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
69 value.as_str().and_then(|s| {
70 if let Some(b' ') = s.as_bytes().get(23) {
71 return Self::parse(s, &LEGACY_DATE_TIME_FORMAT).map_err(FromSqlError::other);
73 }
74 if s[8..].contains('+') || s[8..].contains('-') {
75 return Self::parse(s, &OFFSET_DATE_TIME_FORMAT).map_err(FromSqlError::other);
77 }
78 PrimitiveDateTime::parse(s, &UTC_DATE_TIME_FORMAT)
80 .map(|p| p.assume_utc())
81 .map_err(FromSqlError::other)
82 })
83 }
84}
85
86impl ToSql for Date {
88 #[inline]
89 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
90 let date_str = self
91 .format(&DATE_FORMAT)
92 .map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
93 Ok(ToSqlOutput::from(date_str))
94 }
95}
96
97impl FromSql for Date {
99 #[inline]
100 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
101 value.as_str().and_then(|s| {
102 Self::parse(s, &DATE_FORMAT).map_err(|err| FromSqlError::Other(err.into()))
103 })
104 }
105}
106
107impl ToSql for Time {
109 #[inline]
110 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
111 let time_str = self
112 .format(&TIME_ENCODING)
113 .map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
114 Ok(ToSqlOutput::from(time_str))
115 }
116}
117
118impl FromSql for Time {
120 #[inline]
121 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
122 value.as_str().and_then(|s| {
123 Self::parse(s, &TIME_FORMAT).map_err(|err| FromSqlError::Other(err.into()))
124 })
125 }
126}
127
128impl ToSql for PrimitiveDateTime {
130 #[inline]
131 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
132 let date_time_str = self
133 .format(&PRIMITIVE_DATE_TIME_ENCODING)
134 .map_err(|err| Error::ToSqlConversionFailure(err.into()))?;
135 Ok(ToSqlOutput::from(date_time_str))
136 }
137}
138
139impl FromSql for PrimitiveDateTime {
147 #[inline]
148 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
149 value.as_str().and_then(|s| {
150 Self::parse(s, &PRIMITIVE_DATE_TIME_FORMAT)
151 .map_err(|err| FromSqlError::Other(err.into()))
152 })
153 }
154}
155
156#[cfg(test)]
157mod test {
158 use crate::{Connection, Result};
159 use time::macros::{date, datetime, time};
160 use time::{Date, OffsetDateTime, PrimitiveDateTime, Time};
161
162 fn checked_memory_handle() -> Result<Connection> {
163 let db = Connection::open_in_memory()?;
164 db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB)")?;
165 Ok(db)
166 }
167
168 #[test]
169 fn test_offset_date_time() -> Result<()> {
170 let db = checked_memory_handle()?;
171
172 let mut ts_vec = vec![];
173
174 let make_datetime = |secs: i128, nanos: i128| {
175 OffsetDateTime::from_unix_timestamp_nanos(1_000_000_000 * secs + nanos).unwrap()
176 };
177
178 ts_vec.push(make_datetime(10_000, 0)); ts_vec.push(make_datetime(10_000, 1000)); ts_vec.push(make_datetime(1_500_391_124, 1_000_000)); ts_vec.push(make_datetime(2_000_000_000, 2_000_000)); ts_vec.push(make_datetime(3_000_000_000, 999_999_999)); ts_vec.push(make_datetime(10_000_000_000, 0)); for ts in ts_vec {
186 db.execute("INSERT INTO foo(t) VALUES (?1)", [ts])?;
187
188 let from: OffsetDateTime = db.one_column("SELECT t FROM foo", [])?;
189
190 db.execute("DELETE FROM foo", [])?;
191
192 assert_eq!(from, ts);
193 }
194 Ok(())
195 }
196
197 #[test]
198 fn test_offset_date_time_parsing() -> Result<()> {
199 let db = checked_memory_handle()?;
200 let tests = vec![
201 (
203 "2013-10-07T08:23:19.123456789Z",
204 datetime!(2013-10-07 8:23:19.123456789 UTC),
205 ),
206 (
207 "2013-10-07 08:23:19.123456789Z",
208 datetime!(2013-10-07 8:23:19.123456789 UTC),
209 ),
210 ("2013-10-07 08:23", datetime!(2013-10-07 8:23 UTC)),
212 ("2013-10-07 08:23Z", datetime!(2013-10-07 8:23 UTC)),
213 ("2013-10-07 08:23+04:00", datetime!(2013-10-07 8:23 +4)),
214 ("2013-10-07 08:23:19", datetime!(2013-10-07 8:23:19 UTC)),
216 ("2013-10-07 08:23:19Z", datetime!(2013-10-07 8:23:19 UTC)),
217 (
218 "2013-10-07 08:23:19+04:00",
219 datetime!(2013-10-07 8:23:19 +4),
220 ),
221 (
223 "2013-10-07 08:23:19.123",
224 datetime!(2013-10-07 8:23:19.123 UTC),
225 ),
226 (
227 "2013-10-07 08:23:19.123Z",
228 datetime!(2013-10-07 8:23:19.123 UTC),
229 ),
230 (
231 "2013-10-07 08:23:19.123+04:00",
232 datetime!(2013-10-07 8:23:19.123 +4),
233 ),
234 ("2013-10-07T08:23", datetime!(2013-10-07 8:23 UTC)),
236 ("2013-10-07T08:23Z", datetime!(2013-10-07 8:23 UTC)),
237 ("2013-10-07T08:23+04:00", datetime!(2013-10-07 8:23 +4)),
238 ("2013-10-07T08:23:19", datetime!(2013-10-07 8:23:19 UTC)),
240 ("2013-10-07T08:23:19Z", datetime!(2013-10-07 8:23:19 UTC)),
241 (
242 "2013-10-07T08:23:19+04:00",
243 datetime!(2013-10-07 8:23:19 +4),
244 ),
245 (
247 "2013-10-07T08:23:19.123",
248 datetime!(2013-10-07 8:23:19.123 UTC),
249 ),
250 (
251 "2013-10-07T08:23:19.123Z",
252 datetime!(2013-10-07 8:23:19.123 UTC),
253 ),
254 (
255 "2013-10-07T08:23:19.123+04:00",
256 datetime!(2013-10-07 8:23:19.123 +4),
257 ),
258 (
260 "2013-10-07 08:23:12:987 -07:00",
261 datetime!(2013-10-07 8:23:12.987 -7),
262 ),
263 ];
264
265 for (s, t) in tests {
266 let result: OffsetDateTime = db.one_column("SELECT ?1", [s])?;
267 assert_eq!(result, t);
268 }
269 Ok(())
270 }
271
272 #[test]
273 fn test_date() -> Result<()> {
274 let db = checked_memory_handle()?;
275 let date = date!(2016 - 02 - 23);
276 db.execute("INSERT INTO foo (t) VALUES (?1)", [date])?;
277
278 let s: String = db.one_column("SELECT t FROM foo", [])?;
279 assert_eq!("2016-02-23", s);
280 let t: Date = db.one_column("SELECT t FROM foo", [])?;
281 assert_eq!(date, t);
282 Ok(())
283 }
284
285 #[test]
286 fn test_time() -> Result<()> {
287 let db = checked_memory_handle()?;
288 let time = time!(23:56:04.00001);
289 db.execute("INSERT INTO foo (t) VALUES (?1)", [time])?;
290
291 let s: String = db.one_column("SELECT t FROM foo", [])?;
292 assert_eq!("23:56:04.00001", s);
293 let v: Time = db.one_column("SELECT t FROM foo", [])?;
294 assert_eq!(time, v);
295 Ok(())
296 }
297
298 #[test]
299 fn test_primitive_date_time() -> Result<()> {
300 let db = checked_memory_handle()?;
301 let dt = date!(2016 - 02 - 23).with_time(time!(23:56:04));
302
303 db.execute("INSERT INTO foo (t) VALUES (?1)", [dt])?;
304
305 let s: String = db.one_column("SELECT t FROM foo", [])?;
306 assert_eq!("2016-02-23 23:56:04.0", s);
307 let v: PrimitiveDateTime = db.one_column("SELECT t FROM foo", [])?;
308 assert_eq!(dt, v);
309
310 db.execute("UPDATE foo set b = datetime(t)", [])?; let hms: PrimitiveDateTime = db.one_column("SELECT b FROM foo", [])?;
312 assert_eq!(dt, hms);
313 Ok(())
314 }
315
316 #[test]
317 fn test_date_parsing() -> Result<()> {
318 let db = checked_memory_handle()?;
319 let result: Date = db.one_column("SELECT ?1", ["2013-10-07"])?;
320 assert_eq!(result, date!(2013 - 10 - 07));
321 Ok(())
322 }
323
324 #[test]
325 fn test_time_parsing() -> Result<()> {
326 let db = checked_memory_handle()?;
327 let tests = vec![
328 ("08:23", time!(08:23)),
329 ("08:23:19", time!(08:23:19)),
330 ("08:23:19.111", time!(08:23:19.111)),
331 ];
332
333 for (s, t) in tests {
334 let result: Time = db.one_column("SELECT ?1", [s])?;
335 assert_eq!(result, t);
336 }
337 Ok(())
338 }
339
340 #[test]
341 fn test_primitive_date_time_parsing() -> Result<()> {
342 let db = checked_memory_handle()?;
343
344 let tests = vec![
345 ("2013-10-07T08:23", datetime!(2013-10-07 8:23)),
346 ("2013-10-07T08:23:19", datetime!(2013-10-07 8:23:19)),
347 ("2013-10-07T08:23:19.111", datetime!(2013-10-07 8:23:19.111)),
348 ("2013-10-07 08:23", datetime!(2013-10-07 8:23)),
349 ("2013-10-07 08:23:19", datetime!(2013-10-07 8:23:19)),
350 ("2013-10-07 08:23:19.111", datetime!(2013-10-07 8:23:19.111)),
351 ];
352
353 for (s, t) in tests {
354 let result: PrimitiveDateTime = db.one_column("SELECT ?1", [s])?;
355 assert_eq!(result, t);
356 }
357 Ok(())
358 }
359
360 #[test]
361 fn test_sqlite_functions() -> Result<()> {
362 let db = checked_memory_handle()?;
363 db.one_column::<Time, _>("SELECT CURRENT_TIME", [])?;
364 db.one_column::<Date, _>("SELECT CURRENT_DATE", [])?;
365 db.one_column::<PrimitiveDateTime, _>("SELECT CURRENT_TIMESTAMP", [])?;
366 db.one_column::<OffsetDateTime, _>("SELECT CURRENT_TIMESTAMP", [])?;
367 Ok(())
368 }
369
370 #[test]
371 fn test_time_param() -> Result<()> {
372 let db = checked_memory_handle()?;
373 let now = OffsetDateTime::now_utc().time();
374 let result: Result<bool> = db.one_column(
375 "SELECT 1 WHERE ?1 BETWEEN time('now', '-1 minute') AND time('now', '+1 minute')",
376 [now],
377 );
378 result?;
379 Ok(())
380 }
381
382 #[test]
383 fn test_date_param() -> Result<()> {
384 let db = checked_memory_handle()?;
385 let now = OffsetDateTime::now_utc().date();
386 let result: Result<bool> = db.one_column(
387 "SELECT 1 WHERE ?1 BETWEEN date('now', '-1 day') AND date('now', '+1 day')",
388 [now],
389 );
390 result?;
391 Ok(())
392 }
393
394 #[test]
395 fn test_primitive_date_time_param() -> Result<()> {
396 let db = checked_memory_handle()?;
397 let now = PrimitiveDateTime::new(
398 OffsetDateTime::now_utc().date(),
399 OffsetDateTime::now_utc().time(),
400 );
401 let result: Result<bool> = db.one_column(
402 "SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')",
403 [now],
404 );
405 result?;
406 Ok(())
407 }
408
409 #[test]
410 fn test_offset_date_time_param() -> Result<()> {
411 let db = checked_memory_handle()?;
412 let result: Result<bool> = db.one_column(
413 "SELECT 1 WHERE ?1 BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute')",
414 [OffsetDateTime::now_utc()],
415 );
416 result?;
417 Ok(())
418 }
419}