Add date and timestamp wrappers that can represent infinity
This patch adds the `types::Date` and `types::Timestamp` enums, which can represent either a date/timestamp, or positive or negative infinity.
This commit is contained in:
parent
e350c05124
commit
09ede70a74
@ -9,6 +9,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
|
||||
|
||||
pub use self::slice::Slice;
|
||||
pub use self::types::Type;
|
||||
pub use self::special::{Date, Timestamp};
|
||||
use {Result, SessionInfoNew, InnerConnection, OtherNew, WrongTypeNew, FieldNew};
|
||||
use error::Error;
|
||||
|
||||
@ -70,6 +71,7 @@ mod chrono;
|
||||
#[cfg(feature = "eui48")]
|
||||
mod eui48;
|
||||
|
||||
mod special;
|
||||
mod types;
|
||||
|
||||
/// A structure providing information for conversion methods.
|
||||
|
118
src/types/special.rs
Normal file
118
src/types/special.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use std::io::prelude::*;
|
||||
use std::{i32, i64};
|
||||
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
|
||||
|
||||
use Result;
|
||||
use error::Error;
|
||||
use types::{Type, FromSql, ToSql, IsNull, SessionInfo};
|
||||
|
||||
/// A wrapper that can be used to represent infinity with `Type::Date` types.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Date<T> {
|
||||
/// Represents `infinity`, a date that is later than all other dates.
|
||||
PosInfinity,
|
||||
/// Represents `-infinity`, a date that is earlier than all other dates.
|
||||
NegInfinity,
|
||||
/// The wrapped date.
|
||||
Value(T),
|
||||
}
|
||||
|
||||
impl<T: FromSql> FromSql for Date<T> {
|
||||
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Self> {
|
||||
if *ty != Type::Date {
|
||||
return Err(Error::Conversion("expected date type".into()));
|
||||
}
|
||||
|
||||
let mut buf = [0; 4];
|
||||
try!(raw.read_exact(buf.as_mut()));
|
||||
|
||||
match try!(buf.as_ref().read_i32::<BigEndian>()) {
|
||||
i32::MAX => Ok(Date::PosInfinity),
|
||||
i32::MIN => Ok(Date::NegInfinity),
|
||||
_ => T::from_sql(ty, &mut &mut buf.as_ref(), ctx).map(Date::Value),
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
*ty == Type::Date && T::accepts(ty)
|
||||
}
|
||||
}
|
||||
impl<T: ToSql> ToSql for Date<T> {
|
||||
fn to_sql<W: Write+?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
|
||||
if *ty != Type::Date {
|
||||
return Err(Error::Conversion("expected date type".into()));
|
||||
}
|
||||
|
||||
let value = match *self {
|
||||
Date::PosInfinity => i32::MAX,
|
||||
Date::NegInfinity => i32::MIN,
|
||||
Date::Value(ref v) => return v.to_sql(ty, out, ctx),
|
||||
};
|
||||
|
||||
try!(out.write_i32::<BigEndian>(value));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
*ty == Type::Date && T::accepts(ty)
|
||||
}
|
||||
|
||||
to_sql_checked!();
|
||||
}
|
||||
|
||||
/// A wrapper that can be used to represent infinity with `Type::Timestamp` and `Type::TimestampTZ`
|
||||
/// types.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Timestamp<T> {
|
||||
/// Represents `infinity`, a timestamp that is later than all other timestamps.
|
||||
PosInfinity,
|
||||
/// Represents `-infinity`, a timestamp that is earlier than all other timestamps.
|
||||
NegInfinity,
|
||||
/// The wrapped timestamp.
|
||||
Value(T),
|
||||
}
|
||||
|
||||
impl<T: FromSql> FromSql for Timestamp<T> {
|
||||
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Self> {
|
||||
if *ty != Type::Timestamp && *ty != Type::TimestampTZ {
|
||||
return Err(Error::Conversion("expected timestamp or timestamptz type".into()));
|
||||
}
|
||||
|
||||
let mut buf = [0; 8];
|
||||
try!(raw.read_exact(buf.as_mut()));
|
||||
|
||||
match try!(buf.as_ref().read_i64::<BigEndian>()) {
|
||||
i64::MAX => Ok(Timestamp::PosInfinity),
|
||||
i64::MIN => Ok(Timestamp::NegInfinity),
|
||||
_ => T::from_sql(ty, &mut &mut buf.as_ref(), ctx).map(Timestamp::Value),
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
(*ty == Type::Timestamp || *ty == Type::TimestampTZ) && T::accepts(ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToSql> ToSql for Timestamp<T> {
|
||||
fn to_sql<W: Write+?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
|
||||
if *ty != Type::Timestamp && *ty != Type::TimestampTZ {
|
||||
return Err(Error::Conversion("expected timestamp or timestamptz type".into()));
|
||||
}
|
||||
|
||||
let value = match *self {
|
||||
Timestamp::PosInfinity => i64::MAX,
|
||||
Timestamp::NegInfinity => i64::MIN,
|
||||
Timestamp::Value(ref v) => return v.to_sql(ty, out, ctx),
|
||||
};
|
||||
|
||||
try!(out.write_i64::<BigEndian>(value));
|
||||
Ok(IsNull::No)
|
||||
}
|
||||
|
||||
fn accepts(ty: &Type) -> bool {
|
||||
(*ty == Type::Timestamp || *ty == Type::TimestampTZ) && T::accepts(ty)
|
||||
}
|
||||
|
||||
to_sql_checked!();
|
||||
}
|
@ -3,6 +3,8 @@ extern crate chrono;
|
||||
use self::chrono::{TimeZone, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC};
|
||||
use types::test_type;
|
||||
|
||||
use postgres::types::{Date, Timestamp};
|
||||
|
||||
#[test]
|
||||
fn test_naive_date_time_params() {
|
||||
fn make_check<'a>(time: &'a str) -> (Option<NaiveDateTime>, &'a str) {
|
||||
@ -15,6 +17,20 @@ fn test_naive_date_time_params() {
|
||||
(None, "NULL")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_special_naive_date_time_params() {
|
||||
fn make_check<'a>(time: &'a str) -> (Timestamp<NaiveDateTime>, &'a str) {
|
||||
(Timestamp::Value(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()),
|
||||
time)
|
||||
}
|
||||
test_type("TIMESTAMP",
|
||||
&[make_check("'1970-01-01 00:00:00.010000000'"),
|
||||
make_check("'1965-09-25 11:19:33.100314000'"),
|
||||
make_check("'2010-02-09 23:11:45.120200000'"),
|
||||
(Timestamp::PosInfinity, "'infinity'"),
|
||||
(Timestamp::NegInfinity, "'-infinity'")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_time_params() {
|
||||
fn make_check<'a>(time: &'a str) -> (Option<DateTime<UTC>>, &'a str) {
|
||||
@ -27,6 +43,19 @@ fn test_date_time_params() {
|
||||
(None, "NULL")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_special_date_time_params() {
|
||||
fn make_check<'a>(time: &'a str) -> (Timestamp<DateTime<UTC>>, &'a str) {
|
||||
(Timestamp::Value(UTC.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), time)
|
||||
}
|
||||
test_type("TIMESTAMP WITH TIME ZONE",
|
||||
&[make_check("'1970-01-01 00:00:00.010000000'"),
|
||||
make_check("'1965-09-25 11:19:33.100314000'"),
|
||||
make_check("'2010-02-09 23:11:45.120200000'"),
|
||||
(Timestamp::PosInfinity, "'infinity'"),
|
||||
(Timestamp::NegInfinity, "'-infinity'")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_date_params() {
|
||||
fn make_check<'a>(time: &'a str) -> (Option<NaiveDate>, &'a str) {
|
||||
@ -39,6 +68,19 @@ fn test_date_params() {
|
||||
(None, "NULL")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_special_date_params() {
|
||||
fn make_check<'a>(date: &'a str) -> (Date<NaiveDate>, &'a str) {
|
||||
(Date::Value(NaiveDate::parse_from_str(date, "'%Y-%m-%d'").unwrap()), date)
|
||||
}
|
||||
test_type("DATE",
|
||||
&[make_check("'1970-01-01'"),
|
||||
make_check("'1965-09-25'"),
|
||||
make_check("'2010-02-09'"),
|
||||
(Date::PosInfinity, "'infinity'"),
|
||||
(Date::NegInfinity, "'-infinity'")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_params() {
|
||||
fn make_check<'a>(time: &'a str) -> (Option<NaiveTime>, &'a str) {
|
||||
|
@ -3,6 +3,8 @@ extern crate time;
|
||||
use self::time::Timespec;
|
||||
use types::test_type;
|
||||
|
||||
use postgres::types::Timestamp;
|
||||
|
||||
#[test]
|
||||
fn test_tm_params() {
|
||||
fn make_check<'a>(time: &'a str) -> (Option<Timespec>, &'a str) {
|
||||
@ -19,3 +21,23 @@ fn test_tm_params() {
|
||||
make_check("'2010-02-09 23:11:45.1202'"),
|
||||
(None, "NULL")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_special_tm_params() {
|
||||
fn make_check<'a>(time: &'a str) -> (Timestamp<Timespec>, &'a str) {
|
||||
(Timestamp::Value(time::strptime(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap().to_timespec()),
|
||||
time)
|
||||
}
|
||||
test_type("TIMESTAMP",
|
||||
&[make_check("'1970-01-01 00:00:00.01'"),
|
||||
make_check("'1965-09-25 11:19:33.100314'"),
|
||||
make_check("'2010-02-09 23:11:45.1202'"),
|
||||
(Timestamp::PosInfinity, "'infinity'"),
|
||||
(Timestamp::NegInfinity, "'-infinity'")]);
|
||||
test_type("TIMESTAMP WITH TIME ZONE",
|
||||
&[make_check("'1970-01-01 00:00:00.01'"),
|
||||
make_check("'1965-09-25 11:19:33.100314'"),
|
||||
make_check("'2010-02-09 23:11:45.1202'"),
|
||||
(Timestamp::PosInfinity, "'infinity'"),
|
||||
(Timestamp::NegInfinity, "'-infinity'")]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user