Skip to content

Add support for time 0.3 #824

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions postgres-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ with-geo-types-0_7 = ["geo-types-0_7"]
with-serde_json-1 = ["serde-1", "serde_json-1"]
with-uuid-0_8 = ["uuid-08"]
with-time-0_2 = ["time-02"]
with-time-0_3 = ["time-03"]

[dependencies]
bytes = "1.0"
Expand All @@ -40,3 +41,4 @@ serde-1 = { version = "1.0", package = "serde", optional = true }
serde_json-1 = { version = "1.0", package = "serde_json", optional = true }
uuid-08 = { version = "0.8", package = "uuid", optional = true }
time-02 = { version = "0.2", package = "time", optional = true }
time-03 = { version = "0.3", package = "time", default-features = false, optional = true }
2 changes: 2 additions & 0 deletions postgres-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ mod geo_types_07;
mod serde_json_1;
#[cfg(feature = "with-time-0_2")]
mod time_02;
#[cfg(feature = "with-time-0_3")]
mod time_03;
#[cfg(feature = "with-uuid-0_8")]
mod uuid_08;

Expand Down
108 changes: 108 additions & 0 deletions postgres-types/src/time_03.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use bytes::BytesMut;
use postgres_protocol::types;
use std::convert::TryFrom;
use std::error::Error;
use time_03::{Date, Duration, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset};

use crate::{FromSql, IsNull, ToSql, Type};

fn base() -> PrimitiveDateTime {
PrimitiveDateTime::new(Date::from_ordinal_date(2000, 1).unwrap(), Time::MIDNIGHT)
}

impl<'a> FromSql<'a> for PrimitiveDateTime {
fn from_sql(_: &Type, raw: &[u8]) -> Result<PrimitiveDateTime, Box<dyn Error + Sync + Send>> {
let t = types::timestamp_from_sql(raw)?;
Ok(base() + Duration::microseconds(t))
}

accepts!(TIMESTAMP);
}

impl ToSql for PrimitiveDateTime {
fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
let time = match i64::try_from((*self - base()).whole_microseconds()) {
Ok(time) => time,
Err(_) => return Err("value too large to transmit".into()),
};
types::timestamp_to_sql(time, w);
Ok(IsNull::No)
}

accepts!(TIMESTAMP);
to_sql_checked!();
}

impl<'a> FromSql<'a> for OffsetDateTime {
fn from_sql(type_: &Type, raw: &[u8]) -> Result<OffsetDateTime, Box<dyn Error + Sync + Send>> {
let primitive = PrimitiveDateTime::from_sql(type_, raw)?;
Ok(primitive.assume_utc())
}

accepts!(TIMESTAMPTZ);
}

impl ToSql for OffsetDateTime {
fn to_sql(
&self,
type_: &Type,
w: &mut BytesMut,
) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
let utc_datetime = self.to_offset(UtcOffset::UTC);
let date = utc_datetime.date();
let time = utc_datetime.time();
let primitive = PrimitiveDateTime::new(date, time);
primitive.to_sql(type_, w)
}

accepts!(TIMESTAMPTZ);
to_sql_checked!();
}

impl<'a> FromSql<'a> for Date {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Date, Box<dyn Error + Sync + Send>> {
let jd = types::date_from_sql(raw)?;
Ok(base().date() + Duration::days(i64::from(jd)))
}

accepts!(DATE);
}

impl ToSql for Date {
fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
let jd = (*self - base().date()).whole_days();
if jd > i64::from(i32::max_value()) || jd < i64::from(i32::min_value()) {
return Err("value too large to transmit".into());
}

types::date_to_sql(jd as i32, w);
Ok(IsNull::No)
}

accepts!(DATE);
to_sql_checked!();
}

impl<'a> FromSql<'a> for Time {
fn from_sql(_: &Type, raw: &[u8]) -> Result<Time, Box<dyn Error + Sync + Send>> {
let usec = types::time_from_sql(raw)?;
Ok(Time::MIDNIGHT + Duration::microseconds(usec))
}

accepts!(TIME);
}

