-
Notifications
You must be signed in to change notification settings - Fork 22
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
Add formatting for durations e.g. "2h 4min. 2sec." #56
Add formatting for durations e.g. "2h 4min. 2sec." #56
Conversation
2813a39
to
be125e5
Compare
Update - new implementation! I took some inspiration from the Intl js proposal (https://github.com/tc39/proposal-intl-duration-format). Basically, now, instead of taking a number of seconds, it takes a hash specifying The The What I did not implement from the javascript side of things:
Some examples!
|
is looking good, could you add:
{hours: 25, minutes: 61}
{hours: 0, minutes: 2} |
Yowch. Negative durations. Going to make a whole separate comment about that... So firstly, it's possible to construct a hash like Now, what to do when all parts of the duration are negative? That is how the JS Temporal object represents a negative duration:
As written, this PR would emit the following:
i.e. each component is individually formatted as a negative number, which is a bit silly IMO. This was discussed in the duration formatting spec here: tc39/proposal-intl-duration-format#29. They concluded that the requirements for formatting a negative duration were:
However there was also some suggestion that CLDR should be aware of this:
But, CLDR doesn't have any data for doing this differently as far as I can tell. It would have to be part of the list format , and it's not - https://github.com/unicode-org/cldr/blob/208411e0dd2562db70d67b8d75dc7e8ed4ce30a8/common/main/en.xml#L8942. There would need to be a pattern like this defined, and there isn't:
And also (This problem isn't unique to time, by the way - I think exactly the same issue would crop up if we wanted to display a quantity like "-3m 20cm" for example) So, since this is tricky, and the correct CLDR/ICU support isn't there for doing this properly, I just made this formatter throw exceptions if any component of the duration is negative. |
The other questions:
It doesn't do any balancing (and neither does the Intl one: tc39/proposal-intl-duration-format#10) so it'll print exactly that:
In Ruby-land, I think one would use something like
Anyway, a long way of saying "Not My Problem" 😂
Ignores the zero parts, as per the js spec (and there are tests for this):
Interesting. I wonder what it currently outputs.
To my uninformed eye it looks like it's doing the "2" vs "lots" plurals correctly (минуты vs минут) but I'll try and go poke a russian speaker and see what they think. |
Why is it not converting 7.3 hours in
|
It seems in the Intl DurationFormatter spec no consensus had been reached about how to format fractional digits for anything > seconds: tc39/proposal-intl-duration-format#89, tc39/proposal-intl-duration-format#65 I think the reason not to convert 7.3 hours -> 7 hours, 18 minutes is that everything > seconds are calendar units - we add leap seconds to days to correct for the slowing of the earth's rotation, for example, so the precise number of seconds in 7.3 hours actually depends on whether this day has a leap second in it or not. Now, perhaps we could just format it as "2 days, 7.3 hours, 40.9 minutes, 0.43 seconds", but the DurationFormatter spec explicitly doesn't define anything about fractionalDigits for anything > seconds. So I figured the most sensible behaviour was to just truncate off any fractional parts in > seconds. I could also just make it raise an exception if we think that's a better idea. |
BTW @aurelieverrot if you want the balancing behaviour of 7.3 hours -> 7 hours, 18 minutes, I think ActiveSupport::Duration can do that for you:
But i think the reason for punting this to application code is that it needs to make choices about how to handle uneven months, leap seconds, etc etc. For example, ActiveSupport makes the following choices for you:
But different applications might want more sophisticaed handling. |
Oh so we could pass the result of |
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.
Nice, I will see what can I do with Travis.
list_join_format = STYLES_TO_LIST_JOIN_FORMAT.fetch(style) | ||
@list_formatter = FFI::AutoPointer.new( | ||
Lib.check_error { |error| | ||
Lib.ulistfmt_openForType(@locale, :units, list_join_format, error) |
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.
What do we translate this :units
to?
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 don't quite follow your question, sorry. :units
here refers to the constant ULISTFMT_TYPE_UNITS
, which is for formatting lists of quantities like "5 pounds, 12 ounces".
This list formatter gets used at the end of #format
to glue all the duration components together
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.
Ok, it looks good.
Uses unumf_* to format the individual components of the duration, and ulistfmt_* to join them. That's roughly analogous with what the C++ MeasureFormatter class does internally. I asked about some feedback for this approach on the ICU mailing list too: https://sourceforge.net/p/icu/mailman/message/37645785/
dc811fd
to
8daded6
Compare
Thanks. I will release it soon. |
Uses unumf_* to format the individual components of the duration, and
ulistfmt_* to join them. That's roughly analogous with what the C++
MeasureFormatter class does internally.
I asked about some feedback for this approach on the ICU mailing list
too: https://sourceforge.net/p/icu/mailman/message/37645785/
Annoyingly, this won't work on ICU < 67 because it needs
UNumberFormatter
andUListFormatter
, so of course CI isn't actually running any of the tests I wrote. But they pass, I promise. If you like I wonder if I should put up a PR to make travis run the tests on a newer ICU as well.