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 } ;
2021-06-13 00:29:01 +08:00
use crate ::{ Error , Result } ;
use time ::format_description ::well_known ::Rfc3339 ;
2021-06-15 03:29:24 +08:00
use time ::format_description ::FormatItem ;
use time ::macros ::format_description ;
2021-06-13 00:29:01 +08:00
use time ::{ OffsetDateTime , PrimitiveDateTime , UtcOffset } ;
2021-06-15 03:29:24 +08:00
const PRIMITIVE_SHORT_DATE_TIME_FORMAT : & [ FormatItem < '_ > ] =
format_description! ( " [year]-[month]-[day] [hour]:[minute]:[second] " ) ;
const PRIMITIVE_DATE_TIME_FORMAT : & [ FormatItem < '_ > ] =
format_description! ( " [year]-[month]-[day] [hour]:[minute]:[second].[subsecond] " ) ;
const PRIMITIVE_DATE_TIME_Z_FORMAT : & [ FormatItem < '_ > ] =
format_description! ( " [year]-[month]-[day] [hour]:[minute]:[second].[subsecond]Z " ) ;
const OFFSET_SHORT_DATE_TIME_FORMAT : & [ FormatItem < '_ > ] = format_description! (
" [year]-[month]-[day] [hour]:[minute]:[second][offset_hour sign:mandatory]:[offset_minute] "
) ;
const OFFSET_DATE_TIME_FORMAT : & [ FormatItem < '_ > ] = format_description! (
" [year]-[month]-[day] [hour]:[minute]:[second].[subsecond][offset_hour sign:mandatory]:[offset_minute] "
) ;
const LEGACY_DATE_TIME_FORMAT : & [ FormatItem < '_ > ] = format_description! (
" [year]-[month]-[day] [hour]:[minute]:[second]:[subsecond] [offset_hour sign:mandatory]:[offset_minute] "
) ;
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
2021-06-12 18:04:10 +08:00
let time_string = self
. to_offset ( UtcOffset ::UTC )
2021-06-13 00:29:01 +08:00
. format ( & PRIMITIVE_DATE_TIME_Z_FORMAT )
. map_err ( | err | Error ::ToSqlConversionFailure ( err . into ( ) ) ) ? ;
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 | {
2021-06-13 00:29:01 +08:00
let s = s . strip_suffix ( 'Z' ) . unwrap_or ( s ) ;
2020-07-11 23:59:29 +08:00
match s . len ( ) {
2021-06-12 18:04:10 +08:00
len if len < = 19 = > {
// TODO YYYY-MM-DDTHH:MM:SS
2021-06-13 00:29:01 +08:00
PrimitiveDateTime ::parse ( s , & PRIMITIVE_SHORT_DATE_TIME_FORMAT )
. map ( | d | d . assume_utc ( ) )
2021-06-12 18:04:10 +08:00
}
_ if s . as_bytes ( ) [ 10 ] = = b 'T' = > {
// YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM
2021-06-13 00:29:01 +08:00
OffsetDateTime ::parse ( s , & Rfc3339 )
2021-06-12 18:04:10 +08:00
}
_ if s . as_bytes ( ) [ 19 ] = = b ':' = > {
// legacy
2021-06-13 00:29:01 +08:00
OffsetDateTime ::parse ( s , & LEGACY_DATE_TIME_FORMAT )
2021-06-12 18:04:10 +08:00
}
2021-06-13 00:29:01 +08:00
_ if s . as_bytes ( ) [ 19 ] = = b '.' = > OffsetDateTime ::parse ( s , & OFFSET_DATE_TIME_FORMAT )
. or_else ( | err | {
PrimitiveDateTime ::parse ( s , & PRIMITIVE_DATE_TIME_FORMAT )
2021-06-12 18:04:10 +08:00
. map ( | d | d . assume_utc ( ) )
. map_err ( | _ | err )
2021-06-13 00:29:01 +08:00
} ) ,
_ = > OffsetDateTime ::parse ( s , & OFFSET_SHORT_DATE_TIME_FORMAT ) ,
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 } ;
2021-06-15 02:49:59 +08:00
use time ::format_description ::well_known ::Rfc3339 ;
2021-06-15 03:29:24 +08:00
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! [ ] ;
2021-06-13 00:29:01 +08:00
let make_datetime = | secs : i128 , nanos : i128 | {
OffsetDateTime ::from_unix_timestamp_nanos ( 1_000_000_000 * secs + nanos ) . unwrap ( )
} ;
2020-07-11 23:59:29 +08:00
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 ( ) ? ;
2021-06-12 18:04:10 +08:00
for ( s , t ) in vec! [
(
" 2013-10-07 08:23:19.120 " ,
2021-06-15 03:29:24 +08:00
Ok ( OffsetDateTime ::parse ( " 2013-10-07T08:23:19.120Z " , & Rfc3339 ) . unwrap ( ) ) ,
2021-06-12 18:04:10 +08:00
) ,
(
" 2013-10-07 08:23:19.120Z " ,
2021-06-15 03:29:24 +08:00
Ok ( OffsetDateTime ::parse ( " 2013-10-07T08:23:19.120Z " , & Rfc3339 ) . unwrap ( ) ) ,
2021-06-12 18:04:10 +08:00
) ,
2021-06-12 03:42:02 +08:00
//"2013-10-07T08:23:19.120Z", // TODO
2021-06-12 18:04:10 +08:00
(
" 2013-10-07 04:23:19.120-04:00 " ,
2021-06-15 03:29:24 +08:00
Ok ( OffsetDateTime ::parse ( " 2013-10-07T04:23:19.120-04:00 " , & Rfc3339 ) . unwrap ( ) ) ,
2021-06-12 18:04:10 +08:00
) ,
2021-06-12 03:42:02 +08:00
] {
let result : Result < OffsetDateTime > = db . query_row ( " SELECT ? " , [ s ] , | r | r . get ( 0 ) ) ;
2021-06-12 18:04:10 +08:00
assert_eq! ( result , t ) ;
2021-06-12 03:42:02 +08:00
}
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
}