Skip to content
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

fixup explainer and update to latest design #66

Merged
merged 2 commits into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
309 changes: 187 additions & 122 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@
**Authors**: [Younies Mahmoud](https://github.com/younies), [Ujjwal Sharma](https://github.com/ryzokuken)

**Stage 3 Reviewers**

- Michael Ficarra [@michaelficarra](https://github.com/michaelficarra)
- Ron Buckton [@rbuckton](https://github.com/rbuckton)
- Ross Kirsling [@rkirsling](https://github.com/rkirsling)

**[slide](https://docs.google.com/presentation/d/1QmrhwsYwlsfe8FJqgGarCIAySWxeZzDqCrVN3-DWiGk/edit?usp=sharing)**
**[slides](https://docs.google.com/presentation/d/1QmrhwsYwlsfe8FJqgGarCIAySWxeZzDqCrVN3-DWiGk/edit?usp=sharing)**

## Status

This proposal reached Stage 1 at the 2020 Feb TC39 meeting.
This proposal reached Stage 2 at the 2020 June TC39 meeting.

# Overview
## Overview

* Time Duration is how long something lasts, from the start to end. It can be represented by a single time unit or multiple ones.
+ For example,
Expand All @@ -31,95 +33,71 @@ This proposal reached Stage 1 at the 2020 Feb TC39 meeting.
- 1 hour, 46 minutes and 40 seconds → Wide
- 1 hr, 46 min, 40 sec → Short

# Quick Start Example
## Quick Start

```javascript
new Intl.DurationFormat("fr-FR", { style: "long" }).format({
hours: 1,
minutes: 46,
seconds: 40,
});
// "1 heure, 46 minutes et 40 secondes"
// => "1 heure, 46 minutes et 40 secondes"
```

# V8 Prototypes
Three v8 prototypes (try to use two different possible ICU classes) were made, ALL are
* Sync with ["Stage 1 Draft / June 1, 2020" version of spec](https://github.com/tc39/proposal-intl-duration-format/commit/fc8ff131cf7e688810b38d7e95d6fa44b1f1964e)
* Flag --harmony_intl_duration_format
* Have not implement any changes not yet spec out in https://tc39.es/proposal-intl-duration-format/ such as
+ hideZeroValued
+ smallestUnit / largestUnit
## Motivation

1. Base the implementation on [icu::MeasureFormat::formatMeasures()](https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/classicu_1_1MeasureFormat.html#ade104d40578223bd194050914b090904)
* https://chromium-review.googlesource.com/c/v8/v8/+/2762664
* Not yet implment formatToParts
* Need solution of [ICU-21543 "Add methods to return FormattedValue to MeasureFormat "](https://unicode-org.atlassian.net/browse/ICU-21543) to implement formatToParts().
2. Based on [the support of "-and-" unit in LocalizedNumberFormatter](https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/classicu_1_1number_1_1LocalizedNumberFormatter.html)
* https://chromium-review.googlesource.com/c/v8/v8/+/2775300
* Not yet implment formatToParts
* Not yet implement style:"digital"
* Need solution of the following to implement formatToParts():
+ [ICU-21544 "unit format in number formatter return U_INTERNAL_PROGRAM_ERROR with "year-and-" (except month) and "month-and-""](https://unicode-org.atlassian.net/browse/ICU-21544)
+ [ICU-21547 "nextPosition() of FormattedNumber of unit with "-and-" is buggy"](https://unicode-org.atlassian.net/browse/ICU-21547)
3. Based on icu::ListFormatter and icu::number::LocalizedNumberFormatter w/o depend on the "X-and-Y" units.
* https://chromium-review.googlesource.com/c/v8/v8/+/2776518
* Implements formatToParts
* Not yet implement style:"digital"
# Motivation

* Users need all types of duration format depending on the requirements of their application. For example, to show how long a flight takes, the duration should be in Short or Narrow format
+ 1 hr 40 min 60 sec → Short
+ 1 h 40 m 60 sec → Narrow
* Users need all types of duration formatting depending on the requirements of their application. For example, to show how long a flight takes, the duration should be in Short or Narrow format
+ "1 hr 40 min 60 sec" → Short
+ "1h 40m 60s" → Narrow

# Requirements & Design
## Requirements & Design

In this section, we are going to illustrate each user needs (requirements) and a design for each need (requirement)

## Input Value
### Input Value

+ Users need to identify how to input a duration value. For example, if a user needs to format `1000 seconds` , how could the user pass the value to the formatting function.
+ Users need to identify how to input a duration value. For example, if a user needs to format `1000 seconds` , how could the user pass the value to the formatting function.

### Design
#### Design

* Input value will be an object of type `Temporal.Duration`
* Example:
- `const d = new DurationFormat();`
- `d.format(Temporal.Duration.from({hours: 3, minutes: 4});`
* Example: `new DurationFormat().format(Temporal.Duration.from({hours: 3, minutes: 4});`

## Formatting width
### Formatting width

Users want to determine several types of the formatting width as following

| Format width | Example |
|--------------|-----------------------|
| Wide | 1 hour and 50 minutes |
| Long | 1 hour and 50 minutes |
| Short | 1 hr, 50 min |
| Narrow | 1h 50 m |
| Digital | 1: 50: 00 |
| Narrow | 1h 50m |
| Digital | 1:50:00 |

### Design
#### Design

+ The user can determine the formatting width using a parameter `style` and the value of this parameter may be one of the following `string` s:
- `"wide"`
- `"short"`
- `"narrow"`
- `"digital"`
+ The user can determine the formatting width using a parameter `style` and the value of this parameter may be one of the following strings:
- `"long"`
- `"short"`
- `"narrow"`
- `"digital"`
+ The width of each field can be set separately in the options, for example: `{ years: "long", months: "short", days: "narrow" }`.

## Supported Time Fields
### Supported Duration Units

* Users need the following fields to be supported
+ Year
+ Month
+ Week
+ Day
+ Hour
+ Minute
+ Second
+ Millisecond
+ Microsecond
+ Nanosecond

### Design
+ Years
+ Months
+ Weeks
+ Days
+ Hours
+ Minutes
+ Seconds
+ Milliseconds
+ Microseconds
+ Nanoseconds

#### Design

* We are going to support the same fields as in [ `Temporal.Duration` ](https://github.com/tc39/proposal-temporal/blob/main/docs/duration.md), which contains:
+ years
Expand All @@ -133,16 +111,15 @@ Users want to determine several types of the formatting width as following
+ microseconds
+ nanoseconds

## Determining Time Fields
### Determining Duration Units

* Users need to determine the time fields,
* For example: 6059 seconds
+ Hours, Minutes, Seconds
- 1 hours, 40 minutes and 59 seconds
+ Minutes and Seconds
- 100 minutes and 59 seconds.
`DurationFormat` does not do any arithmetic operations nor rounding on the input implicitly.
Instead, the user must edit the input (e.g. using [`Temporal.Duration.prototype.round`](https://tc39.es/proposal-temporal/docs/duration.html#round)) to ensure the input is within the desired range.

### Design
To avoid accidentally omitting part of the duration, `DurationFormat` always outputs all nonzero fields (except sub-second fields truncated by `fractionalDigits`).
Callers who want to omit nonzero fields (for example, only showing the date or time portion of the duration) should edit the input duration.

#### Design

* Users can determine the time fields in array.
* Example:
Expand All @@ -155,37 +132,15 @@ Users want to determine several types of the formatting width as following
]
```

#### NOTE (For stage 3)

* We are discussing to use `largestField` and `smallestField` options to determine the time fields instead of an array.
### Hide zero-value fields

## Hide zero-value fields
In most cases, users want to avoid displaying zero-value fields. All zero-valued fields are hidden by default. If you specify the style for a specific field, then it is always displayed, but you can override that behavior by also setting the display option for that particular field to `"auto"` again explicitly.

* Users needs to hide zero-value fields depends in some criteria
+ Hide all zero-value fields
- Hours, Minutes, Seconds (without the `hide` option)
* 1 hours, 0 minutes and 59 seconds.
- Hours, Minutes, Seconds (with the `hide` option)
* 1 hours and 59 seconds.
+ Hide-upper-limit with zero-valued.
- Hours, Minutes, Seconds, MilliSeconds (without the `hide-upper-limit` option)
* 0 hr, 20 min, 0 sec, 100 msec .
- Hours, Minutes, Seconds MilliSeconds(with the `hide-upper-limit` option)
* 20 min, 0 sec, 100 msec.
+ Hide-lower-limit with zero-valued.
#### Design

### Design
For each field `foo`, there is an option `fooDisplay` that is set to `"auto"` by default. Setting that option to `"always"` causes that field to be displayed even if it is zero; for example, to always show the "day" field, set `{ dayDisplay: "always" }`. If you specify the style for that field by setting the `foo` option, then the default for `fooDisplay` becomes `"always"`; for example, `{ day: "short" }` implies `{ day: "short", dayDisplay: "always" }`.

* Users can set the `hideZeroValues` parameter by one of the following values:
+ `"all"` // Hide all the fields that have a zero-valued.
+ `"leadingAndTrailing"` // Hide all the zero fields in the leading or the training
+ `"leadingOnly"`
+ `"trailingOnly"`
+ `"none"` // Do not hide any zero-valued fields.

* Default Value: `"none"`

## Round
### Round

* Users wants to decide if they are going to round the smallest field or not.
* for example:
Expand All @@ -194,11 +149,11 @@ Users want to determine several types of the formatting width as following
+ With rounding option
- 1 hour and 31 minutes.

### Design
#### Design

* Users could use `Temporal.Duration#round` function.

## Locale aware format
### Locale-aware format

* Users needs the formatting to be dependent on the locale
* For example:
Expand All @@ -207,31 +162,141 @@ Users want to determine several types of the formatting width as following
+ fr-FR
- 1 heure, 46 minutes et 40 secondes

### Design
#### Design

Adding the locale in string format as a first argument, or specifying a ranked list of locales as an array of string locale values.

### Display fractional values

Sometimes it is desirable to display the smallest sub-second unit not by itself but as a fraction of the immediately larger unit.

Adding the locale in `string` format as a first argument.
#### Design

# API design
We allow users to specify a `fractionalDigits` option that will display the smallest sub-second unit with display set to `"auto"` as a fraction of the previous unit if it is non-zero. The number of digits used will be the value passed to this option.

#### Example

```javascript
new Intl.DurationFormat('en', { fractionalDigits: 2 }).format('PT12.3456S'); // => 12.34 sec
new Intl.DurationFormat('en', { milliseconds: 'narrow', fractionalDigits: 2 }).format('PT12.3456S'); // => 12s 345.60ms
```

## API design

``` javascript
const formatter =
new Intl.DurationFormat('en-US', {
fields: [
'hour',
'minute',
'second'
],
style: 'short',
hideZeroValues: 'none',
});

const duration =
Temporal.Duration.from({
hours: 2,
minutes: 46,
seconds: 40
});

console.log(formatter.format(duration));
// output: 2 hr 46 min 40 sec
new Intl.DurationFormat('en').format(Temporal.Duration.from('PT2H46M40S')); // => 2 hr 46 min 40 sec
new Intl.DurationFormat('en', {
hours: 'numeric',
seconds: 'numeric',
}).format(Temporal.Duration.from('PT2H40S')); // => 2:00:40
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a section explaining the motivation and design for the fractionalDigits option. Also a section for the style option.

Finally, I was hoping to see an "Options Summary" or "Design Summary" section that shows the full list of options and their allowed values. The current layout of the page with multiple, separate "Design" sections is fine, but it makes it visualize every option to understand the big picture of how the API works. I think both are helpful.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new fractionalDigits section with examples is great. But IMHO this page still needs an options summary section which lists all the possible options, their possible values, and a short sentence about each one. Something like the "parameters" section of https://tc39.es/proposal-temporal/docs/zoneddatetime.html#until.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justingrant updated! Please check out the README now and let me know what you think.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New content is much clearer and easier to understand. Thanks!

### Constructor

#### Syntax

```javascript
new Intl.DurationFormat(locales, options)
```

#### Parameters

* `locales: Array<string> | string`: A locale string or a list of locale strings in decreasing order of preference.
* `options?: object`: An object for configuring the behavior of the instance. It may have some or all of the following properties:
* `localeMatcher: "best-fit" | "lookup"`: A string denoting which locale matching algorithm to use. Defaults to `"best fit"`.
* `numberingSystem: string`: A string containing the name of the numbering system to be used for number formatting.
* `style: "long" | "short" | "narrow" | "digital"`: The base style to be used for formatting. This can be overriden per-unit by setting the more granular options. Defaults to `"short"`.
* `years: "long" | "short" | "narrow"`: The style to be used for formatting years.
* `yearsDisplay: "always" | "auto"`: Whether to always display years, or only if nonzero.
* `months: "long" | "short" | "narrow"`: The style to be used for formatting months.
* `monthsDisplay: "always" | "auto"`: Whether to always display months, or only if nonzero.
* `weeks: "long" | "short" | "narrow"`: The style to be used for formatting weeks.
* `weeksDisplay: "always" | "auto"`: Whether to always display weeks, or only if nonzero.
* `days: "long" | "short" | "narrow"`: The style to be used for formatting days.
ryzokuken marked this conversation as resolved.
Show resolved Hide resolved
* `daysDisplay: "always" | "auto"`: Whether to always display days, or only if nonzero.
* `hours: "long" | "short" | "narrow" | "numeric" | "2-digit"`: The style to be used for formatting hours.
* `hoursDisplay: "always" | "auto"`: Whether to always display hours, or only if nonzero.
* `minutes: "long" | "short" | "narrow" | "numeric" | "2-digit"`: The style to be used for formatting minutes.
ryzokuken marked this conversation as resolved.
Show resolved Hide resolved
* `minutesDisplay: "always" | "auto"`: Whether to always display minutes, or only if nonzero.
* `seconds: "long" | "short" | "narrow" | "numeric" | "2-digit"`: The style to be used for formatting seconds.
* `secondsDisplay: "always" | "auto"`: Whether to always display seconds, or only if nonzero.
* `milliseconds: "long" | "short" | "narrow" | "numeric"`: The style to be used for formatting milliseconds.
* `millisecondsDisplay: "always" | "auto"`: Whether to always display milliseconds, or only if nonzero.
* `microseconds: "long" | "short" | "narrow" | "numeric"`: The style to be used for formatting microseconds.
* `microsecondsDisplay: "always" | "auto"`: Whether to always display microseconds, or only if nonzero.
* `nanoseconds: "long" | "short" | "narrow" | "numeric"`: The style to be used for formatting nanoseconds.
* `nanosecondsDisplay: "always" | "auto"`: Whether to always display nanoseconds, or only if nonzero.
* `fractionalDigits: number`: How many fractional digits to display in the output.
Additional decimal places will be truncated towards zero.
(`Temporal.Duration.prototype.round` can be used to obtain different rounding behavior.)
Normally this option applies to fractional seconds, but this option actually applies to the largest seconds-or-smaller unit that uses the `"numeric"` or `"2-digit"` style.
For example, if options are `{ seconds: "narrow", milliseconds: "narrow", fractionalDigits: 2}` then the output can be `"5s 2.39ms"`.
If this option is omitted, only nonzero decimals will be displayed and trailing zeroes will be omitted.

##### Default values

* The per-unit style options default to the value of `style` for all styles except `"digital"`, for which units years till days default to `"narrow"` and hours till nanoseconds default to `"numeric"`.
* The per-unit display options default to `"auto"` if the corresponding style option is `undefined` and `"always"` otherwise.

#### Notes

* Some locales may share the same representation between `"long"` and `"short"` or between `"narrow"` and `"short"`. Others may use different representations for each one, e.g. "3 seconds", "3 secs", "3s".
* Any unit with the style `"numeric"` preceded by a unit of style `"numeric"` or `"2-digit"` should act like the style `"2-digit"` was used instead. For example, `{hours: 'numeric', minutes: 'numeric'}` can produce output like "3:08".

ryzokuken marked this conversation as resolved.
Show resolved Hide resolved
### `Intl.DurationFormat#format`

#### Syntax

```javascript
new Intl.DurationFormat('en').format(duration)
```

#### Parameters

* `duration` (`Temporal.Duration` | `string` | `object`): The duration to be formatted. This could either be a `Temporal.Duration` object or a string or options bag that can be converted into one.

#### Returns

A `string` containing the formatted duration.

### `Intl.DurationFormat#formatToParts`

#### Syntax

```javascript
new Intl.DurationFormat('en').formatToParts(duration)
```

#### Parameters

* `duration` (`Temporal.Duration` | `string` | `object`): The duration to be formatted. This could either be a `Temporal.Duration` object or a string or options bag that can be converted into one.

#### Returns

An `Array<{type: string, value: string}>` containing the formatted duration in parts.

## Implementation Status

### V8 Prototypes
Three v8 prototypes (try to use two different possible ICU classes) were made, ALL are:
* Sync with ["Stage 1 Draft / June 1, 2020" version of spec](https://github.com/tc39/proposal-intl-duration-format/commit/fc8ff131cf7e688810b38d7e95d6fa44b1f1964e)
* Flag --harmony_intl_duration_format
* Have not implement any changes not yet spec out in https://tc39.es/proposal-intl-duration-format/ such as
+ hideZeroValued
+ smallestUnit / largestUnit

1. Base the implementation on [icu::MeasureFormat::formatMeasures()](https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/classicu_1_1MeasureFormat.html#ade104d40578223bd194050914b090904)
* https://chromium-review.googlesource.com/c/v8/v8/+/2762664
* Not yet implment formatToParts
* Need solution of [ICU-21543 "Add methods to return FormattedValue to MeasureFormat "](https://unicode-org.atlassian.net/browse/ICU-21543) to implement formatToParts().
2. Based on [the support of "-and-" unit in LocalizedNumberFormatter](https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/classicu_1_1number_1_1LocalizedNumberFormatter.html)
* https://chromium-review.googlesource.com/c/v8/v8/+/2775300
* Not yet implment formatToParts
* Not yet implement style:"digital"
* Need solution of the following to implement formatToParts():
+ [ICU-21544 "unit format in number formatter return U_INTERNAL_PROGRAM_ERROR with "year-and-" (except month) and "month-and-""](https://unicode-org.atlassian.net/browse/ICU-21544)
+ [ICU-21547 "nextPosition() of FormattedNumber of unit with "-and-" is buggy"](https://unicode-org.atlassian.net/browse/ICU-21547)
3. Based on icu::ListFormatter and icu::number::LocalizedNumberFormatter w/o depend on the "X-and-Y" units.
* https://chromium-review.googlesource.com/c/v8/v8/+/2776518
* Implements formatToParts
* Not yet implement style:"digital"
Loading