Ensure consistent timestamp precision
use chrono::DateTime;
use chrono::SecondsFormat;
+use chrono::Timelike;
use chrono::Utc;
use core::convert::TryFrom;
use core::fmt::Debug;
@@ -23,15 +24,15 @@ pub struct Timestamp(DateTime<Utc>);
impl Timestamp {
/// Parses a [`Timestamp`] from the provided input string.
pub fn parse(input: &str) -> Result<Self> {
- DateTime::parse_from_rfc3339(input)
- .map_err(Into::into)
- .map(Into::into)
- .map(Self)
+ let this: DateTime<Utc> = DateTime::parse_from_rfc3339(input)?.into();
+ let this: DateTime<Utc> = Self::truncate(this);
+
+ Ok(Self(this))
}
/// Creates a new [`Timestamp`] with the current date and time.
pub fn now() -> Self {
- Self(Utc::now())
+ Self(Self::truncate(Utc::now()))
}
/// Returns the [`Timestamp`] as a Unix timestamp.
@@ -43,6 +44,11 @@ impl Timestamp {
pub fn to_rfc3339(&self) -> String {
self.0.to_rfc3339_opts(SecondsFormat::Secs, true)
}
+
+ fn truncate(value: DateTime<Utc>) -> DateTime<Utc> {
+ // safe to unwrap because 0 is a valid nanosecond
+ value.with_nanosecond(0).unwrap()
+ }
}
impl Default for Timestamp {
@@ -96,6 +102,7 @@ impl FromStr for Timestamp {
#[cfg(test)]
mod tests {
use crate::common::Timestamp;
+ use crate::convert::AsJson;
#[test]
fn test_parse_valid() {
@@ -110,6 +117,15 @@ mod tests {
assert_eq!(timestamp.to_rfc3339(), original);
}
+ #[test]
+ fn test_parse_valid_truncated() {
+ let original = "1980-01-01T12:34:56.789Z";
+ let expected = "1980-01-01T12:34:56Z";
+ let timestamp = Timestamp::parse(original).unwrap();
+
+ assert_eq!(timestamp.to_rfc3339(), expected);
+ }
+
#[test]
#[should_panic = "InvalidTimestamp"]
fn test_parse_empty() {
@@ -127,4 +143,13 @@ mod tests {
fn test_parse_invalid_fmt() {
Timestamp::parse("2020/01/01 03:30:16").unwrap();
}
+
+ #[test]
+ fn test_json_roundtrip() {
+ let time1: Timestamp = Timestamp::now();
+ let json: Vec<u8> = time1.to_json_vec().unwrap();
+ let time2: Timestamp = Timestamp::from_json_slice(&json).unwrap();
+
+ assert_eq!(time1, time2);
+ }
}
