From 01517e6d027e5c8508f858ef5e2a6c6777c8baea Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Tue, 14 Mar 2023 14:24:04 -0700 Subject: [PATCH 1/4] adds accessors for Timestamp * adds year, month, day, hour, minute and second methods for given Timetsamp in local time. * adds utc_hour, utc_minute and utc_second methods for given Timestamp in UTC time. * adds unit tests for these accessor methods. --- src/types/timestamp.rs | 168 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/src/types/timestamp.rs b/src/types/timestamp.rs index 2cd171d7..bfb15309 100644 --- a/src/types/timestamp.rs +++ b/src/types/timestamp.rs @@ -572,6 +572,66 @@ impl Timestamp { pub fn precision(&self) -> Precision { self.precision } + + /// Returns the year that has been specified in the [Timestamp]. + pub fn year(&self) -> i32 { + self.date_time.year() + } + + /// Returns the month that has been specified in the [Timestamp]. + pub fn month(&self) -> u32 { + self.date_time.month() + } + + /// Returns the day that has been specified in the [Timestamp]. + pub fn day(&self) -> u32 { + self.date_time.day() + } + + /// Returns the hour(s) that has been specified in the [Timestamp]. + pub fn hour(&self) -> u32 { + // verify if the timestamp has an offset + if let Some(offset) = self.offset { + // `NativeDateTime#hours()` returns hours normalized as per UTC + // for local time we need to +/- the difference + return (self.date_time.hour() as i32 + (offset.local_minus_utc() / 3600)) as u32; + } + self.date_time.hour() + } + + /// Returns the minute(s) that has been specified in the [Timestamp]. + pub fn minute(&self) -> u32 { + // verify if the timestamp has an offset + if let Some(offset) = self.offset { + // `NativeDateTime#hours()` returns minutes normalized as per UTC + // for local time we need to +/- the difference + let local_minus_utc_minute = (offset.local_minus_utc() % 3600) / 60; + return (self.date_time.minute() as i32 + local_minus_utc_minute) as u32; + } + self.date_time.minute() + } + + /// Returns the second(s) that has been specified in the [Timestamp]. + pub fn second(&self) -> u32 { + self.date_time.second() + } + + /// Returns the UTC hour(s) that has been specified in the [Timestamp]. + pub fn utc_hour(&self) -> u32 { + self.date_time.hour() + } + + /// Returns the UTC minute(s) that has been specified in the [Timestamp]. + pub fn utc_minute(&self) -> u32 { + self.date_time.minute() + } + + /// Returns the UTC second(s) that has been specified in the [Timestamp]. + // this will be similar to `second` method because `Timestamp` creation takes minutes offset + // i.e. we can not put an offset with second precision and hence no conversion for offset would be needed + pub fn utc_second(&self) -> u32 { + self.date_time.second() + } } /// Formats an ISO-8601 timestamp of appropriate precision and offset. @@ -1584,6 +1644,114 @@ mod timestamp_tests { Ok(()) } + #[test] + fn test_timestamp_year() -> IonResult<()> { + let timestamp = Timestamp::with_year(2021).with_month(2).build()?; + assert_eq!(timestamp.year(), 2021); + Ok(()) + } + + #[test] + fn test_timestamp_month() -> IonResult<()> { + let timestamp = Timestamp::with_year(2021).with_month(2).build()?; + assert_eq!(timestamp.month(), 2); + Ok(()) + } + + #[test] + fn test_timestamp_day() -> IonResult<()> { + let timestamp_1 = Timestamp::with_year(2021).with_month(2).build()?; + assert_eq!(timestamp_1.day(), 1); + + let timestamp_2 = Timestamp::with_year(2021) + .with_month(2) + .with_day(4) + .build()?; + + assert_eq!(timestamp_2.day(), 4); + Ok(()) + } + + #[test] + fn test_timestamp_hour() -> IonResult<()> { + let timestamp_1 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; + assert_eq!(timestamp_1.hour(), 10); + + let timestamp_2 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; + assert_eq!(timestamp_2.hour(), 10); + + let timestamp_3 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; + assert_eq!(timestamp_3.hour(), 10); + Ok(()) + } + + #[test] + fn test_timestamp_minute() -> IonResult<()> { + let timestamp_1 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90)?; + assert_eq!(timestamp_1.minute(), 15); + + let timestamp_2 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; + assert_eq!(timestamp_2.minute(), 15); + + let timestamp_3 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; + assert_eq!(timestamp_3.minute(), 15); + + let timestamp_4 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; + assert_eq!(timestamp_4.minute(), 15); + Ok(()) + } + + #[test] + fn test_timestamp_second() -> IonResult<()> { + let timestamp = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; + assert_eq!(timestamp.second(), 30); + Ok(()) + } + + #[test] + fn test_timestamp_utc_hour() -> IonResult<()> { + let timestamp_1 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; + assert_eq!(timestamp_1.utc_hour(), 15); + + let timestamp_2 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; + assert_eq!(timestamp_2.utc_hour(), 5); + + let timestamp_3 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; + assert_eq!(timestamp_3.utc_hour(), 10); + Ok(()) + } + + #[test] + fn test_timestamp_utc_minute() -> IonResult<()> { + let timestamp_1 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90)?; + assert_eq!(timestamp_1.utc_minute(), 45); + + let timestamp_2 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; + assert_eq!(timestamp_2.utc_minute(), 15); + + let timestamp_3 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; + assert_eq!(timestamp_3.utc_minute(), 15); + + let timestamp_4 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; + assert_eq!(timestamp_4.utc_minute(), 15); + Ok(()) + } + + #[test] + fn test_timestamp_utc_second() -> IonResult<()> { + let timestamp = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; + assert_eq!(timestamp.utc_second(), 30); + Ok(()) + } + #[test] fn test_timestamp_fractional_seconds_scale() -> IonResult<()> { // Set fractional seconds as Decimal From 249db3764cebba0c07ad28fc47aa68a8898971d1 Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Fri, 17 Mar 2023 13:28:09 -0700 Subject: [PATCH 2/4] adds suggested changes --- src/types/timestamp.rs | 99 ++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/src/types/timestamp.rs b/src/types/timestamp.rs index 935d1f32..0fb58b4e 100644 --- a/src/types/timestamp.rs +++ b/src/types/timestamp.rs @@ -6,7 +6,7 @@ use crate::types::coefficient::Sign::Negative; use crate::types::decimal::Decimal; use crate::types::integer::UInt; use chrono::{ - DateTime, Datelike, FixedOffset, LocalResult, NaiveDate, NaiveDateTime, TimeZone, Timelike, + DateTime, Datelike, FixedOffset, LocalResult, NaiveDate, NaiveDateTime, TimeZone, Timelike, Utc, }; use num_bigint::BigUint; use num_traits::ToPrimitive; @@ -234,7 +234,7 @@ impl Timestamp { /// NOTE: This is a potentially lossy operation. A Timestamp with picoseconds would return a /// number of nanoseconds, losing precision. Similarly, a Timestamp with milliseconds would /// also return a number of nanoseconds, erroneously gaining precision. - fn fractional_seconds_as_nanoseconds(&self) -> Option { + pub fn fractional_seconds_as_nanoseconds(&self) -> Option { // This function is used when converting a Timestamp to a DateTime or // NaiveDateTime. use Mantissa::*; @@ -594,7 +594,8 @@ impl Timestamp { if let Some(offset) = self.offset { // `NativeDateTime#hours()` returns hours normalized as per UTC // for local time we need to +/- the difference - return (self.date_time.hour() as i32 + (offset.local_minus_utc() / 3600)) as u32; + let local_date_time = DateTime::::from_utc(self.date_time, offset); + return local_date_time.hour(); } self.date_time.hour() } @@ -605,8 +606,8 @@ impl Timestamp { if let Some(offset) = self.offset { // `NativeDateTime#hours()` returns minutes normalized as per UTC // for local time we need to +/- the difference - let local_minus_utc_minute = (offset.local_minus_utc() % 3600) / 60; - return (self.date_time.minute() as i32 + local_minus_utc_minute) as u32; + let local_date_time = DateTime::::from_utc(self.date_time, offset); + return local_date_time.minute(); } self.date_time.minute() } @@ -616,21 +617,10 @@ impl Timestamp { self.date_time.second() } - /// Returns the UTC hour(s) that has been specified in the [Timestamp]. - pub fn utc_hour(&self) -> u32 { - self.date_time.hour() - } - - /// Returns the UTC minute(s) that has been specified in the [Timestamp]. - pub fn utc_minute(&self) -> u32 { - self.date_time.minute() - } - - /// Returns the UTC second(s) that has been specified in the [Timestamp]. - // this will be similar to `second` method because `Timestamp` creation takes minutes offset - // i.e. we can not put an offset with second precision and hence no conversion for offset would be needed - pub fn utc_second(&self) -> u32 { - self.date_time.second() + /// Return a UTC timestamp for this [Timestamp] + pub fn to_utc(&self) -> Timestamp { + let utc_date_time = DateTime::::from_utc(self.date_time, Utc); + utc_date_time.naive_utc().into() } } @@ -1672,36 +1662,39 @@ mod timestamp_tests { Ok(()) } - #[test] - fn test_timestamp_hour() -> IonResult<()> { - let timestamp_1 = - Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; - assert_eq!(timestamp_1.hour(), 10); - - let timestamp_2 = - Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; - assert_eq!(timestamp_2.hour(), 10); + #[rstest( + timestamp, expected_hours, + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90), 10), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60), 10), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60), 10), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(15), 10), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(30), 10), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0), 10), + case(Timestamp::with_ymd_hms(2021, 4, 6, 0, 15, 30).build_at_offset(5 * 60), 0), + case(Timestamp::with_ymd_hms(2021, 4, 6, 23, 15, 30).build_at_offset(-5 * 60), 23), + case(Timestamp::with_ymd_hms(2021, 4, 6, 0, 15, 30).build_at_offset(23 * 60), 0) + )] + fn test_timestamp_hour(timestamp: IonResult, expected_hours: u32) -> IonResult<()> { + assert_eq!(timestamp?.hour(), expected_hours); - let timestamp_3 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; - assert_eq!(timestamp_3.hour(), 10); Ok(()) } - #[test] - fn test_timestamp_minute() -> IonResult<()> { - let timestamp_1 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90)?; - assert_eq!(timestamp_1.minute(), 15); - - let timestamp_2 = - Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; - assert_eq!(timestamp_2.minute(), 15); - - let timestamp_3 = - Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; - assert_eq!(timestamp_3.minute(), 15); + #[rstest( + timestamp, expected_minutes, + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90), 15), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60), 15), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60), 15), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0), 15), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 0, 30).build_at_offset(5 * 60), 0), + case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 59, 30).build_at_offset(5 * 60), 59) + )] + fn test_timestamp_minute( + timestamp: IonResult, + expected_minutes: u32, + ) -> IonResult<()> { + assert_eq!(timestamp?.minute(), expected_minutes); - let timestamp_4 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; - assert_eq!(timestamp_4.minute(), 15); Ok(()) } @@ -1716,39 +1709,39 @@ mod timestamp_tests { fn test_timestamp_utc_hour() -> IonResult<()> { let timestamp_1 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; - assert_eq!(timestamp_1.utc_hour(), 15); + assert_eq!(timestamp_1.to_utc().hour(), 15); let timestamp_2 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; - assert_eq!(timestamp_2.utc_hour(), 5); + assert_eq!(timestamp_2.to_utc().hour(), 5); let timestamp_3 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; - assert_eq!(timestamp_3.utc_hour(), 10); + assert_eq!(timestamp_3.to_utc().hour(), 10); Ok(()) } #[test] fn test_timestamp_utc_minute() -> IonResult<()> { let timestamp_1 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90)?; - assert_eq!(timestamp_1.utc_minute(), 45); + assert_eq!(timestamp_1.to_utc().minute(), 45); let timestamp_2 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; - assert_eq!(timestamp_2.utc_minute(), 15); + assert_eq!(timestamp_2.to_utc().minute(), 15); let timestamp_3 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; - assert_eq!(timestamp_3.utc_minute(), 15); + assert_eq!(timestamp_3.to_utc().minute(), 15); let timestamp_4 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; - assert_eq!(timestamp_4.utc_minute(), 15); + assert_eq!(timestamp_4.to_utc().minute(), 15); Ok(()) } #[test] fn test_timestamp_utc_second() -> IonResult<()> { let timestamp = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; - assert_eq!(timestamp.utc_second(), 30); + assert_eq!(timestamp.to_utc().second(), 30); Ok(()) } From 1e8f911aa6289c29ec6d3798bdadf6aa87d64599 Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Fri, 17 Mar 2023 15:34:20 -0700 Subject: [PATCH 3/4] adds more tests and doc comment changes --- src/types/timestamp.rs | 208 +++++++++++++++++++++++++++++------------ 1 file changed, 146 insertions(+), 62 deletions(-) diff --git a/src/types/timestamp.rs b/src/types/timestamp.rs index 0fb58b4e..f468f630 100644 --- a/src/types/timestamp.rs +++ b/src/types/timestamp.rs @@ -6,7 +6,7 @@ use crate::types::coefficient::Sign::Negative; use crate::types::decimal::Decimal; use crate::types::integer::UInt; use chrono::{ - DateTime, Datelike, FixedOffset, LocalResult, NaiveDate, NaiveDateTime, TimeZone, Timelike, Utc, + DateTime, Datelike, FixedOffset, LocalResult, NaiveDate, NaiveDateTime, TimeZone, Timelike, }; use num_bigint::BigUint; use num_traits::ToPrimitive; @@ -159,9 +159,6 @@ pub struct Timestamp { pub(crate) fractional_seconds: Option, } -// TODO: Timestamp does not yet provide useful accessors for its individual fields. It can be -// instantiated and tested for equality, but will not very useful as a general purpose -// datetime until these methods are added. impl Timestamp { /// Converts a [`NaiveDateTime`] or [`DateTime`] to a Timestamp with the specified /// precision. If the precision is [`Precision::Second`], nanosecond precision (the maximum @@ -234,7 +231,7 @@ impl Timestamp { /// NOTE: This is a potentially lossy operation. A Timestamp with picoseconds would return a /// number of nanoseconds, losing precision. Similarly, a Timestamp with milliseconds would /// also return a number of nanoseconds, erroneously gaining precision. - pub fn fractional_seconds_as_nanoseconds(&self) -> Option { + fn fractional_seconds_as_nanoseconds(&self) -> Option { // This function is used when converting a Timestamp to a DateTime or // NaiveDateTime. use Mantissa::*; @@ -575,24 +572,50 @@ impl Timestamp { /// Returns the year that has been specified in the [Timestamp]. pub fn year(&self) -> i32 { + // verify if the timestamp has an offset + if let Some(offset) = self.offset { + // `NaiveDateTime#hours()` returns hours normalized as per UTC + // for local time we need to +/- the difference + let local_date_time = DateTime::::from_utc(self.date_time, offset); + return local_date_time.year(); + } self.date_time.year() } /// Returns the month that has been specified in the [Timestamp]. + /// Returns the month number starting from 1. + /// The return value ranges from 1 to 12. pub fn month(&self) -> u32 { + // verify if the timestamp has an offset + if let Some(offset) = self.offset { + // `NaiveDateTime#hours()` returns hours normalized as per UTC + // for local time we need to +/- the difference + let local_date_time = DateTime::::from_utc(self.date_time, offset); + return local_date_time.month(); + } self.date_time.month() } /// Returns the day that has been specified in the [Timestamp]. + /// Returns the day of month starting from 1. + // The return value ranges from 1 to 31. (The last day of month differs by months.) pub fn day(&self) -> u32 { + // verify if the timestamp has an offset + if let Some(offset) = self.offset { + // `NaiveDateTime#hours()` returns hours normalized as per UTC + // for local time we need to +/- the difference + let local_date_time = DateTime::::from_utc(self.date_time, offset); + return local_date_time.day(); + } self.date_time.day() } /// Returns the hour(s) that has been specified in the [Timestamp]. + /// Returns the hour number from 0 to 23. pub fn hour(&self) -> u32 { // verify if the timestamp has an offset if let Some(offset) = self.offset { - // `NativeDateTime#hours()` returns hours normalized as per UTC + // `NaiveDateTime#hours()` returns hours normalized as per UTC // for local time we need to +/- the difference let local_date_time = DateTime::::from_utc(self.date_time, offset); return local_date_time.hour(); @@ -601,10 +624,11 @@ impl Timestamp { } /// Returns the minute(s) that has been specified in the [Timestamp]. + /// Returns the minute number from 0 to 59. pub fn minute(&self) -> u32 { // verify if the timestamp has an offset if let Some(offset) = self.offset { - // `NativeDateTime#hours()` returns minutes normalized as per UTC + // `NaiveDateTime#hours()` returns minutes normalized as per UTC // for local time we need to +/- the difference let local_date_time = DateTime::::from_utc(self.date_time, offset); return local_date_time.minute(); @@ -613,14 +637,33 @@ impl Timestamp { } /// Returns the second(s) that has been specified in the [Timestamp]. + /// Returns the second number from 0 to 59. pub fn second(&self) -> u32 { self.date_time.second() } /// Return a UTC timestamp for this [Timestamp] pub fn to_utc(&self) -> Timestamp { - let utc_date_time = DateTime::::from_utc(self.date_time, Utc); - utc_date_time.naive_utc().into() + self.date_time.into() + } + + /// Returns this Timestamp's fractional seconds in nanoseconds + /// + /// NOTE: This is a potentially lossy operation. A Timestamp with picoseconds would return a + /// number of nanoseconds, losing precision. Similarly, a Timestamp with milliseconds would + /// also return a number of nanoseconds, erroneously gaining precision. + pub fn nanoseconds(&self) -> u32 { + self.fractional_seconds_as_nanoseconds().unwrap_or_default() + } + + /// Returns this Timestamp's fractional seconds in milliseconds + /// + /// NOTE: This is a potentially lossy operation. A Timestamp with picoseconds would return a + /// number of milliseconds, losing precision. + pub fn milliseconds(&self) -> u32 { + self.fractional_seconds_as_nanoseconds() + .map(|s| s / 1000000) + .unwrap_or_default() } } @@ -1636,15 +1679,37 @@ mod timestamp_tests { #[test] fn test_timestamp_year() -> IonResult<()> { - let timestamp = Timestamp::with_year(2021).with_month(2).build()?; - assert_eq!(timestamp.year(), 2021); + let timestamp_1 = Timestamp::with_year(2021).with_month(2).build()?; + assert_eq!(timestamp_1.year(), 2021); + + let timestamp_2 = + Timestamp::with_ymd_hms(2021, 12, 31, 10, 15, 30).build_at_offset(-11 * 60)?; + + assert_eq!(timestamp_2.month(), 12); + + let timestamp_3 = + Timestamp::with_ymd_hms(2021, 12, 31, 15, 15, 30).build_at_offset(10 * 60)?; + + assert_eq!(timestamp_3.month(), 12); + Ok(()) } #[test] fn test_timestamp_month() -> IonResult<()> { - let timestamp = Timestamp::with_year(2021).with_month(2).build()?; - assert_eq!(timestamp.month(), 2); + let timestamp_1 = Timestamp::with_year(2021).with_month(2).build()?; + assert_eq!(timestamp_1.month(), 2); + + let timestamp_2 = + Timestamp::with_ymd_hms(2021, 1, 31, 10, 15, 30).build_at_offset(-11 * 60)?; + + assert_eq!(timestamp_2.month(), 1); + + let timestamp_3 = + Timestamp::with_ymd_hms(2021, 1, 31, 15, 15, 30).build_at_offset(10 * 60)?; + + assert_eq!(timestamp_3.month(), 1); + Ok(()) } @@ -1659,42 +1724,54 @@ mod timestamp_tests { .build()?; assert_eq!(timestamp_2.day(), 4); + + let timestamp_3 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-11 * 60)?; + + assert_eq!(timestamp_3.day(), 6); + + let timestamp_4 = + Timestamp::with_ymd_hms(2021, 4, 6, 15, 15, 30).build_at_offset(10 * 60)?; + + assert_eq!(timestamp_4.day(), 6); + Ok(()) } - #[rstest( - timestamp, expected_hours, - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90), 10), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60), 10), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60), 10), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(15), 10), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(30), 10), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0), 10), - case(Timestamp::with_ymd_hms(2021, 4, 6, 0, 15, 30).build_at_offset(5 * 60), 0), - case(Timestamp::with_ymd_hms(2021, 4, 6, 23, 15, 30).build_at_offset(-5 * 60), 23), - case(Timestamp::with_ymd_hms(2021, 4, 6, 0, 15, 30).build_at_offset(23 * 60), 0) - )] - fn test_timestamp_hour(timestamp: IonResult, expected_hours: u32) -> IonResult<()> { + #[rstest] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90), 10)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60), 10)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60), 10)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(15), 10)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(30), 10)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0), 10)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 0, 15, 30).build_at_offset(5 * 60), 0)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 23, 15, 30).build_at_offset(-5 * 60), 23)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 0, 15, 30).build_at_offset(23 * 60), 0)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-11 * 60), 10)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 15, 15, 30).build_at_offset(10 * 60), 15)] + fn test_timestamp_hour( + #[case] timestamp: IonResult, + #[case] expected_hours: u32, + ) -> IonResult<()> { assert_eq!(timestamp?.hour(), expected_hours); - Ok(()) } - #[rstest( - timestamp, expected_minutes, - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90), 15), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60), 15), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60), 15), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0), 15), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 0, 30).build_at_offset(5 * 60), 0), - case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 59, 30).build_at_offset(5 * 60), 59) - )] + #[rstest] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90), 15)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60), 15)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60), 15)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0), 15)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 0, 30).build_at_offset(5 * 60), 0)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 59, 30).build_at_offset(5 * 60), 59)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-11 * 60), 15)] + #[case(Timestamp::with_ymd_hms(2021, 4, 6, 15, 15, 30).build_at_offset(10 * 60), 15)] fn test_timestamp_minute( - timestamp: IonResult, - expected_minutes: u32, + #[case] timestamp: IonResult, + #[case] expected_minutes: u32, ) -> IonResult<()> { assert_eq!(timestamp?.minute(), expected_minutes); - Ok(()) } @@ -1706,42 +1783,49 @@ mod timestamp_tests { } #[test] - fn test_timestamp_utc_hour() -> IonResult<()> { - let timestamp_1 = - Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; - assert_eq!(timestamp_1.to_utc().hour(), 15); + fn test_timestamp_nanoseconds() -> IonResult<()> { + let timestamp_1 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30) + .with_nanoseconds(192) + .build_at_offset(-5 * 60)?; + assert_eq!(timestamp_1.nanoseconds(), 192); - let timestamp_2 = - Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; - assert_eq!(timestamp_2.to_utc().hour(), 5); + let timestamp_2 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30) + .with_milliseconds(192) + .build_at_offset(-5 * 60)?; + assert_eq!(timestamp_2.nanoseconds(), 192000000); + + let timestamp_3 = + Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; + assert_eq!(timestamp_3.nanoseconds(), 0); - let timestamp_3 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; - assert_eq!(timestamp_3.to_utc().hour(), 10); Ok(()) } #[test] - fn test_timestamp_utc_minute() -> IonResult<()> { - let timestamp_1 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-90)?; - assert_eq!(timestamp_1.to_utc().minute(), 45); + fn test_timestamp_milliseconds() -> IonResult<()> { + let timestamp_1 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30) + .with_milliseconds(192) + .build_at_offset(-5 * 60)?; + assert_eq!(timestamp_1.milliseconds(), 192); let timestamp_2 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; - assert_eq!(timestamp_2.to_utc().minute(), 15); - - let timestamp_3 = - Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(5 * 60)?; - assert_eq!(timestamp_3.to_utc().minute(), 15); - - let timestamp_4 = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(0)?; - assert_eq!(timestamp_4.to_utc().minute(), 15); + assert_eq!(timestamp_2.milliseconds(), 0); Ok(()) } #[test] - fn test_timestamp_utc_second() -> IonResult<()> { - let timestamp = Timestamp::with_ymd_hms(2021, 4, 6, 10, 15, 30).build_at_offset(-5 * 60)?; - assert_eq!(timestamp.to_utc().second(), 30); + fn test_timestamp_to_utc() -> IonResult<()> { + let new_years_eve_nyc = + Timestamp::with_ymd_hms(2022, 12, 31, 23, 59, 00).build_at_offset(-5 * 60)?; + + let london = new_years_eve_nyc.to_utc(); + assert_eq!(london.year(), 2023); + assert_eq!(london.month(), 1); + assert_eq!(london.day(), 1); + assert_eq!(london.hour(), 4); + assert_eq!(london.minute(), 59); + assert_eq!(london.second(), 0); Ok(()) } From 8e6c8916c566a6cf9a7fb0ecaa8f8d5fe2f81f4e Mon Sep 17 00:00:00 2001 From: Khushboo Desai Date: Fri, 17 Mar 2023 16:09:48 -0700 Subject: [PATCH 4/4] add more suggested changes --- src/types/timestamp.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/types/timestamp.rs b/src/types/timestamp.rs index f468f630..a6227e88 100644 --- a/src/types/timestamp.rs +++ b/src/types/timestamp.rs @@ -650,8 +650,8 @@ impl Timestamp { /// Returns this Timestamp's fractional seconds in nanoseconds /// /// NOTE: This is a potentially lossy operation. A Timestamp with picoseconds would return a - /// number of nanoseconds, losing precision. Similarly, a Timestamp with milliseconds would - /// also return a number of nanoseconds, erroneously gaining precision. + /// number of nanoseconds, losing precision. If it loses precision then truncation is preformed. + /// (e.g. a timestamp with fractional seconds of `0.000000000999` would be returned as `0`) pub fn nanoseconds(&self) -> u32 { self.fractional_seconds_as_nanoseconds().unwrap_or_default() } @@ -659,7 +659,8 @@ impl Timestamp { /// Returns this Timestamp's fractional seconds in milliseconds /// /// NOTE: This is a potentially lossy operation. A Timestamp with picoseconds would return a - /// number of milliseconds, losing precision. + /// number of milliseconds, losing precision. If it loses precision then truncation is preformed. + /// (e.g. a timestamp with fractional seconds of `0.000999` would be returned as `0`) pub fn milliseconds(&self) -> u32 { self.fractional_seconds_as_nanoseconds() .map(|s| s / 1000000) @@ -1685,12 +1686,12 @@ mod timestamp_tests { let timestamp_2 = Timestamp::with_ymd_hms(2021, 12, 31, 10, 15, 30).build_at_offset(-11 * 60)?; - assert_eq!(timestamp_2.month(), 12); + assert_eq!(timestamp_2.year(), 2021); let timestamp_3 = Timestamp::with_ymd_hms(2021, 12, 31, 15, 15, 30).build_at_offset(10 * 60)?; - assert_eq!(timestamp_3.month(), 12); + assert_eq!(timestamp_3.year(), 2021); Ok(()) }