Skip to content
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

Add time crate support for Timestamp #151

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
8 changes: 4 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ jobs:
with:
components: clippy
- name: Check Clippy lints (reqwest)
run: cargo clippy --manifest-path influxdb/Cargo.toml --all-targets --no-default-features --features serde,derive,reqwest-client-rustls -- -D warnings
run: cargo clippy --manifest-path influxdb/Cargo.toml --all-targets --no-default-features --features chrono,time,serde,derive,reqwest-client-rustls -- -D warnings
- name: Check Clippy lints (surf)
run: cargo clippy --manifest-path influxdb/Cargo.toml --all-targets --no-default-features --features serde,derive,hyper-client -- -D warnings
run: cargo clippy --manifest-path influxdb/Cargo.toml --all-targets --no-default-features --features chrono,time,serde,derive,hyper-client -- -D warnings

# this checks that the code is formatted with rustfmt
rustfmt:
Expand Down Expand Up @@ -145,7 +145,7 @@ jobs:
run: |
for test in integration_tests{,_v2}
do
cargo test -p influxdb --no-default-features --features 'serde derive ${{matrix.http-backend}}' --no-fail-fast --test $test
cargo test -p influxdb --no-default-features --features 'chrono time serde derive ${{matrix.http-backend}}' --no-fail-fast --test $test
done

# this uses cargo-tarpaulin to inspect the code coverage
Expand Down Expand Up @@ -192,7 +192,7 @@ jobs:
cargo tarpaulin -v \
--target-dir target/tarpaulin \
--workspace \
--features serde,derive \
--features chrono,time,serde,derive \
--exclude-files 'derive/*' \
--exclude-files 'target/*' \
--ignore-panics --ignore-tests \
Expand Down
9 changes: 7 additions & 2 deletions influxdb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ include = ["src/**/*", "tests/**/*", "Cargo.toml", "LICENSE"]
repository = "https://github.com/influxdb-rs/influxdb-rust"

[dependencies]
chrono = { version = "0.4.23", features = ["serde"], default-features = false }
chrono = { version = "0.4.23", features = ["serde"], default-features = false, optional = true }
futures-util = "0.3.17"
http = "0.2.4"
influxdb_derive = { version = "0.5.1", optional = true }
lazy-regex = "3.1"
reqwest = { version = "0.11.4", default-features = false, optional = true }
surf = { version = "2.2.0", default-features = false, optional = true }
serde = { version = "1.0.186", optional = true }
serde_derive = { version = "1.0.186", optional = true }
serde_json = { version = "1.0.48", optional = true }
surf = { version = "2.2.0", default-features = false, optional = true }
thiserror = "1.0"
time = { version = "0.3.34", optional = true }

[features]
default = ["serde", "reqwest-client-rustls"]
Expand All @@ -40,6 +41,10 @@ reqwest-client-native-tls = ["reqwest", "reqwest/native-tls-alpn"]
reqwest-client-native-tls-vendored = ["reqwest", "reqwest/native-tls-vendored"]
wasm-client = ["surf", "surf/wasm-client"]

# etc
time = ["dep:time"]
chrono = ["dep:chrono"]

[dev-dependencies]
async-std = { version = "1.6.5", features = ["attributes", "tokio02", "tokio1"] }
indoc = "1.0"
Expand Down
85 changes: 52 additions & 33 deletions influxdb/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@
//! assert!(read_query.is_ok());
//! ```

use chrono::prelude::{DateTime, TimeZone, Utc};
use std::convert::TryInto;

pub mod consts;
mod line_proto_term;
pub mod read_query;
Expand All @@ -47,6 +44,21 @@ pub enum Timestamp {
Hours(u128),
}

impl Timestamp {
pub fn nanos(&self) -> u128 {
match self {
Timestamp::Hours(h) => {
h * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI
}
Timestamp::Minutes(m) => m * SECONDS_PER_MINUTE * MILLIS_PER_SECOND * NANOS_PER_MILLI,
Timestamp::Seconds(s) => s * MILLIS_PER_SECOND * NANOS_PER_MILLI,
Timestamp::Milliseconds(millis) => millis * NANOS_PER_MILLI,
Timestamp::Microseconds(micros) => micros * NANOS_PER_MICRO,
Timestamp::Nanoseconds(nanos) => *nanos,
}
}
}

impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Timestamp::*;
Expand All @@ -57,44 +69,38 @@ impl fmt::Display for Timestamp {
}
}

impl From<Timestamp> for DateTime<Utc> {
fn from(ts: Timestamp) -> DateTime<Utc> {
match ts {
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(micros) => {
let nanos = micros * NANOS_PER_MICRO;
Utc.timestamp_nanos(nanos.try_into().unwrap())
}
}
#[cfg(feature = "chrono")]
impl From<Timestamp> for chrono::prelude::DateTime<chrono::prelude::Utc> {
fn from(ts: Timestamp) -> chrono::prelude::DateTime<chrono::prelude::Utc> {
use chrono::prelude::TimeZone;
chrono::prelude::Utc.timestamp_nanos(ts.nanos() as i64)
}
}

impl<T> From<DateTime<T>> for Timestamp
#[cfg(feature = "chrono")]
impl<T> From<chrono::prelude::DateTime<T>> for Timestamp
where
T: TimeZone,
T: chrono::prelude::TimeZone,
{
fn from(date_time: DateTime<T>) -> Self {
fn from(date_time: chrono::prelude::DateTime<T>) -> Self {
Timestamp::Nanoseconds(date_time.timestamp_nanos_opt().unwrap() as u128)
}
}

#[cfg(feature = "time")]
impl From<Timestamp> for time::OffsetDateTime {
fn from(value: Timestamp) -> Self {
time::OffsetDateTime::from_unix_timestamp_nanos(value.nanos() as i128).unwrap()
}
}

#[cfg(feature = "time")]
impl From<time::OffsetDateTime> for Timestamp {
fn from(value: time::OffsetDateTime) -> Self {
Timestamp::Nanoseconds(value.unix_timestamp_nanos() as u128)
}
}

pub trait Query {
/// Builds valid InfluxSQL which can be run against the Database.
/// In case no fields have been specified, it will return an error,
Expand Down Expand Up @@ -235,7 +241,6 @@ mod tests {
MILLIS_PER_SECOND, MINUTES_PER_HOUR, NANOS_PER_MICRO, NANOS_PER_MILLI, SECONDS_PER_MINUTE,
};
use crate::query::{Timestamp, ValidQuery};
use chrono::prelude::{DateTime, TimeZone, Utc};
use std::convert::TryInto;
#[test]
fn test_equality_str() {
Expand All @@ -252,8 +257,10 @@ mod tests {
fn test_format_for_timestamp_else() {
assert!(format!("{}", Timestamp::Nanoseconds(100)) == "100");
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_hours() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Hours(2).into();
assert_eq!(
Utc.timestamp_nanos(
Expand All @@ -264,8 +271,10 @@ mod tests {
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_minutes() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Minutes(2).into();
assert_eq!(
Utc.timestamp_nanos(
Expand All @@ -276,8 +285,10 @@ mod tests {
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_seconds() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Seconds(2).into();
assert_eq!(
Utc.timestamp_nanos(
Expand All @@ -288,29 +299,37 @@ mod tests {
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_millis() {
use chrono::prelude::*;
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")]
#[test]
fn test_chrono_datetime_from_timestamp_nanos() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Nanoseconds(1).into();
assert_eq!(Utc.timestamp_nanos(1), datetime_from_timestamp)
}
#[cfg(feature = "chrono")]
#[test]
fn test_chrono_datetime_from_timestamp_micros() {
use chrono::prelude::*;
let datetime_from_timestamp: DateTime<Utc> = Timestamp::Microseconds(2).into();
assert_eq!(
Utc.timestamp_nanos((2 * NANOS_PER_MICRO).try_into().unwrap()),
datetime_from_timestamp
)
}
#[cfg(feature = "chrono")]
#[test]
fn test_timestamp_from_chrono_date() {
use chrono::prelude::*;
let timestamp_from_datetime: Timestamp = Utc
.with_ymd_and_hms(1970, 1, 1, 0, 0, 1)
.single()
Expand Down
Loading