Skip to content

Commit

Permalink
Merge pull request #349 from sfackler/type-versions
Browse files Browse the repository at this point in the history
SystemTime impls
  • Loading branch information
sfackler committed Apr 29, 2018
2 parents d1660e1 + 6d61b05 commit ad2fcf3
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
53 changes: 53 additions & 0 deletions postgres-shared/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use types::type_gen::{Inner, Other};

Expand All @@ -16,6 +17,11 @@ pub use postgres_protocol::Oid;

pub use types::special::{Date, Timestamp};

// Number of seconds from 1970-01-01 to 2000-01-01
const TIME_SEC_CONVERSION: u64 = 946684800;
const USEC_PER_SEC: u64 = 1_000_000;
const NSEC_PER_USEC: u64 = 1_000;

/// Generates a simple implementation of `ToSql::accepts` which accepts the
/// types passed to it.
#[macro_export]
Expand Down Expand Up @@ -252,6 +258,7 @@ impl WrongType {
/// | `&str`/`String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME, UNKNOWN |
/// | `&[u8]`/`Vec<u8>` | BYTEA |
/// | `HashMap<String, Option<String>>` | HSTORE |
/// | `SystemTime` | TIMESTAMP, TIMESTAMP WITH TIME ZONE |
///
/// In addition, some implementations are provided for types in third party
/// crates. These are disabled by default; to opt into one of these
Expand Down Expand Up @@ -449,6 +456,30 @@ impl<'a> FromSql<'a> for HashMap<String, Option<String>> {
}
}

impl<'a> FromSql<'a> for SystemTime {
fn from_sql(_: &Type, raw: &'a [u8]) -> Result<SystemTime, Box<Error + Sync + Send>> {
let time = types::timestamp_from_sql(raw)?;
let epoch = UNIX_EPOCH + Duration::from_secs(TIME_SEC_CONVERSION);

let negative = time < 0;
let time = time.abs() as u64;

let secs = time / USEC_PER_SEC;
let nsec = (time % USEC_PER_SEC) * NSEC_PER_USEC;
let offset = Duration::new(secs, nsec as u32);

let time = if negative {
epoch - offset
} else {
epoch + offset
};

Ok(time)
}

accepts!(TIMESTAMP, TIMESTAMPTZ);
}

/// An enum representing the nullability of a Postgres value.
pub enum IsNull {
/// The value is NULL.
Expand Down Expand Up @@ -477,6 +508,7 @@ pub enum IsNull {
/// | `&str`/`String` | VARCHAR, CHAR(n), TEXT, CITEXT, NAME |
/// | `&[u8]`/Vec<u8>` | BYTEA |
/// | `HashMap<String, Option<String>>` | HSTORE |
/// | `SystemTime` | TIMESTAMP, TIMESTAMP WITH TIME ZONE |
///
/// In addition, some implementations are provided for types in third party
/// crates. These are disabled by default; to opt into one of these
Expand Down Expand Up @@ -724,6 +756,27 @@ impl ToSql for HashMap<String, Option<String>> {
to_sql_checked!();
}

impl ToSql for SystemTime {
fn to_sql(&self, _: &Type, w: &mut Vec<u8>) -> Result<IsNull, Box<Error + Sync + Send>> {
let epoch = UNIX_EPOCH + Duration::from_secs(TIME_SEC_CONVERSION);

let to_usec =
|d: Duration| d.as_secs() * USEC_PER_SEC + (d.subsec_nanos() as u64) / NSEC_PER_USEC;

let time = match self.duration_since(epoch) {
Ok(duration) => to_usec(duration) as i64,
Err(e) => -(to_usec(e.duration()) as i64),
};

types::timestamp_to_sql(time, w);
Ok(IsNull::No)
}

accepts!(TIMESTAMP, TIMESTAMPTZ);

to_sql_checked!();
}

fn downcast(len: usize) -> Result<i32, Box<Error + Sync + Send>> {
if len > i32::max_value() as usize {
Err("value too large to transmit".into())
Expand Down
23 changes: 23 additions & 0 deletions postgres/tests/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::f32;
use std::f64;
use std::fmt;
use std::result;
use std::time::{Duration, UNIX_EPOCH};

use postgres::types::{FromSql, FromSqlOwned, IsNull, Kind, ToSql, Type, WrongType};
use postgres::{Connection, TlsMode};
Expand Down Expand Up @@ -503,3 +504,25 @@ fn enum_() {
_ => panic!("bad type"),
}
}

#[test]
fn system_time() {
test_type(
"TIMESTAMP",
&[
(
Some(UNIX_EPOCH + Duration::from_millis(1_010)),
"'1970-01-01 00:00:01.01'",
),
(
Some(UNIX_EPOCH - Duration::from_millis(1_010)),
"'1969-12-31 23:59:58.99'",
),
(
Some(UNIX_EPOCH + Duration::from_millis(946684800 * 1000 + 1_010)),
"'2000-01-01 00:00:01.01'",
),
(None, "NULL"),
],
);
}

0 comments on commit ad2fcf3

Please sign in to comment.