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
Options for smallestUnit/largestUnit and hideZeroValued #32
Comments
@younies ... TODO: add all the use cases and how this will be affected with each option. |
For Option 1: For example: For Option 2 For Option 3 |
Is the hiding of interior zeros the only case that options 1 and 2 are unable to handle? |
Can you please post specific code examples of operations that you can do with options 3 and 4 that you can't do with options 1 and 2? |
Option 5:
|
I do not think there is a case that could done by |
Can you please post examples with code? Show me example duration inputs and outputs, and how to produce each output given each of the four options. |
Okay, lets say - Option 1: duration.round({ smallestUnit: "seconds", largestUnit: "hours" }).toLocaleString({ smallestUnit: "seconds" }) and the output will be
- Option 2: duration.toLocaleString({ smallestUnit: "seconds", largestUnit: "hours" }) the output will be
- Option 3: duration.toLocaleString({ smallestUnit: "seconds", largestUnit: "hours", hideZeroValued: "all" }) the output will be
- Option 4: duration.round({ smallestUnit: "seconds", largestUnit: "hours" })
.toLocaleString({ requiredFields: ["hours", "seconds"] }) the output would be
- Option 5: duration.round({ smallestUnit: "seconds", largestUnit: "hours" }).toLocaleString({ hideZeroValue: "all" }) the output would be
|
Regardless of which Per @sffc, the current plan is for format(duration, options) {
if (!isDuration(duration)) duration = Temporal.Duration.from(duration);
return duration.toLocaleString(options);
} My suggestion would be to reverse this flow and make toLocaleString(options) {
const { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = this;
const nonZero = Object.entries({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds })
.filter(e => e[1]);
const toDisplay = Object.fromEntries(nonZero.length ? nonZero : { seconds: 0 });
return new Intl.DurationFormat(options).format(toDisplay);
}
format(duration, options) {
if (isDuration(duration)) return duration.toLocaleString(options);
if (typeof(duration) !== 'object') throw new TypeError('Object required');
// format the provided object bag
} If this is available, then the uncommon "arbitrary set of fields" case that justifies Option 4 could be handled in userland without needing to offer a const duration = Temporal.Duration.from('PT30M');
const {hours, minutes, seconds} = duration;
new Intl.DurationFormat().format({hours, minutes, seconds});
// => 0 hours, 30 minutes, 0 seconds
const nonZero = Object.entries({hours, minutes, seconds}).filter(e => e[1]);
const toDisplay = Object.fromEntries(nonZero.length ? nonZero : { seconds: 0 });
new Intl.DurationFormat().format(toDisplay);
// => 30 minutes
new Intl.DurationFormat().format({});
// => RangeError - at least one unit required I have more feedback about the 1-5 choices above, but I'll comment on those separately. |
Some bikeshed notes: Instead of verbose Also, why is it negative (hideXXX) not positive (showXxx)? I don't have a strong opinion either way, just wondering why negative was chosen. EDIT: If we did pick |
OK, now finally some feedback about 1-5 options: my main concern would be confusion caused by divergent behavior between For this reason, my current preference would be to have If the user passes an object bag and provides some options, then we'd have to walk through the cases to understand how they'd intersect. Let's discuss! |
Meeting 2021-02-11: Use Option 3. Here's my understanding of exactly what Option 3 is. Below that are a few open issues where I didn't know the answers. Feedback welcome!
ISSUES: A) If the user passes a plain object (not a Duration), should the default for
B) If all units are zero, then what should be displayed? Here's a few cases to consider:
C) Should
C) Should
D) Should there be an option to explicitly prevent rounding, in order to allow display of unbalanced durations? This would be an alternative to choosing one behavior in (B) and (C) above. |
consent on Option 3, the values of |
so in this smallestUnit+largestUnit world, how would the "weeks" be handle? new Intl.DurationFormat("en", { style: "long", smallestUnit: "days", largestUnit: "years"}).format({
years: 1,
weeks: 3,
days: 2,
}); what will be the output? new Intl.DurationFormat("en", { style: "long", smallestUnit: "days", largestUnit: "years"}).format({
years: 1,
months: 2,
days: 2,
}); "1 years, 2 months and 2 days"? |
Is that true the decision means:
|
@FrankYFTang I see your point. I was wrong to assume that the constructor should only accept a specific set of values: it should accept all subsets as well, including single units. Then, it could use the best-effort algorithm you mentioned to find the appropriate pattern to use based on the actual input. Does that sounds okay to you? I don't mind the second solution you proposed all that much either, it's inelegant but then so is everything else about What do you folks think? I feel that we're pretty much at consensus? |
Could we see some code samples and expected output of what you're proposing? |
// I prefer to use the example of Intl.DurationFormat , because it will help us to understand if you will
// throw exception, when will it be throw, using toLocaleString as example will hide such info.
// Example 3
let df3 = new Intl.DurationFormat('en-US', { style: 'digital', requiredUnits: [ 'hours' ] });
df3.format(new Duration('P1M2DT3H4M')); // 1m 2d 03:04
// Example 4
let df4 = new Intl.DurationFormat('en-US', { style: 'digital', requiredUnits: [ 'seconds' ] });
df4.format(new Duration('P1M2DT3H4M')); // 1m 2d 03:04:00
// Example 5
let df5 = new Intl.DurationFormat('en-US', { style: 'digital', requiredUnits: [ 'microseconds' ] });
df5.format(new Duration('P1M2DT3H4M')); // 1m 2d 03:04:00.000000 |
so... based on the df3, and df4 above and and if I have so both A) 3 hours 4 minutes and B) 3 minutes 4 seconds will have the same output. Remember, once the Duration object is constructed, we can only tell a field is 0 or non zero, and we cannot tell is that field specified in the string or not. |
@FrankYFTang that is true, how do we best deal with this edge case? Or should we just not and assume that the developer should be responsible here? |
We could write in the spec that whne style is 'digital' if there is only requiredUnits: [ 'minute' ] is specified, it is equivelant to requiredUnits: [ 'minute', 'second' ] , right?
|
Another choice would be to require users to provide enough required units to avoid ambiguity. So I don't have an opinion about which solution is better. I'd be fine with either one. Also, how would |
This is exactly why I had opposed single-unit |
@ryzokuken - I agree that single-unit is the problem case (specifically |
I don't think there is an "obvious" default here, so I prefer throwing. @FrankYFTang what do you think? |
how about "00" - "59" for requiredUnits: ['second' ] |
Real world usage of 2 digits timers https://www.youtube.com/watch?v=k3X_1TE6P5g |
@FrankYFTang, if the input is If the option were Also, I've been using DateTimeFormat a lot over the last week and I notice that what we're planning for DurationFormat (a single option to determine which units to show) is different from how DateTimeFormat works (a separate option for each unit). Is this difference intentional? I admit I don't like the DateTimeFormat pattern because it's so verbose, but having them be different is also potentially confusing so I'm not sure if the ergonomic improvement is worth the inconsistency. What do you guys think? |
"02:30" |
IMHO this seems like it'd potentially confuse callers, because |
Are you suggesting we do something like this? new Intl.DurationFormat("en", {
style: "short",
hour: "numeric",
minute: "2-digit",
}); |
Yeah. I haven't thought it through enough yet, but after recently spending a lot of time with DateTimeFormat I think that aligning the two XxxFormat APIs may make sense given the highly-overlapping Venn diagram of users and use cases. Even if the resulting API isn't as ergonomic as we'd like (personally I find DateTimeFormat really verbose) there's value in consistency that may be worth some compromise in ergonomics. I admit I don't understand the corner cases of DateTimeFormat as well as DurationFormat, so it's possible that aligning the APIs will be more trouble than it's worth. But the model of explicitly specifying which units you want to see is admittedly appealing given the odd behavior (esp. of digital) that we've been discussing here in this issue and elsewhere. In your snippet above, does Some quick thoughts if we want to pursue this:
I've just started thinking about this while writing this comment, so the suggestions above may not be workable. But there's value in consistency so it seems worth spending some time on whether aligning these two APIs could make sense. I apologize for not suggesting this alignment sooner; I admittedly didn't really know the DateTimeFormat API very well until a week ago when I wrote a bunch of PRs using it. |
I'm against this design because of the reasons I'd mentioned earlier: a |
(I'm not advocating for this option, but I'm writing out what it could look like) I think the DateTimeFormat-style API would only replace Here is what the allowed values would be:
I would suggest that we still follow my proposal earlier where we render all nonzero fields from the duration. I see that problem as orthogonal to this one. |
@justingrant, @ryzokuken, and I had a call and discussed some more options. Here is something that came out of that discussion:
The defaults for these fields could be:
By decoupling style from display, for each field in the duration, we first evaluate whether to display it, and then if we should display it, we look at what the style should be. |
I agree with the general framework that @sffc proposes above, mainly because it aligns with the precedent of DateTimeFormat (for easier learning/use) and seems to hand common use-cases (e.g. one option to choose digital vs. short for all units) ergonomically while also handling uncommon use cases like showing internal zero values. Would it be possible to add a few code examples and expected output for the use cases we know about? Here's a few cases (many of which were challenging in previous iterations of this proposal):
A bunch of questions: How will users toggle between showing fractional seconds (which should be the default because it's the most common case, not "3 seconds 456 milliseconds") and the less-common case of wanting to spell out trailing sub-second units? Do we even need to support the latter case? If the rule is that for sub-second units, the largest displayed second-or-smaller unit (either because it's nonzero or because of Related: will we use Which style options are compatible with which fields? For example, I assume that What happens if the user provides a style option for a field which doesn't support that style option? Will it snap to a default value? Is there a hierarchy of fallbacks, e.g.
Love it! Is there precedent for "always"/"never" in other Intl APIs, as opposed to "show"/"hide" or some other naming? What happens if it's a digital style with nonzero HMS and I choose
What's a "skeleton"? Is that the user's options like Would |
Thanks @sffc for posting this. Let's get some consensus around this before the meeting on thursday so we can make some progress on this! I agree that the idea to split into options for each unit is a good one and it avoid some of the inconsistencies we've seen before, but I still think that two options per field gets a bit too much. At the same time, if the I propose that we keep one option for each unit (eg:
(and I believe strongly that this solution retains all the positive properties of the solution proposed above, while avoiding some of the ergonomic losses I mentioned and keeping the API simple, concise and the list of @sffc @justingrant @FrankYFTang what do you folks think? If this sounds fine, I'd post the answer to @justingrant's questions assuming this framework. |
Although the idea of combining the two options down to a single string appealed to me at one point, I think two options is better because:
If we're concerned about this, we could resolve it by
|
Yes. Thanks for the detailed list of test cases. We should fill these in. I'll answer your questions first.
Fractional seconds are being discussed in #64 and the recommendations over there are still current, not influenced by the style/display discussion in this thread.
That is the current proposal in #64.
I was thinking that "numeric" and "2-digit" would only be valid for hour, minute, and second (the three "digital" fields).
The programmer should get an exception if an invalid string is passed to an option, as it is now in all other Intl objects. For example, Interesting case:
signDisplay.
Let's come back to this one later. I think we may consider dropping "never" since it has other issues that @ryzokuken pointed out, and the same behavior can be obtained by using "auto" and zeroing out the field before passing it to
The user's options. The word "skeleton" comes from ICU.
Yes; so maybe |
Also:
I don't see them as two options per field. I see them as a single option per field with an additional sub-setting to customize it. The sub-setting is the same for each field. On other words, what we really have is {
day: {
style: "short",
display: "always",
}
} But since we don't do nested options in ECMA-402, we flatten it with camel casing. |
Okay, then since both of you feel so strongly about two options per unit and since it's not unacceptable to me (more delays, on the other hand are), I'll start to make spec changes in that direction. Let's iron out the details in the meeting tomorrow and let's get this show on the road! |
@sffc overall I agree with your response above. Thanks!
One possible exception could be
How we control whether milliseconds (and smaller) units seems fairly closely connected to how fractions are displayed. So I think it'd make sense to design both together, because if the proposals in this issue don't work for fractions then it'd be problematic.
Yeah, I think I agree. The only use case I can think of for an option like this would be to control behavior of internal zeroes. Should we consider an option for that specific case instead of a more general
One possibility could be to change the choices for |
2021-08-03 discussion:
@justingrant suggested:
@sffc - |
This has been updated in the latest version. Closing. |
We discussed several semantics here.
It would be good to have a list of use cases, and how those use cases would look in each of these options.
The text was updated successfully, but these errors were encountered: