-
Notifications
You must be signed in to change notification settings - Fork 33
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
adds accessor methods for Timestamp
#482
Changes from 4 commits
01517e6
52ddcd1
249db37
1e8f911
8e6c891
f0b9ba7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -159,9 +159,6 @@ pub struct Timestamp { | |
pub(crate) fractional_seconds: Option<Mantissa>, | ||
} | ||
|
||
// 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<FixedOffset>`] to a Timestamp with the specified | ||
/// precision. If the precision is [`Precision::Second`], nanosecond precision (the maximum | ||
|
@@ -572,6 +569,102 @@ impl Timestamp { | |
pub fn precision(&self) -> Precision { | ||
self.precision | ||
} | ||
|
||
/// Returns the year that has been specified in the [Timestamp]. | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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::<FixedOffset>::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::<FixedOffset>::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::<FixedOffset>::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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you thought about returning an /// Returns the hour(s) that has been specified in the [Timestamp] if the timestamp's precision
/// includes hours. If you need hour, regardless of precision, use `hour().unwrap_or_default()`,
/// which will correctly return an hour of `0` when the precision does not include hours.
pub fn hour(&self) -> Option<u32> {
// ...
} I think it makes it match the data model a little better. It's a little more verbose when you always want some value, but it's a little more ergonomic when you only want to deal with the subfields that are included by the timestamp's precision. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good observation. I'm unsure which way to go on it; Ion I suspect that as a generalized datetime value, providing field defaults is probably what users would prefer. @jpschorr, what's your take on this? Would you rather check the timestamp's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. + 1. I think it would rather simple to check |
||
// 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::<FixedOffset>::from_utc(self.date_time, offset); | ||
return local_date_time.hour(); | ||
} | ||
self.date_time.hour() | ||
} | ||
|
||
/// 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 { | ||
// `NaiveDateTime#hours()` returns minutes normalized as per UTC | ||
// for local time we need to +/- the difference | ||
let local_date_time = DateTime::<FixedOffset>::from_utc(self.date_time, offset); | ||
return local_date_time.minute(); | ||
} | ||
self.date_time.minute() | ||
} | ||
|
||
/// 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 { | ||
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() | ||
} | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// 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() | ||
} | ||
} | ||
|
||
/// Formats an ISO-8601 timestamp of appropriate precision and offset. | ||
|
@@ -1584,6 +1677,158 @@ mod timestamp_tests { | |
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_timestamp_year() -> IonResult<()> { | ||
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); | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn test_timestamp_month() -> IonResult<()> { | ||
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(()) | ||
} | ||
|
||
#[test] | ||
fn test_timestamp_day() -> IonResult<()> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need some tests for the |
||
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); | ||
|
||
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] | ||
#[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<Timestamp>, | ||
#[case] expected_hours: u32, | ||
) -> IonResult<()> { | ||
assert_eq!(timestamp?.hour(), expected_hours); | ||
Ok(()) | ||
} | ||
|
||
#[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( | ||
#[case] timestamp: IonResult<Timestamp>, | ||
#[case] expected_minutes: u32, | ||
) -> IonResult<()> { | ||
assert_eq!(timestamp?.minute(), expected_minutes); | ||
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_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) | ||
.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); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[test] | ||
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.milliseconds(), 0); | ||
Ok(()) | ||
} | ||
|
||
#[test] | ||
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(()) | ||
} | ||
|
||
#[test] | ||
fn test_timestamp_fractional_seconds_scale() -> IonResult<()> { | ||
// Set fractional seconds as Decimal | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