impl ToSql for Time {
fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
let delta = *self - Time::MIDNIGHT;
let time = match i64::try_from(delta.whole_microseconds()) {
Ok(time) => time,
Err(_) => return Err("value too large to transmit".into()),
};
types::time_to_sql(time, w);
Ok(IsNull::No)
}

accepts!(TIME);
to_sql_checked!();
}
1 change: 1 addition & 0 deletions postgres/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ with-geo-types-0_7 = ["tokio-postgres/with-geo-types-0_7"]
with-serde_json-1 = ["tokio-postgres/with-serde_json-1"]
with-uuid-0_8 = ["tokio-postgres/with-uuid-0_8"]
with-time-0_2 = ["tokio-postgres/with-time-0_2"]
with-time-0_3 = ["tokio-postgres/with-time-0_3"]

[dependencies]
bytes = "1.0"
Expand Down
3 changes: 2 additions & 1 deletion postgres/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@
//! | `with-geo-types-0_7` | Enable support for the 0.7 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.7.0) 0.7 | no |
//! | `with-serde_json-1` | Enable support for the `serde_json` crate. | [serde_json](https://crates.io/crates/serde_json) 1.0 | no |
//! | `with-uuid-0_8` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 0.8 | no |
//! | `with-time-0_2` | Enable support for the `time` crate. | [time](https://crates.io/crates/time) 0.2 | no |
//! | `with-time-0_2` | Enable support for the 0.2 version of the `time` crate. | [time](https://crates.io/crates/time/0.2.0) 0.2 | no |
//! | `with-time-0_3` | Enable support for the 0.3 version of the `time` crate. | [time](https://crates.io/crates/time/0.3.0) 0.3 | no |
#![warn(clippy::all, rust_2018_idioms, missing_docs)]

pub use fallible_iterator;
Expand Down
3 changes: 2 additions & 1 deletion tokio-postgres/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ with-geo-types-0_7 = ["postgres-types/with-geo-types-0_7"]
with-serde_json-1 = ["postgres-types/with-serde_json-1"]
with-uuid-0_8 = ["postgres-types/with-uuid-0_8"]
with-time-0_2 = ["postgres-types/with-time-0_2"]
with-time-0_3 = ["postgres-types/with-time-0_3"]

[dependencies]
async-trait = "0.1"
Expand Down Expand Up @@ -70,4 +71,4 @@ serde-1 = { version = "1.0", package = "serde" }
serde_json-1 = { version = "1.0", package = "serde_json" }
uuid-08 = { version = "0.8", package = "uuid" }
time-02 = { version = "0.2", package = "time" }

time-03 = { version = "0.3", package = "time", features = ["parsing"] }
3 changes: 2 additions & 1 deletion tokio-postgres/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@
//! | `with-geo-types-0_7` | Enable support for the 0.7 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.7.0) 0.7 | no |
//! | `with-serde_json-1` | Enable support for the `serde_json` crate. | [serde_json](https://crates.io/crates/serde_json) 1.0 | no |
//! | `with-uuid-0_8` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 0.8 | no |
//! | `with-time-0_2` | Enable support for the `time` crate. | [time](https://crates.io/crates/time) 0.2 | no |
//! | `with-time-0_2` | Enable support for the 0.2 version of the `time` crate. | [time](https://crates.io/crates/time/0.2.0) 0.2 | no |
//! | `with-time-0_3` | Enable support for the 0.3 version of the `time` crate. | [time](https://crates.io/crates/time/0.3.0) 0.3 | no |
#![doc(html_root_url = "https://docs.rs/tokio-postgres/0.7")]
#![warn(rust_2018_idioms, clippy::all, missing_docs)]

Expand Down
2 changes: 2 additions & 0 deletions tokio-postgres/tests/test/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ mod geo_types_07;
mod serde_json_1;
#[cfg(feature = "with-time-0_2")]
mod time_02;
#[cfg(feature = "with-time-0_3")]
mod time_03;
#[cfg(feature = "with-uuid-0_8")]
mod uuid_08;

