Skip to content

Chrono types for Timestamp #41

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 14 commits into from
Oct 31, 2019
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ matrix:
script: |
export RUST_BACKTRACE=1 &&
cargo build &&
cargo test &&
cargo test --all-features &&
cargo doc --no-deps
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Utc>` via `chrono_timestamps` compilation feature
### Planned Features

- Read Query Builder instead of supplying raw queries
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Utc>` via `chrono_timestamps` compilation feature
//! ## Planned Features
//!
//! - Read Query Builder instead of supplying raw queries
Expand Down
7 changes: 7 additions & 0 deletions src/query/consts.rs
Original file line number Diff line number Diff line change
@@ -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;
149 changes: 146 additions & 3 deletions src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -48,6 +60,47 @@ impl fmt::Display for Timestamp {
}
}

#[cfg(feature = "chrono_timestamps")]
impl Into<DateTime<Utc>> for Timestamp {
fn into(self) -> DateTime<Utc> {
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<T> From<DateTime<T>> for Timestamp
where
T: TimeZone,
{
fn from(date_time: DateTime<T>) -> 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),
Expand Down Expand Up @@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be feature-gated (see above)

#[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");
Expand All @@ -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<Utc> = 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<Utc> = 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<Utc> = 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<Utc> = 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<Utc> = 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<Utc> = 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<Utc> = 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
)
}
}