diff --git a/abci/Cargo.toml b/abci/Cargo.toml index 42267472..b6f5390a 100644 --- a/abci/Cargo.toml +++ b/abci/Cargo.toml @@ -37,7 +37,7 @@ tracing-subscriber = { version = "0.3", optional = true, default-features = fals "ansi", "env-filter", ] } -thiserror = "1.0.39" +thiserror = { version = "1.0.39" } url = { version = "2.3.1" } semver = { version = "1.0.17" } lhash = { version = "1.0.1", features = ["sha256"], optional = true } @@ -56,9 +56,9 @@ tokio = { version = "1.28", features = [ futures = { version = "0.3.28", optional = true } [dev-dependencies] -anyhow = "1.0.69" -bincode = "2.0.0-rc.2" -blake2 = "0.10.6" +anyhow = { version = "1.0.69" } +bincode = { version = "2.0.0-rc.2" } +blake2 = { version = "0.10.6" } bollard = { version = "0.14.0" } futures = { version = "0.3.26" } tokio = { version = "1", features = ["macros", "signal", "time", "io-std"] } diff --git a/proto/src/serializers/timestamp.rs b/proto/src/serializers/timestamp.rs index 84f1d371..fc0a5f3a 100644 --- a/proto/src/serializers/timestamp.rs +++ b/proto/src/serializers/timestamp.rs @@ -1,6 +1,6 @@ //! Serialize/deserialize Timestamp type from and into string: -use core::fmt; +use core::fmt::{self, Debug}; use serde::{de::Error as _, ser::Error, Deserialize, Deserializer, Serialize, Serializer}; use time::{ @@ -29,6 +29,16 @@ impl From for Timestamp { pub trait ToMilis { /// Convert protobuf timestamp into miliseconds since epoch + + /// Note there is a resolution difference, as timestamp uses nanoseconds + /// + /// # Arguments + /// + /// * millis - time since epoch, in miliseconds + /// + /// # Panics + /// + /// Panics when timestamp doesn't fit `u64` type fn to_milis(&self) -> u64; } @@ -37,7 +47,43 @@ impl ToMilis for Timestamp { fn to_milis(&self) -> u64 { chrono::NaiveDateTime::from_timestamp_opt(self.seconds, self.nanos as u32) .unwrap() - .timestamp_millis() as u64 + .timestamp_millis() + .try_into() + .expect("timestamp value out of u64 range") + } +} + +pub trait FromMilis { + /// Create protobuf timestamp from miliseconds since epoch + /// + /// Note there is a resolution difference, as timestamp uses nanoseconds + /// + /// # Arguments + /// + /// * millis - time since epoch, in miliseconds; must fit `i64` type + fn from_milis(millis: u64) -> Self; +} + +impl FromMilis for Timestamp { + /// Create protobuf timestamp from miliseconds since epoch + /// + /// Note there is a resolution difference, as timestamp uses nanoseconds + /// + /// # Panics + /// + /// Panics when `millis` don't fit `i64` type + fn from_milis(millis: u64) -> Self { + let dt = chrono::NaiveDateTime::from_timestamp_millis( + millis + .try_into() + .expect("milliseconds timestamp out of i64 range"), + ) + .expect("cannot parse timestamp"); + + Self { + nanos: dt.timestamp_subsec_nanos() as i32, + seconds: dt.timestamp(), + } } } @@ -207,4 +253,33 @@ mod test { assert_eq!(json, serde_json::to_string(&rfc).unwrap()); } } + + #[test] + fn timestamp_from_to() { + let time_ms = 1687848809533; + + let from = Timestamp::from_milis(time_ms); + let to = from.to_milis(); + + assert_eq!(to, time_ms); + } + + #[test] + #[should_panic] + fn timestamp_millis_out_of_range() { + let time_ms = u64::MAX - 1; + + let from = Timestamp::from_milis(time_ms); + } + + #[test] + #[should_panic] + fn timestamp_negative() { + let ts = Timestamp { + nanos: 1000, + seconds: -12, + }; + + let to = ts.to_milis(); + } }