2016-02-22 20:36:49 +01:00
//! Convert most of the [Time Strings](http://sqlite.org/lang_datefunc.html) to chrono types.
2019-08-17 08:18:37 +02:00
use chrono ::{ DateTime , Local , NaiveDate , NaiveDateTime , NaiveTime , TimeZone , Utc } ;
2016-02-22 20:36:49 +01:00
2018-10-30 20:11:35 +01:00
use crate ::types ::{ FromSql , FromSqlError , FromSqlResult , ToSql , ToSqlOutput , ValueRef } ;
use crate ::Result ;
2016-02-22 20:36:49 +01:00
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
impl ToSql for NaiveDate {
2020-11-03 19:10:23 -08:00
#[ inline ]
2018-12-07 21:57:04 +01:00
fn to_sql ( & self ) -> Result < ToSqlOutput < '_ > > {
2021-01-29 22:03:50 +01:00
let date_str = self . format ( " %F " ) . to_string ( ) ;
2016-05-25 22:57:43 -04:00
Ok ( ToSqlOutput ::from ( date_str ) )
2016-02-22 20:36:49 +01:00
}
}
2016-05-15 19:56:57 -05:00
/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone.
2016-02-22 20:36:49 +01:00
impl FromSql for NaiveDate {
2020-11-03 19:10:23 -08:00
#[ inline ]
2018-12-07 21:57:04 +01:00
fn column_result ( value : ValueRef < '_ > ) -> FromSqlResult < Self > {
2017-04-07 19:43:24 +02:00
value
. as_str ( )
2021-01-29 22:03:50 +01:00
. and_then ( | s | match NaiveDate ::parse_from_str ( s , " %F " ) {
2018-08-11 12:48:21 +02:00
Ok ( dt ) = > Ok ( dt ) ,
Err ( err ) = > Err ( FromSqlError ::Other ( Box ::new ( err ) ) ) ,
} )
2016-05-15 19:56:57 -05:00
}
2016-02-22 20:36:49 +01:00
}
/// ISO 8601 time without timezone => "HH:MM:SS.SSS"
impl ToSql for NaiveTime {
2020-11-03 19:10:23 -08:00
#[ inline ]
2018-12-07 21:57:04 +01:00
fn to_sql ( & self ) -> Result < ToSqlOutput < '_ > > {
2021-01-29 22:03:50 +01:00
let date_str = self . format ( " %T%.f " ) . to_string ( ) ;
2016-05-25 22:57:43 -04:00
Ok ( ToSqlOutput ::from ( date_str ) )
2016-02-22 20:36:49 +01:00
}
}
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
impl FromSql for NaiveTime {
2018-12-07 21:57:04 +01:00
fn column_result ( value : ValueRef < '_ > ) -> FromSqlResult < Self > {
2018-08-11 12:48:21 +02:00
value . as_str ( ) . and_then ( | s | {
let fmt = match s . len ( ) {
5 = > " %H:%M " ,
2021-01-29 22:03:50 +01:00
8 = > " %T " ,
_ = > " %T%.f " ,
2018-08-11 12:48:21 +02:00
} ;
match NaiveTime ::parse_from_str ( s , fmt ) {
Ok ( dt ) = > Ok ( dt ) ,
Err ( err ) = > Err ( FromSqlError ::Other ( Box ::new ( err ) ) ) ,
}
} )
2016-02-22 20:36:49 +01:00
}
}
2018-08-16 18:29:46 +02:00
/// ISO 8601 combined date and time without timezone =>
2021-01-29 22:03:50 +01:00
/// "YYYY-MM-DD HH:MM:SS.SSS"
2016-02-22 20:36:49 +01:00
impl ToSql for NaiveDateTime {
2020-11-03 19:10:23 -08:00
#[ inline ]
2018-12-07 21:57:04 +01:00
fn to_sql ( & self ) -> Result < ToSqlOutput < '_ > > {
2021-01-29 22:03:50 +01:00
let date_str = self . format ( " %F %T%.f " ) . to_string ( ) ;
2016-05-25 22:57:43 -04:00
Ok ( ToSqlOutput ::from ( date_str ) )
2016-02-22 20:36:49 +01:00
}
}
2018-08-16 18:29:46 +02:00
/// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date
/// and time without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS"
/// also supported)
2016-02-22 20:36:49 +01:00
impl FromSql for NaiveDateTime {
2018-12-07 21:57:04 +01:00
fn column_result ( value : ValueRef < '_ > ) -> FromSqlResult < Self > {
2018-08-11 12:48:21 +02:00
value . as_str ( ) . and_then ( | s | {
let fmt = if s . len ( ) > = 11 & & s . as_bytes ( ) [ 10 ] = = b 'T' {
2021-01-29 22:03:50 +01:00
" %FT%T%.f "
2018-08-11 12:48:21 +02:00
} else {
2021-01-29 22:03:50 +01:00
" %F %T%.f "
2018-08-11 12:48:21 +02:00
} ;
match NaiveDateTime ::parse_from_str ( s , fmt ) {
Ok ( dt ) = > Ok ( dt ) ,
Err ( err ) = > Err ( FromSqlError ::Other ( Box ::new ( err ) ) ) ,
}
} )
2016-05-15 22:23:02 -05:00
}
2016-02-22 20:36:49 +01:00
}
2018-08-16 18:29:46 +02:00
/// Date and time with time zone => UTC RFC3339 timestamp
2021-01-29 22:03:50 +01:00
/// ("YYYY-MM-DD HH:MM:SS.SSS+00:00").
2016-05-16 09:08:31 -05:00
impl < Tz : TimeZone > ToSql for DateTime < Tz > {
2020-11-03 19:10:23 -08:00
#[ inline ]
2018-12-07 21:57:04 +01:00
fn to_sql ( & self ) -> Result < ToSqlOutput < '_ > > {
2021-01-29 22:03:50 +01:00
let date_str = self . with_timezone ( & Utc ) . format ( " %F %T%.f%:z " ) . to_string ( ) ;
Ok ( ToSqlOutput ::from ( date_str ) )
2016-02-22 20:36:49 +01:00
}
}
2021-01-29 22:03:50 +01:00
/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") into `DateTime<Utc>`.
2017-10-09 22:55:55 +03:00
impl FromSql for DateTime < Utc > {
2018-12-07 21:57:04 +01:00
fn column_result ( value : ValueRef < '_ > ) -> FromSqlResult < Self > {
2016-05-23 21:49:54 -04:00
{
// Try to parse value as rfc3339 first.
2018-10-30 20:11:35 +01:00
let s = value . as_str ( ) ? ;
2016-05-23 21:49:54 -04:00
2021-01-29 22:03:50 +01:00
let fmt = if s . len ( ) > = 11 & & s . as_bytes ( ) [ 10 ] = = b 'T' {
" %FT%T%.f%:z "
2016-05-23 21:49:54 -04:00
} else {
2021-01-29 22:03:50 +01:00
" %F %T%.f%:z "
2016-05-23 21:49:54 -04:00
} ;
2021-01-29 22:03:50 +01:00
if let Ok ( dt ) = DateTime ::parse_from_str ( s , fmt ) {
2017-10-09 22:55:55 +03:00
return Ok ( dt . with_timezone ( & Utc ) ) ;
2016-05-16 09:27:50 -05:00
}
2016-02-22 20:36:49 +01:00
}
2016-05-23 21:49:54 -04:00
// Couldn't parse as rfc3339 - fall back to NaiveDateTime.
2017-10-09 22:55:55 +03:00
NaiveDateTime ::column_result ( value ) . map ( | dt | Utc . from_utc_datetime ( & dt ) )
2016-02-22 20:36:49 +01:00
}
}
2021-01-29 22:03:50 +01:00
/// RFC3339 ("YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM") into `DateTime<Local>`.
2016-02-22 20:36:49 +01:00
impl FromSql for DateTime < Local > {
2020-11-03 19:10:23 -08:00
#[ inline ]
2018-12-07 21:57:04 +01:00
fn column_result ( value : ValueRef < '_ > ) -> FromSqlResult < Self > {
2018-10-30 20:11:35 +01:00
let utc_dt = DateTime ::< Utc > ::column_result ( value ) ? ;
2016-05-15 22:23:02 -05:00
Ok ( utc_dt . with_timezone ( & Local ) )
2016-02-22 20:36:49 +01:00
}
2016-05-15 22:23:02 -05:00
}
2016-02-23 18:18:56 +01:00
#[ cfg(test) ]
mod test {
2020-11-03 01:32:46 -08:00
use crate ::{ Connection , Result } ;
2019-08-17 08:18:37 +02:00
use chrono ::{ DateTime , Duration , Local , NaiveDate , NaiveDateTime , NaiveTime , TimeZone , Utc } ;
2016-02-23 18:18:56 +01:00
2020-11-05 22:14:00 +01:00
fn checked_memory_handle ( ) -> Result < Connection > {
let db = Connection ::open_in_memory ( ) ? ;
db . execute_batch ( " CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB) " ) ? ;
Ok ( db )
2016-02-23 18:18:56 +01:00
}
#[ test ]
2020-11-05 22:14:00 +01:00
fn test_naive_date ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2016-04-02 13:57:55 +02:00
let date = NaiveDate ::from_ymd ( 2016 , 2 , 23 ) ;
2021-01-19 21:16:08 +01:00
db . execute ( " INSERT INTO foo (t) VALUES (?) " , [ date ] ) ? ;
2016-02-23 18:18:56 +01:00
2020-11-05 22:14:00 +01:00
let s : String = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-02-23 18:18:56 +01:00
assert_eq! ( " 2016-02-23 " , s ) ;
2020-11-05 22:14:00 +01:00
let t : NaiveDate = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-04-02 13:57:55 +02:00
assert_eq! ( date , t ) ;
2020-11-05 22:14:00 +01:00
Ok ( ( ) )
2016-02-23 18:18:56 +01:00
}
#[ test ]
2020-11-05 22:14:00 +01:00
fn test_naive_time ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2016-04-02 13:57:55 +02:00
let time = NaiveTime ::from_hms ( 23 , 56 , 4 ) ;
2021-01-19 21:16:08 +01:00
db . execute ( " INSERT INTO foo (t) VALUES (?) " , [ time ] ) ? ;
2016-02-23 18:18:56 +01:00
2020-11-05 22:14:00 +01:00
let s : String = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-02-25 18:44:53 +01:00
assert_eq! ( " 23:56:04 " , s ) ;
2020-11-05 22:14:00 +01:00
let v : NaiveTime = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-04-02 13:57:55 +02:00
assert_eq! ( time , v ) ;
2020-11-05 22:14:00 +01:00
Ok ( ( ) )
2016-02-23 18:18:56 +01:00
}
#[ test ]
2020-11-05 22:14:00 +01:00
fn test_naive_date_time ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2016-04-02 13:57:55 +02:00
let date = NaiveDate ::from_ymd ( 2016 , 2 , 23 ) ;
let time = NaiveTime ::from_hms ( 23 , 56 , 4 ) ;
let dt = NaiveDateTime ::new ( date , time ) ;
2016-02-23 18:18:56 +01:00
2021-01-19 21:16:08 +01:00
db . execute ( " INSERT INTO foo (t) VALUES (?) " , [ dt ] ) ? ;
2016-02-23 18:18:56 +01:00
2020-11-05 22:14:00 +01:00
let s : String = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2021-01-29 22:03:50 +01:00
assert_eq! ( " 2016-02-23 23:56:04 " , s ) ;
2020-11-05 22:14:00 +01:00
let v : NaiveDateTime = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-02-23 18:18:56 +01:00
assert_eq! ( dt , v ) ;
2020-11-05 22:14:00 +01:00
db . execute ( " UPDATE foo set b = datetime(t) " , [ ] ) ? ; // "YYYY-MM-DD HH:MM:SS"
let hms : NaiveDateTime = db . query_row ( " SELECT b FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-04-02 16:48:33 +02:00
assert_eq! ( dt , hms ) ;
2020-11-05 22:14:00 +01:00
Ok ( ( ) )
2016-02-23 18:18:56 +01:00
}
#[ test ]
2020-11-05 22:14:00 +01:00
fn test_date_time_utc ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2016-04-02 13:57:55 +02:00
let date = NaiveDate ::from_ymd ( 2016 , 2 , 23 ) ;
2016-05-15 22:23:02 -05:00
let time = NaiveTime ::from_hms_milli ( 23 , 56 , 4 , 789 ) ;
2016-04-02 13:57:55 +02:00
let dt = NaiveDateTime ::new ( date , time ) ;
2017-10-09 22:55:55 +03:00
let utc = Utc . from_utc_datetime ( & dt ) ;
2016-02-23 18:18:56 +01:00
2021-01-19 21:16:08 +01:00
db . execute ( " INSERT INTO foo (t) VALUES (?) " , [ utc ] ) ? ;
2016-02-23 18:18:56 +01:00
2020-11-05 22:14:00 +01:00
let s : String = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2021-01-29 22:03:50 +01:00
assert_eq! ( " 2016-02-23 23:56:04.789+00:00 " , s ) ;
2016-05-16 09:13:45 -05:00
2020-11-05 22:14:00 +01:00
let v1 : DateTime < Utc > = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-05-16 09:13:45 -05:00
assert_eq! ( utc , v1 ) ;
2020-11-05 22:14:00 +01:00
let v2 : DateTime < Utc > =
db . query_row ( " SELECT '2016-02-23 23:56:04.789' " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-05-16 09:13:45 -05:00
assert_eq! ( utc , v2 ) ;
2020-11-05 22:14:00 +01:00
let v3 : DateTime < Utc > = db . query_row ( " SELECT '2016-02-23 23:56:04' " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-05-16 09:13:45 -05:00
assert_eq! ( utc - Duration ::milliseconds ( 789 ) , v3 ) ;
2016-05-16 09:27:50 -05:00
2020-11-05 22:14:00 +01:00
let v4 : DateTime < Utc > =
db . query_row ( " SELECT '2016-02-23 23:56:04.789+00:00' " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-05-16 09:27:50 -05:00
assert_eq! ( utc , v4 ) ;
2020-11-05 22:14:00 +01:00
Ok ( ( ) )
2016-02-23 18:18:56 +01:00
}
#[ test ]
2020-11-05 22:14:00 +01:00
fn test_date_time_local ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2016-04-02 13:57:55 +02:00
let date = NaiveDate ::from_ymd ( 2016 , 2 , 23 ) ;
2016-05-15 22:23:02 -05:00
let time = NaiveTime ::from_hms_milli ( 23 , 56 , 4 , 789 ) ;
2016-04-02 13:57:55 +02:00
let dt = NaiveDateTime ::new ( date , time ) ;
2016-02-23 18:18:56 +01:00
let local = Local . from_local_datetime ( & dt ) . single ( ) . unwrap ( ) ;
2021-01-19 21:16:08 +01:00
db . execute ( " INSERT INTO foo (t) VALUES (?) " , [ local ] ) ? ;
2016-02-23 18:18:56 +01:00
2016-05-16 09:08:31 -05:00
// Stored string should be in UTC
2020-11-05 22:14:00 +01:00
let s : String = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-05-16 09:08:31 -05:00
assert! ( s . ends_with ( " +00:00 " ) ) ;
2020-11-05 22:14:00 +01:00
let v : DateTime < Local > = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2016-02-23 18:18:56 +01:00
assert_eq! ( local , v ) ;
2020-11-05 22:14:00 +01:00
Ok ( ( ) )
2016-05-04 21:57:16 +02:00
}
2018-11-22 16:43:19 +01:00
#[ test ]
2020-11-05 22:14:00 +01:00
fn test_sqlite_functions ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2020-11-03 01:32:46 -08:00
let result : Result < NaiveTime > = db . query_row ( " SELECT CURRENT_TIME " , [ ] , | r | r . get ( 0 ) ) ;
2018-11-22 16:43:19 +01:00
assert! ( result . is_ok ( ) ) ;
2020-11-03 01:32:46 -08:00
let result : Result < NaiveDate > = db . query_row ( " SELECT CURRENT_DATE " , [ ] , | r | r . get ( 0 ) ) ;
2018-11-22 16:43:19 +01:00
assert! ( result . is_ok ( ) ) ;
let result : Result < NaiveDateTime > =
2020-11-03 01:32:46 -08:00
db . query_row ( " SELECT CURRENT_TIMESTAMP " , [ ] , | r | r . get ( 0 ) ) ;
2018-11-22 16:43:19 +01:00
assert! ( result . is_ok ( ) ) ;
let result : Result < DateTime < Utc > > =
2020-11-03 01:32:46 -08:00
db . query_row ( " SELECT CURRENT_TIMESTAMP " , [ ] , | r | r . get ( 0 ) ) ;
2018-11-22 16:43:19 +01:00
assert! ( result . is_ok ( ) ) ;
2020-11-05 22:14:00 +01:00
Ok ( ( ) )
2018-11-22 16:43:19 +01:00
}
2021-01-29 22:03:50 +01:00
#[ test ]
fn test_naive_date_time_param ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
let result : Result < bool > = db . query_row ( " SELECT 1 WHERE ? BETWEEN datetime('now', '-1 seconds') AND datetime('now', '+1 seconds') " , [ Utc ::now ( ) . naive_utc ( ) ] , | r | r . get ( 0 ) ) ;
assert! ( result . is_ok ( ) ) ;
Ok ( ( ) )
}
#[ test ]
fn test_date_time_param ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
let result : Result < bool > = db . query_row ( " SELECT 1 WHERE ? BETWEEN datetime('now', '-1 seconds') AND datetime('now', '+1 seconds') " , [ Utc ::now ( ) ] , | r | r . get ( 0 ) ) ;
assert! ( result . is_ok ( ) ) ;
Ok ( ( ) )
}
2016-02-23 18:18:56 +01:00
}