-
Notifications
You must be signed in to change notification settings - Fork 185
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
Update ixdtf
to handle unbound fraction length
#6036
base: main
Are you sure you want to change the base?
Conversation
ixdtf
to handle unbound fraction length
utils/ixdtf/src/parsers/records.rs
Outdated
pub enum Fraction { | ||
/// Parsed nanoseconds value (A fraction value from 1-9 digits length) | ||
Nanoseconds(u64), | ||
/// Parsed picoseconds value (A fraction value from 10-12 digits length) | ||
Picoseconds(u64), | ||
/// Parsed femtoseconds value (A fraction value from 12-15 digits length) | ||
Femtoseconds(u64), | ||
/// A parsed value truncated to nanoseconds (A fraction value of 15+ digits length) | ||
Truncated(u64), // An unbound fraction value truncated to nanoseconds | ||
} |
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.
Suggestion: change this to
pub struct Fraction {
fraction_digits: u8,
value: u64,
}
and then give it functions such as
impl Fraction {
pub fn try_to_nanoseconds(self) -> Option<u64> {
if self.fraction_digits <= 9 {
// compute the pow and return
} else {
None
}
}
pub fn to_truncated_nanoseconds(self) -> u64 {
// compute pow, either + or -
}
}
Another future advantage of this model is that the Fraction can directly return a FixedDecimal, which could make fractional second formatting more efficient in icu4x.
utils/ixdtf/src/error.rs
Outdated
@@ -46,6 +46,8 @@ pub enum ParseError { | |||
TimeSecond, | |||
#[displaydoc("Invalid character while parsing fraction part value.")] | |||
FractionPart, | |||
#[displaydoc("Fraction part value exceeds a representable range.")] | |||
InvalidFractionRange, |
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.
"Invalid" is not very descriptive. This is an error, that already tells me that something is not valid.
InvalidFractionRange, | |
ExcessiveSecondPrecision, |
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.
I was double checking this and the error is specific to a Duration hour fraction and minute fraction potentially exceeding the range of u64::MAX
when calculated. Maybe DurationFractionExceededRange
(this would then be moved down into the Duration errors).
Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com>
utils/ixdtf/src/parsers/records.rs
Outdated
pub fn to_nanoseconds(&self) -> Option<u32> { | ||
if self.digits <= 9 { | ||
10u32 | ||
.checked_pow(9 - u32::from(self.digits)) | ||
.map(|x| x * self.value as u32) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
/// Returns a `u64` representing the `Fraction` as it's computed | ||
/// nanosecond value, truncating any value beyond 9 digits to | ||
/// nanoseconds | ||
pub fn to_truncated_nanoseconds(&self) -> u64 { | ||
if self.digits <= 9 { | ||
self.value | ||
} else { | ||
self.value / 10u64.pow(u32::from(self.digits - 9)) | ||
} |
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.
Issue: Something here doesn't look right. The code for the self.digits <= 9
case should be the same in both functions I think. Please make sure this is tested.
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.
Huh, that's true. I think when I initially wrote that, it was with the idea of truncating on all cases, but that doesn't align with the function name.
I've updated the behavior and added some doc tests and tests for potentially malformed Fraction
s since the fields are public.
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.
Thought: kind-of wish the Fraction
type didn't have so many weird invariants. It is also a bit sad that we need to do so much checked arithmetic to get the nanoseconds out of it. There's probably still room to iterate on its design.
Note: the TC decided to start using the language "subsecond" instead of "fractional second" in icu_datetime
, but I don't know if that also applies to the ixdtf
crate, and even if it does, it shouldn't block merging this PR.
Agreed on the amount of checking that's needed for arithmetic. I wasn't super happy with it. It could probably be avoided by making I'm more inclined to refer to this as |
Maybe make And maybe keep |
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.
Made some changes to Fraction
by making the fields private (pub(crate)
) and updating digits
to be a NonZeroU8
.
I think this is cleaner than the previous version.
General question: should there be getters for digit
and value
?
.map(|fraction| { | ||
fraction | ||
.to_nanoseconds() | ||
.ok_or(ParseError::ExcessivePrecision) |
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.
The ExcessivePrecision
error could be remove here in favor of calling to_truncated_nanoseconds
if that were preferred.
Would also remove the transpose
below.
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.
Code looks good, however I'm not happy with the Fraction
naming. I think Subsecond
would be clearer, and it's a term we're introducing in icu_time
and icu_datetime
right now as well (#5999).
What terminology does the RFC use for fractional second / subsecond? |
Essentially the above would mean that "T1.5H" is parsed as To be fair, the name could be changed to |
Good point about this field being used for things other than seconds in the |
This PR updates
ixdtf
's fraction handling to parse fraction values beyond 9 digits in length per feedback from #6004.It introduces a new enum
Fraction
in place of the previousnanosecond
field that allows parsing fraction values beyond 9 digits while preserving the precision in the enum.It also adds a variety of tests to test the new behavior while adjusting tests from the old behavior to the new behavior