Expand Down
149 changes: 149 additions & 0 deletions tokio-postgres/tests/test/types/time_03.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use time_03::{format_description, OffsetDateTime, PrimitiveDateTime};
use tokio_postgres::types::{Date, Timestamp};

use crate::types::test_type;

// time 0.2 does not [yet?] support parsing fractional seconds
// https://github.com/time-rs/time/issues/226

#[tokio::test]
async fn test_primitive_date_time_params() {
fn make_check(time: &str) -> (Option<PrimitiveDateTime>, &str) {
let format =
format_description::parse("'[year]-[month]-[day] [hour]:[minute]:[second]'").unwrap();
(Some(PrimitiveDateTime::parse(time, &format).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
(None, "NULL"),
],
)
.await;
}

#[tokio::test]
async fn test_with_special_primitive_date_time_params() {
fn make_check(time: &str) -> (Timestamp<PrimitiveDateTime>, &str) {
let format =
format_description::parse("'[year]-[month]-[day] [hour]:[minute]:[second]'").unwrap();
(
Timestamp::Value(PrimitiveDateTime::parse(time, &format).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'"),
],
)
.await;
}

#[tokio::test]
async fn test_offset_date_time_params() {
fn make_check(time: &str) -> (Option<OffsetDateTime>, &str) {
let format =
format_description::parse("'[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]'").unwrap();
(Some(OffsetDateTime::parse(time, &format).unwrap()), time)
}
test_type(
"TIMESTAMP WITH TIME ZONE",
&[
make_check("'1970-01-01 00:00:00 +0000'"), // .010000000
make_check("'1965-09-25 11:19:33 +0000'"), // .100314000
make_check("'2010-02-09 23:11:45 +0000'"), // .120200000
(None, "NULL"),
],
)
.await;
}

#[tokio::test]
async fn test_with_special_offset_date_time_params() {
fn make_check(time: &str) -> (Timestamp<OffsetDateTime>, &str) {
let format =
format_description::parse("'[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]'").unwrap();
(
Timestamp::Value(OffsetDateTime::parse(time, &format).unwrap()),
time,
)
}
test_type(
"TIMESTAMP WITH TIME ZONE",
&[
make_check("'1970-01-01 00:00:00 +0000'"), // .010000000
make_check("'1965-09-25 11:19:33 +0000'"), // .100314000
make_check("'2010-02-09 23:11:45 +0000'"), // .120200000
(Timestamp::PosInfinity, "'infinity'"),
(Timestamp::NegInfinity, "'-infinity'"),
],
)
.await;
}

#[tokio::test]
async fn test_date_params() {
fn make_check(date: &str) -> (Option<time_03::Date>, &str) {
let format = format_description::parse("'[year]-[month]-[day]'").unwrap();
(Some(time_03::Date::parse(date, &format).unwrap()), date)
}
test_type(
"DATE",
&[
make_check("'1970-01-01'"),
make_check("'1965-09-25'"),
make_check("'2010-02-09'"),
(None, "NULL"),
],
)
.await;
}

#[tokio::test]
async fn test_with_special_date_params() {
fn make_check(date: &str) -> (Date<time_03::Date>, &str) {
let format = format_description::parse("'[year]-[month]-[day]'").unwrap();
(
Date::Value(time_03::Date::parse(date, &format).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'"),
],
)
.await;
}

#[tokio::test]
async fn test_time_params() {
fn make_check(time: &str) -> (Option<time_03::Time>, &str) {
let format = format_description::parse("'[hour]:[minute]:[second]'").unwrap();
(Some(time_03::Time::parse(time, &format).unwrap()), time)
}
test_type(
"TIME",
&[
make_check("'00:00:00'"), // .010000000
make_check("'11:19:33'"), // .100314000
make_check("'23:11:45'"), // .120200000
(None, "NULL"),
],
)
.await;
}