@@ -8,6 +8,7 @@ use std::collections::HashMap;
88use std:: error:: Error ;
99use std:: fmt;
1010use std:: sync:: Arc ;
11+ use std:: time:: { Duration , SystemTime , UNIX_EPOCH } ;
1112
1213use types:: type_gen:: { Inner , Other } ;
1314
@@ -16,6 +17,11 @@ pub use postgres_protocol::Oid;
1617
1718pub use types:: special:: { Date , Timestamp } ;
1819
20+ // Number of seconds from 1970-01-01 to 2000-01-01
21+ const TIME_SEC_CONVERSION : u64 = 946684800 ;
22+ const USEC_PER_SEC : u64 = 1_000_000 ;
23+ const NSEC_PER_USEC : u64 = 1_000 ;
24+
1925/// Generates a simple implementation of `ToSql::accepts` which accepts the
2026/// types passed to it.
2127#[ macro_export]
@@ -252,6 +258,7 @@ impl WrongType {
252258/// | `&str`/`String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME, UNKNOWN |
253259/// | `&[u8]`/`Vec<u8>` | BYTEA |
254260/// | `HashMap<String, Option<String>>` | HSTORE |
261+ /// | `SystemTime` | TIMESTAMP, TIMESTAMP WITH TIME ZONE |
255262///
256263/// In addition, some implementations are provided for types in third party
257264/// crates. These are disabled by default; to opt into one of these
@@ -449,6 +456,30 @@ impl<'a> FromSql<'a> for HashMap<String, Option<String>> {
449456 }
450457}
451458
459+ impl < ' a > FromSql < ' a > for SystemTime {
460+ fn from_sql ( _: & Type , raw : & ' a [ u8 ] ) -> Result < SystemTime , Box < Error + Sync + Send > > {
461+ let time = types:: timestamp_from_sql ( raw) ?;
462+ let epoch = UNIX_EPOCH + Duration :: from_secs ( TIME_SEC_CONVERSION ) ;
463+
464+ let negative = time < 0 ;
465+ let time = time. abs ( ) as u64 ;
466+
467+ let secs = time / USEC_PER_SEC ;
468+ let nsec = ( time % USEC_PER_SEC ) * NSEC_PER_USEC ;
469+ let offset = Duration :: new ( secs, nsec as u32 ) ;
470+
471+ let time = if negative {
472+ epoch - offset
473+ } else {
474+ epoch + offset
475+ } ;
476+
477+ Ok ( time)
478+ }
479+
480+ accepts ! ( TIMESTAMP , TIMESTAMPTZ ) ;
481+ }
482+
452483/// An enum representing the nullability of a Postgres value.
453484pub enum IsNull {
454485 /// The value is NULL.
@@ -477,6 +508,7 @@ pub enum IsNull {
477508/// | `&str`/`String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME |
478509/// | `&[u8]`/Vec<u8>` | BYTEA |
479510/// | `HashMap<String, Option<String>>` | HSTORE |
511+ /// | `SystemTime` | TIMESTAMP, TIMESTAMP WITH TIME ZONE |
480512///
481513/// In addition, some implementations are provided for types in third party
482514/// crates. These are disabled by default; to opt into one of these
@@ -724,6 +756,27 @@ impl ToSql for HashMap<String, Option<String>> {
724756 to_sql_checked ! ( ) ;
725757}
726758
759+ impl ToSql for SystemTime {
760+ fn to_sql ( & self , _: & Type , w : & mut Vec < u8 > ) -> Result < IsNull , Box < Error + Sync + Send > > {
761+ let epoch = UNIX_EPOCH + Duration :: from_secs ( TIME_SEC_CONVERSION ) ;
762+
763+ let to_usec =
764+ |d : Duration | d. as_secs ( ) * USEC_PER_SEC + ( d. subsec_nanos ( ) as u64 ) / NSEC_PER_USEC ;
765+
766+ let time = match self . duration_since ( epoch) {
767+ Ok ( duration) => to_usec ( duration) as i64 ,
768+ Err ( e) => -( to_usec ( e. duration ( ) ) as i64 ) ,
769+ } ;
770+
771+ types:: timestamp_to_sql ( time, w) ;
772+ Ok ( IsNull :: No )
773+ }
774+
775+ accepts ! ( TIMESTAMP , TIMESTAMPTZ ) ;
776+
777+ to_sql_checked ! ( ) ;
778+ }
779+
727780fn downcast ( len : usize ) -> Result < i32 , Box < Error + Sync + Send > > {
728781 if len > i32:: max_value ( ) as usize {
729782 Err ( "value too large to transmit" . into ( ) )
0 commit comments