2020-11-22 16:34:03 +08:00
//! [`ToSql`] and [`FromSql`] implementation for [`time::OffsetDateTime`].
2018-10-31 03:11:35 +08:00
use crate ::types ::{ FromSql , FromSqlError , FromSqlResult , ToSql , ToSqlOutput , ValueRef } ;
use crate ::Result ;
2020-07-11 23:59:29 +08:00
use time ::{ OffsetDateTime , PrimitiveDateTime , UtcOffset } ;
2016-02-26 01:44:53 +08:00
2018-11-22 23:50:10 +08:00
const CURRENT_TIMESTAMP_FMT : & str = " %Y-%m-%d %H:%M:%S " ;
2021-06-12 03:42:02 +08:00
const SQLITE_DATETIME_FMT : & str = " %Y-%m-%d %H:%M:%S.%N " ;
const SQLITE_DATETIME_Z_FMT : & str = " %Y-%m-%d %H:%M:%S.%NZ " ;
2020-07-11 23:59:29 +08:00
const SQLITE_DATETIME_FMT_LEGACY : & str = " %Y-%m-%d %H:%M:%S:%N %z " ;
2016-02-26 01:44:53 +08:00
2020-07-11 23:59:29 +08:00
impl ToSql for OffsetDateTime {
2020-11-04 11:10:23 +08:00
#[ inline ]
2018-12-08 04:57:04 +08:00
fn to_sql ( & self ) -> Result < ToSqlOutput < '_ > > {
2021-06-12 03:42:02 +08:00
// FIXME keep original offset
let time_string = self . to_offset ( UtcOffset ::UTC ) . format ( SQLITE_DATETIME_Z_FMT ) ;
2016-05-26 10:57:43 +08:00
Ok ( ToSqlOutput ::from ( time_string ) )
2016-02-26 01:44:53 +08:00
}
}
2020-07-11 23:59:29 +08:00
impl FromSql for OffsetDateTime {
2018-12-08 04:57:04 +08:00
fn column_result ( value : ValueRef < '_ > ) -> FromSqlResult < Self > {
2020-07-11 23:59:29 +08:00
value . as_str ( ) . and_then ( | s | {
match s . len ( ) {
19 = > PrimitiveDateTime ::parse ( s , CURRENT_TIMESTAMP_FMT ) . map ( | d | d . assume_utc ( ) ) ,
2021-06-12 03:42:02 +08:00
_ = > {
let format = if s . ends_with ( 'Z' ) {
SQLITE_DATETIME_Z_FMT
} else {
SQLITE_DATETIME_FMT
} ;
PrimitiveDateTime ::parse ( s , format )
. map ( | d | d . assume_utc ( ) )
. or_else ( | err | {
OffsetDateTime ::parse ( s , SQLITE_DATETIME_FMT_LEGACY ) . map_err ( | _ | err )
} )
}
2020-07-11 23:59:29 +08:00
}
. map_err ( | err | FromSqlError ::Other ( Box ::new ( err ) ) )
} )
2016-05-16 11:30:11 +08:00
}
2016-02-26 01:44:53 +08:00
}
#[ cfg(test) ]
mod test {
2020-11-03 17:32:46 +08:00
use crate ::{ Connection , Result } ;
2020-07-11 23:59:29 +08:00
use std ::time ::Duration ;
use time ::OffsetDateTime ;
2016-02-26 01:44:53 +08:00
2020-11-06 05:14:00 +08: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) " ) ? ;
Ok ( db )
2016-02-26 01:44:53 +08:00
}
#[ test ]
2020-11-06 05:14:00 +08:00
fn test_offset_date_time ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2016-02-26 01:44:53 +08:00
2017-07-18 23:46:49 +08:00
let mut ts_vec = vec! [ ] ;
2020-07-11 23:59:29 +08:00
let make_datetime =
| secs , nanos | OffsetDateTime ::from_unix_timestamp ( secs ) + Duration ::from_nanos ( nanos ) ;
ts_vec . push ( make_datetime ( 10_000 , 0 ) ) ; //January 1, 1970 2:46:40 AM
ts_vec . push ( make_datetime ( 10_000 , 1000 ) ) ; //January 1, 1970 2:46:40 AM (and one microsecond)
ts_vec . push ( make_datetime ( 1_500_391_124 , 1_000_000 ) ) ; //July 18, 2017
ts_vec . push ( make_datetime ( 2_000_000_000 , 2_000_000 ) ) ; //May 18, 2033
ts_vec . push ( make_datetime ( 3_000_000_000 , 999_999_999 ) ) ; //January 24, 2065
ts_vec . push ( make_datetime ( 10_000_000_000 , 0 ) ) ; //November 20, 2286
2017-07-18 23:46:49 +08:00
for ts in ts_vec {
2021-01-20 04:16:08 +08:00
db . execute ( " INSERT INTO foo(t) VALUES (?) " , [ ts ] ) ? ;
2017-07-18 23:46:49 +08:00
2020-11-06 05:14:00 +08:00
let from : OffsetDateTime = db . query_row ( " SELECT t FROM foo " , [ ] , | r | r . get ( 0 ) ) ? ;
2017-07-18 23:46:49 +08:00
2020-11-06 05:14:00 +08:00
db . execute ( " DELETE FROM foo " , [ ] ) ? ;
2017-07-18 23:46:49 +08:00
assert_eq! ( from , ts ) ;
}
2020-11-06 05:14:00 +08:00
Ok ( ( ) )
2016-02-26 01:44:53 +08:00
}
2018-11-22 23:50:10 +08:00
2021-06-12 03:42:02 +08:00
#[ test ]
fn test_string_values ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
for s in [
" 2013-10-07 08:23:19.120 " ,
" 2013-10-07 08:23:19.120Z " ,
//"2013-10-07T08:23:19.120Z", // TODO
" 2013-10-07 04:23:19.120-04:00 " , // FIXME offset is lost!!!
] {
let result : Result < OffsetDateTime > = db . query_row ( " SELECT ? " , [ s ] , | r | r . get ( 0 ) ) ;
assert! ( result . is_ok ( ) ) ;
}
Ok ( ( ) )
}
2018-11-22 23:50:10 +08:00
#[ test ]
2020-11-06 05:14:00 +08:00
fn test_sqlite_functions ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2020-07-11 23:59:29 +08:00
let result : Result < OffsetDateTime > =
2020-11-03 17:32:46 +08:00
db . query_row ( " SELECT CURRENT_TIMESTAMP " , [ ] , | r | r . get ( 0 ) ) ;
2018-11-22 23:50:10 +08:00
assert! ( result . is_ok ( ) ) ;
2020-11-06 05:14:00 +08:00
Ok ( ( ) )
2018-11-22 23:50:10 +08:00
}
2021-01-30 05:03:50 +08:00
#[ test ]
fn test_param ( ) -> Result < ( ) > {
let db = checked_memory_handle ( ) ? ;
2021-01-30 19:55:00 +08:00
let result : Result < bool > = db . query_row ( " SELECT 1 WHERE ? BETWEEN datetime('now', '-1 minute') AND datetime('now', '+1 minute') " , [ OffsetDateTime ::now_utc ( ) ] , | r | r . get ( 0 ) ) ;
2021-01-30 05:03:50 +08:00
assert! ( result . is_ok ( ) ) ;
Ok ( ( ) )
}
2016-02-26 01:44:53 +08:00
}