diff --git a/.travis.yml b/.travis.yml index 44a8cc5..7de78c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,5 +53,5 @@ matrix: script: | export RUST_BACKTRACE=1 && cargo build && - cargo test && + cargo test --all-features && cargo doc --no-deps diff --git a/Cargo.toml b/Cargo.toml index bd14466..2b2b74d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,9 @@ tokio = "0.1.20" failure = "0.1.5" serde = { version = "1.0.92", optional = true } serde_json = { version = "1.0", optional = true } +chrono = { version = "0.4.9", optional = true } [features] use-serde = ["serde", "serde_json"] +chrono_timestamps = ["chrono"] default = ["use-serde"] diff --git a/README.md b/README.md index d2f302a..91d8309 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Pull requests are always welcome. See [Contributing](https://github.com/Empty2k1 - Optional Serde Support for Deserialization - Running multiple queries in one request (e.g. `SELECT * FROM weather_berlin; SELECT * FROM weather_london`) - Authenticated and Unauthenticated Connections - +- Optional conversion between `Timestamp` and `Chrono::DateTime` via `chrono_timestamps` compilation feature ### Planned Features - Read Query Builder instead of supplying raw queries diff --git a/src/lib.rs b/src/lib.rs index f270b0d..c9bbf7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ //! - Optional Serde Support for Deserialization //! - Running multiple queries in one request (e.g. `SELECT * FROM weather_berlin; SELECT * FROM weather_london`) //! - Authenticated and Unauthenticated Connections -//! +//! - Optional conversion between `Timestamp` and `Chrono::DateTime` via `chrono_timestamps` compilation feature //! ## Planned Features //! //! - Read Query Builder instead of supplying raw queries diff --git a/src/query/consts.rs b/src/query/consts.rs new file mode 100644 index 0000000..32a1d0b --- /dev/null +++ b/src/query/consts.rs @@ -0,0 +1,7 @@ +pub const MINUTES_PER_HOUR: usize = 60; +pub const SECONDS_PER_MINUTE: usize = 60; +pub const MILLIS_PER_SECOND: usize = 1000; +pub const NANOS_PER_MILLI: usize = 1_000_000; + +#[cfg(test)] +pub const MICROS_PER_NANO: usize = 1000; diff --git a/src/query/mod.rs b/src/query/mod.rs index 97566e2..c25e2e2 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -19,14 +19,26 @@ //! assert!(read_query.is_ok()); //! ``` +#[cfg(feature = "chrono_timestamps")] +extern crate chrono; + +#[cfg(feature = "chrono_timestamps")] +use chrono::prelude::{DateTime, TimeZone, Utc}; +#[cfg(feature = "chrono_timestamps")] +use std::convert::TryInto; + +#[cfg(feature = "chrono_timestamps")] +pub mod consts; pub mod read_query; pub mod write_query; - use std::fmt; use crate::{Error, ReadQuery, WriteQuery}; -#[derive(PartialEq)] +#[cfg(feature = "chrono_timestamps")] +use consts::{MILLIS_PER_SECOND, MINUTES_PER_HOUR, NANOS_PER_MILLI, SECONDS_PER_MINUTE}; + +#[derive(PartialEq, Debug, Copy, Clone)] pub enum Timestamp { Now, Nanoseconds(usize), @@ -48,6 +60,47 @@ impl fmt::Display for Timestamp { } } +#[cfg(feature = "chrono_timestamps")] +impl Into> for Timestamp { + fn into(self) -> DateTime { + match self { + Timestamp::Now => Utc::now(), + Timestamp::Hours(h) => { + let nanos = + h * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI; + Utc.timestamp_nanos(nanos.try_into().unwrap()) + } + Timestamp::Minutes(m) => { + let nanos = m * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI; + Utc.timestamp_nanos(nanos.try_into().unwrap()) + } + Timestamp::Seconds(s) => { + let nanos = s * MILLIS_PER_SECOND * NANOS_PER_MILLI; + Utc.timestamp_nanos(nanos.try_into().unwrap()) + } + Timestamp::Milliseconds(millis) => { + let nanos = millis * NANOS_PER_MILLI; + Utc.timestamp_nanos(nanos.try_into().unwrap()) + } + Timestamp::Nanoseconds(nanos) => Utc.timestamp_nanos(nanos.try_into().unwrap()), + Timestamp::Microseconds(mis) => { + let nanos = mis / 10000; + Utc.timestamp_nanos(nanos.try_into().unwrap()) + } + } + } +} + +#[cfg(feature = "chrono_timestamps")] +impl From> for Timestamp +where + T: TimeZone, +{ + fn from(date_time: DateTime) -> Self { + Timestamp::Nanoseconds(date_time.timestamp_nanos() as usize) + } +} + /// Internal enum used to represent either type of query. pub enum QueryTypes<'a> { Read(&'a ReadQuery), @@ -157,8 +210,17 @@ pub enum QueryType { #[cfg(test)] mod tests { + #[cfg(feature = "chrono_timestamps")] + use std::convert::TryInto; + #[cfg(feature = "chrono_timestamps")] + extern crate chrono; + #[cfg(feature = "chrono_timestamps")] + use super::consts::{ + MICROS_PER_NANO, MILLIS_PER_SECOND, MINUTES_PER_HOUR, NANOS_PER_MILLI, SECONDS_PER_MINUTE, + }; use crate::query::{Timestamp, ValidQuery}; - + #[cfg(feature = "chrono_timestamps")] + use chrono::prelude::{DateTime, TimeZone, Utc}; #[test] fn test_equality_str() { assert_eq!(ValidQuery::from("hello"), "hello"); @@ -181,4 +243,85 @@ mod tests { fn test_format_for_timestamp_else() { assert!(format!("{}", Timestamp::Nanoseconds(100)) == "100"); } + + #[cfg(feature = "chrono_timestamps")] + #[test] + fn test_chrono_datetime_from_timestamp_now() { + let datetime_from_timestamp: DateTime = Timestamp::Now.into(); + assert_eq!(Utc::now().date(), datetime_from_timestamp.date()) + } + #[cfg(feature = "chrono_timestamps")] + #[test] + fn test_chrono_datetime_from_timestamp_hours() { + let datetime_from_timestamp: DateTime = Timestamp::Hours(2).into(); + assert_eq!( + Utc.timestamp_nanos( + (2 * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI) + .try_into() + .unwrap() + ), + datetime_from_timestamp + ) + } + #[cfg(feature = "chrono_timestamps")] + #[test] + fn test_chrono_datetime_from_timestamp_minutes() { + let datetime_from_timestamp: DateTime = Timestamp::Minutes(2).into(); + assert_eq!( + Utc.timestamp_nanos( + (2 * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI) + .try_into() + .unwrap() + ), + datetime_from_timestamp + ) + } + #[cfg(feature = "chrono_timestamps")] + #[test] + fn test_chrono_datetime_from_timestamp_seconds() { + let datetime_from_timestamp: DateTime = Timestamp::Seconds(2).into(); + assert_eq!( + Utc.timestamp_nanos( + (2 * MILLIS_PER_SECOND * NANOS_PER_MILLI) + .try_into() + .unwrap() + ), + datetime_from_timestamp + ) + } + #[cfg(feature = "chrono_timestamps")] + #[test] + fn test_chrono_datetime_from_timestamp_millis() { + let datetime_from_timestamp: DateTime = Timestamp::Milliseconds(2).into(); + assert_eq!( + Utc.timestamp_nanos((2 * NANOS_PER_MILLI).try_into().unwrap()), + datetime_from_timestamp + ) + } + + #[cfg(feature = "chrono_timestamps")] + #[test] + fn test_chrono_datetime_from_timestamp_nanos() { + let datetime_from_timestamp: DateTime = Timestamp::Nanoseconds(1).into(); + assert_eq!(Utc.timestamp_nanos(1), datetime_from_timestamp) + } + #[cfg(feature = "chrono_timestamps")] + #[test] + fn test_chrono_datetime_from_timestamp_micros() { + let datetime_from_timestamp: DateTime = Timestamp::Microseconds(1).into(); + assert_eq!( + Utc.timestamp_nanos((1 / MICROS_PER_NANO).try_into().unwrap()), + datetime_from_timestamp + ) + } + + #[cfg(feature = "chrono_timestamps")] + #[test] + fn test_timestamp_from_chrono_date() { + let timestamp_from_datetime: Timestamp = Utc.ymd(1970, 1, 1).and_hms(0, 0, 1).into(); + assert_eq!( + Timestamp::Nanoseconds(MILLIS_PER_SECOND * NANOS_PER_MILLI), + timestamp_from_datetime + ) + } }