Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 09ede70

Browse files
committed
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.
1 parent e350c05 commit 09ede70

File tree

4 files changed

+184
-0
lines changed

4 files changed

+184
-0
lines changed

src/types/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
99

1010
pub use self::slice::Slice;
1111
pub use self::types::Type;
12+
pub use self::special::{Date, Timestamp};
1213
use {Result, SessionInfoNew, InnerConnection, OtherNew, WrongTypeNew, FieldNew};
1314
use error::Error;
1415

@@ -70,6 +71,7 @@ mod chrono;
7071
#[cfg(feature = "eui48")]
7172
mod eui48;
7273

74+
mod special;
7375
mod types;
7476

7577
/// A structure providing information for conversion methods.

src/types/special.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use std::io::prelude::*;
2+
use std::{i32, i64};
3+
4+
use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
5+
6+
use Result;
7+
use error::Error;
8+
use types::{Type, FromSql, ToSql, IsNull, SessionInfo};
9+
10+
/// A wrapper that can be used to represent infinity with `Type::Date` types.
11+
#[derive(Debug, Clone, Copy, PartialEq)]
12+
pub enum Date<T> {
13+
/// Represents `infinity`, a date that is later than all other dates.
14+
PosInfinity,
15+
/// Represents `-infinity`, a date that is earlier than all other dates.
16+
NegInfinity,
17+
/// The wrapped date.
18+
Value(T),
19+
}
20+
21+
impl<T: FromSql> FromSql for Date<T> {
22+
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Self> {
23+
if *ty != Type::Date {
24+
return Err(Error::Conversion("expected date type".into()));
25+
}
26+
27+
let mut buf = [0; 4];
28+
try!(raw.read_exact(buf.as_mut()));
29+
30+
match try!(buf.as_ref().read_i32::<BigEndian>()) {
31+
i32::MAX => Ok(Date::PosInfinity),
32+
i32::MIN => Ok(Date::NegInfinity),
33+
_ => T::from_sql(ty, &mut &mut buf.as_ref(), ctx).map(Date::Value),
34+
}
35+
}
36+
37+
fn accepts(ty: &Type) -> bool {
38+
*ty == Type::Date && T::accepts(ty)
39+
}
40+
}
41+
impl<T: ToSql> ToSql for Date<T> {
42+
fn to_sql<W: Write+?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
43+
if *ty != Type::Date {
44+
return Err(Error::Conversion("expected date type".into()));
45+
}
46+
47+
let value = match *self {
48+
Date::PosInfinity => i32::MAX,
49+
Date::NegInfinity => i32::MIN,
50+
Date::Value(ref v) => return v.to_sql(ty, out, ctx),
51+
};
52+
53+
try!(out.write_i32::<BigEndian>(value));
54+
Ok(IsNull::No)
55+
}
56+
57+
fn accepts(ty: &Type) -> bool {
58+
*ty == Type::Date && T::accepts(ty)
59+
}
60+
61+
to_sql_checked!();
62+
}
63+
64+
/// A wrapper that can be used to represent infinity with `Type::Timestamp` and `Type::TimestampTZ`
65+
/// types.
66+
#[derive(Debug, Clone, Copy, PartialEq)]
67+
pub enum Timestamp<T> {
68+
/// Represents `infinity`, a timestamp that is later than all other timestamps.
69+
PosInfinity,
70+
/// Represents `-infinity`, a timestamp that is earlier than all other timestamps.
71+
NegInfinity,
72+
/// The wrapped timestamp.
73+
Value(T),
74+
}
75+
76+
impl<T: FromSql> FromSql for Timestamp<T> {
77+
fn from_sql<R: Read>(ty: &Type, raw: &mut R, ctx: &SessionInfo) -> Result<Self> {
78+
if *ty != Type::Timestamp && *ty != Type::TimestampTZ {
79+
return Err(Error::Conversion("expected timestamp or timestamptz type".into()));
80+
}
81+
82+
let mut buf = [0; 8];
83+
try!(raw.read_exact(buf.as_mut()));
84+
85+
match try!(buf.as_ref().read_i64::<BigEndian>()) {
86+
i64::MAX => Ok(Timestamp::PosInfinity),
87+
i64::MIN => Ok(Timestamp::NegInfinity),
88+
_ => T::from_sql(ty, &mut &mut buf.as_ref(), ctx).map(Timestamp::Value),
89+
}
90+
}
91+
92+
fn accepts(ty: &Type) -> bool {
93+
(*ty == Type::Timestamp || *ty == Type::TimestampTZ) && T::accepts(ty)
94+
}
95+
}
96+
97+
impl<T: ToSql> ToSql for Timestamp<T> {
98+
fn to_sql<W: Write+?Sized>(&self, ty: &Type, out: &mut W, ctx: &SessionInfo) -> Result<IsNull> {
99+
if *ty != Type::Timestamp && *ty != Type::TimestampTZ {
100+
return Err(Error::Conversion("expected timestamp or timestamptz type".into()));
101+
}
102+
103+
let value = match *self {
104+
Timestamp::PosInfinity => i64::MAX,
105+
Timestamp::NegInfinity => i64::MIN,
106+
Timestamp::Value(ref v) => return v.to_sql(ty, out, ctx),
107+
};
108+
109+
try!(out.write_i64::<BigEndian>(value));
110+
Ok(IsNull::No)
111+
}
112+
113+
fn accepts(ty: &Type) -> bool {
114+
(*ty == Type::Timestamp || *ty == Type::TimestampTZ) && T::accepts(ty)
115+
}
116+
117+
to_sql_checked!();
118+
}

