Skip to content

Commit ad2fcf3

Browse files
authored
Merge pull request sfackler#349 from sfackler/type-versions
SystemTime impls
2 parents d1660e1 + 6d61b05 commit ad2fcf3

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

postgres-shared/src/types/mod.rs

+53
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::collections::HashMap;
88
use std::error::Error;
99
use std::fmt;
1010
use std::sync::Arc;
11+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
1112

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

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

1718
pub 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.
453484
pub 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+
727780
fn 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())

postgres/tests/types/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::f32;
44
use std::f64;
55
use std::fmt;
66
use std::result;
7+
use std::time::{Duration, UNIX_EPOCH};
78

89
use postgres::types::{FromSql, FromSqlOwned, IsNull, Kind, ToSql, Type, WrongType};
910
use postgres::{Connection, TlsMode};
@@ -503,3 +504,25 @@ fn enum_() {
503504
_ => panic!("bad type"),
504505
}
505506
}
507+
508+
#[test]
509+
fn system_time() {
510+
test_type(
511+
"TIMESTAMP",
512+
&[
513+
(
514+
Some(UNIX_EPOCH + Duration::from_millis(1_010)),
515+
"'1970-01-01 00:00:01.01'",
516+
),
517+
(
518+
Some(UNIX_EPOCH - Duration::from_millis(1_010)),
519+
"'1969-12-31 23:59:58.99'",
520+
),
521+
(
522+
Some(UNIX_EPOCH + Duration::from_millis(946684800 * 1000 + 1_010)),
523+
"'2000-01-01 00:00:01.01'",
524+
),
525+
(None, "NULL"),
526+
],
527+
);
528+
}

0 commit comments

Comments
 (0)