From ebb77d59bc7aeab94f46efcf70759fbfe2d0830f Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Thu, 18 Jan 2018 09:06:23 -0600 Subject: [PATCH] Ensure that DateTime values are truncated to microsecond precision. (#522) r=emily --- core/Cargo.toml | 1 + core/src/lib.rs | 32 ++++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 82127015..02ecfef1 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.1" workspace = ".." [dependencies] +chrono = "0.4" enum-set = { git = "https://github.com/rnewman/enum-set" } lazy_static = "0.2" num = "0.1" diff --git a/core/src/lib.rs b/core/src/lib.rs index 77d4bc92..9ba41229 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -14,6 +14,7 @@ extern crate enum_set; extern crate lazy_static; extern crate ordered_float; +extern crate chrono; extern crate edn; extern crate uuid; @@ -36,8 +37,12 @@ use self::edn::{ pub use uuid::Uuid; -pub use edn::{ +pub use chrono::{ DateTime, + Timelike, // For truncation. +}; + +pub use edn::{ FromMicros, ToMicros, Utc, @@ -135,7 +140,7 @@ pub enum TypedValue { Boolean(bool), Long(i64), Double(OrderedFloat), - Instant(DateTime), + Instant(DateTime), // Use `into()` to ensure truncation. // TODO: &str throughout? String(Rc), Keyword(Rc), @@ -183,7 +188,7 @@ impl TypedValue { } pub fn current_instant() -> TypedValue { - TypedValue::Instant(Utc::now()) + Utc::now().into() } } @@ -195,9 +200,13 @@ impl From for TypedValue { } } +/// Truncate the provided `DateTime` to microsecond precision, and return the corresponding +/// `TypedValue::Instant`. impl From> for TypedValue { fn from(value: DateTime) -> TypedValue { - TypedValue::Instant(value) + let microseconds = value.nanosecond() / 1000; + let truncated = microseconds * 1000; + TypedValue::Instant(value.with_nanosecond(truncated).expect("valid timestamp")) } } @@ -668,6 +677,8 @@ impl Schema { mod test { use super::*; + use std::str::FromStr; + fn associate_ident(schema: &mut Schema, i: NamespacedKeyword, e: Entid) { schema.entid_map.insert(e, i.clone()); schema.ident_map.insert(i, e); @@ -722,6 +733,19 @@ mod test { assert!(attr3.flags() & AttributeBitFlags::UniqueValue as u8 != 0); } + #[test] + fn test_datetime_truncation() { + let dt: DateTime = DateTime::from_str("2018-01-11T00:34:09.273457004Z").expect("parsed"); + let expected: DateTime = DateTime::from_str("2018-01-11T00:34:09.273457Z").expect("parsed"); + + let tv: TypedValue = dt.into(); + if let TypedValue::Instant(roundtripped) = tv { + assert_eq!(roundtripped, expected); + } else { + panic!(); + } + } + #[test] fn test_as_edn_value() { let mut schema = Schema::default();