tests/types/chrono.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ extern crate chrono;
33
use self::chrono::{TimeZone, NaiveDate, NaiveTime, NaiveDateTime, DateTime, UTC};
44
use types::test_type;
55

6+
use postgres::types::{Date, Timestamp};
7+
68
#[test]
79
fn test_naive_date_time_params() {
810
fn make_check<'a>(time: &'a str) -> (Option<NaiveDateTime>, &'a str) {
@@ -15,6 +17,20 @@ fn test_naive_date_time_params() {
1517
(None, "NULL")]);
1618
}
1719

20+
#[test]
21+
fn test_with_special_naive_date_time_params() {
22+
fn make_check<'a>(time: &'a str) -> (Timestamp<NaiveDateTime>, &'a str) {
23+
(Timestamp::Value(NaiveDateTime::parse_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()),
24+
time)
25+
}
26+
test_type("TIMESTAMP",
27+
&[make_check("'1970-01-01 00:00:00.010000000'"),
28+
make_check("'1965-09-25 11:19:33.100314000'"),
29+
make_check("'2010-02-09 23:11:45.120200000'"),
30+
(Timestamp::PosInfinity, "'infinity'"),
31+
(Timestamp::NegInfinity, "'-infinity'")]);
32+
}
33+
1834
#[test]
1935
fn test_date_time_params() {
2036
fn make_check<'a>(time: &'a str) -> (Option<DateTime<UTC>>, &'a str) {
@@ -27,6 +43,19 @@ fn test_date_time_params() {
2743
(None, "NULL")]);
2844
}
2945

46+
#[test]
47+
fn test_with_special_date_time_params() {
48+
fn make_check<'a>(time: &'a str) -> (Timestamp<DateTime<UTC>>, &'a str) {
49+
(Timestamp::Value(UTC.datetime_from_str(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap()), time)
50+
}
51+
test_type("TIMESTAMP WITH TIME ZONE",
52+
&[make_check("'1970-01-01 00:00:00.010000000'"),
53+
make_check("'1965-09-25 11:19:33.100314000'"),
54+
make_check("'2010-02-09 23:11:45.120200000'"),
55+
(Timestamp::PosInfinity, "'infinity'"),
56+
(Timestamp::NegInfinity, "'-infinity'")]);
57+
}
58+
3059
#[test]
3160
fn test_date_params() {
3261
fn make_check<'a>(time: &'a str) -> (Option<NaiveDate>, &'a str) {
@@ -39,6 +68,19 @@ fn test_date_params() {
3968
(None, "NULL")]);
4069
}
4170

71+
#[test]
72+
fn test_with_special_date_params() {
73+
fn make_check<'a>(date: &'a str) -> (Date<NaiveDate>, &'a str) {
74+
(Date::Value(NaiveDate::parse_from_str(date, "'%Y-%m-%d'").unwrap()), date)
75+
}
76+
test_type("DATE",
77+
&[make_check("'1970-01-01'"),
78+
make_check("'1965-09-25'"),
79+
make_check("'2010-02-09'"),
80+
(Date::PosInfinity, "'infinity'"),
81+
(Date::NegInfinity, "'-infinity'")]);
82+
}
83+
4284
#[test]
4385
fn test_time_params() {
4486
fn make_check<'a>(time: &'a str) -> (Option<NaiveTime>, &'a str) {

tests/types/time.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ extern crate time;
33
use self::time::Timespec;
44
use types::test_type;
55

6+
use postgres::types::Timestamp;
7+
68
#[test]
79
fn test_tm_params() {
810
fn make_check<'a>(time: &'a str) -> (Option<Timespec>, &'a str) {
@@ -19,3 +21,23 @@ fn test_tm_params() {
1921
make_check("'2010-02-09 23:11:45.1202'"),
2022
(None, "NULL")]);
2123
}
24+
25+
#[test]
26+
fn test_with_special_tm_params() {
27+
fn make_check<'a>(time: &'a str) -> (Timestamp<Timespec>, &'a str) {
28+
(Timestamp::Value(time::strptime(time, "'%Y-%m-%d %H:%M:%S.%f'").unwrap().to_timespec()),
29+
time)
30+
}
31+
test_type("TIMESTAMP",
32+
&[make_check("'1970-01-01 00:00:00.01'"),
33+
make_check("'1965-09-25 11:19:33.100314'"),
34+
make_check("'2010-02-09 23:11:45.1202'"),
35+
(Timestamp::PosInfinity, "'infinity'"),
36+
(Timestamp::NegInfinity, "'-infinity'")]);
37+
test_type("TIMESTAMP WITH TIME ZONE",
38+
&[make_check("'1970-01-01 00:00:00.01'"),
39+
make_check("'1965-09-25 11:19:33.100314'"),
40+
make_check("'2010-02-09 23:11:45.1202'"),
41+
(Timestamp::PosInfinity, "'infinity'"),
42+
(Timestamp::NegInfinity, "'-infinity'")]);
43+
}

0 commit comments

Comments
 (0)