-
Notifications
You must be signed in to change notification settings - Fork 106
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
Normative: Allow users to specify rounding based on cash denominations in common use #839
Changes from 2 commits
c2abdfc
1428546
ba8b719
7d125bb
90db107
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 |
---|---|---|
|
@@ -17,7 +17,7 @@ <h1>Intl.NumberFormat ( [ _locales_ [ , _options_ ] ] )</h1> | |
|
||
<emu-alg> | ||
1. If NewTarget is *undefined*, let _newTarget_ be the active function object, else let _newTarget_ be NewTarget. | ||
1. Let _numberFormat_ be ? OrdinaryCreateFromConstructor(_newTarget_, *"%NumberFormat.prototype%"*, « [[InitializedNumberFormat]], [[Locale]], [[DataLocale]], [[NumberingSystem]], [[Style]], [[Unit]], [[UnitDisplay]], [[Currency]], [[CurrencyDisplay]], [[CurrencySign]], [[MinimumIntegerDigits]], [[MinimumFractionDigits]], [[MaximumFractionDigits]], [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], [[RoundingType]], [[Notation]], [[CompactDisplay]], [[UseGrouping]], [[SignDisplay]], [[RoundingIncrement]], [[RoundingMode]], [[ComputedRoundingPriority]], [[TrailingZeroDisplay]], [[BoundFormat]] »). | ||
1. Let _numberFormat_ be ? OrdinaryCreateFromConstructor(_newTarget_, *"%NumberFormat.prototype%"*, « [[InitializedNumberFormat]], [[Locale]], [[DataLocale]], [[NumberingSystem]], [[Style]], [[Unit]], [[UnitDisplay]], [[Currency]], [[CurrencyDisplay]], [[CurrencyPrecision]], [[CurrencySign]], [[MinimumIntegerDigits]], [[MinimumFractionDigits]], [[MaximumFractionDigits]], [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], [[RoundingType]], [[Notation]], [[CompactDisplay]], [[UseGrouping]], [[SignDisplay]], [[RoundingIncrement]], [[RoundingMode]], [[ComputedRoundingPriority]], [[TrailingZeroDisplay]], [[BoundFormat]] »). | ||
1. Perform ? InitializeNumberFormat(_numberFormat_, _locales_, _options_). | ||
1. If the implementation supports the normative optional constructor mode of <emu-xref href="#legacy-constructor"></emu-xref>, then | ||
1. Let _this_ be the *this* value. | ||
|
@@ -77,18 +77,21 @@ <h1> | |
1. Let _style_ be _numberFormat_.[[Style]]. | ||
1. If _style_ is *"currency"*, then | ||
1. Let _currency_ be _numberFormat_.[[Currency]]. | ||
1. Let _cDigits_ be CurrencyDigits(_currency_). | ||
1. Let _currencyPrecision_ be _numberFormat_.[[CurrencyPrecision]]. | ||
1. Let _cDigits_ be CurrencyDigits(_currency_, _currencyPrecision_). | ||
1. Let _defaultRoundingIncrement_ be CurrencyRounding(_currency_, _currencyPrecision_). | ||
1. Let _mnfdDefault_ be _cDigits_. | ||
1. Let _mxfdDefault_ be _cDigits_. | ||
1. Else, | ||
1. Let _defaultRoundingIncrement_ be 1. | ||
1. Let _mnfdDefault_ be 0. | ||
1. If _style_ is *"percent"*, then | ||
1. Let _mxfdDefault_ be 0. | ||
1. Else, | ||
1. Let _mxfdDefault_ be 3. | ||
1. Let _notation_ be ? GetOption(_options_, *"notation"*, ~string~, « *"standard"*, *"scientific"*, *"engineering"*, *"compact"* », *"standard"*). | ||
1. Set _numberFormat_.[[Notation]] to _notation_. | ||
1. Perform ? SetNumberFormatDigitOptions(_numberFormat_, _options_, _mnfdDefault_, _mxfdDefault_, _notation_). | ||
1. Perform ? SetNumberFormatDigitOptions(_numberFormat_, _options_, _mnfdDefault_, _mxfdDefault_, _notation_, _defaultRoundingIncrement_). | ||
1. Let _compactDisplay_ be ? GetOption(_options_, *"compactDisplay"*, ~string~, « *"short"*, *"long"* », *"short"*). | ||
1. Let _defaultUseGrouping_ be *"auto"*. | ||
1. If _notation_ is *"compact"*, then | ||
|
@@ -113,6 +116,7 @@ <h1> | |
_mnfdDefault_: an integer, | ||
_mxfdDefault_: an integer, | ||
_notation_: a String, | ||
_defaultRoundingIncrement_: an integer, | ||
): either a normal completion containing ~unused~ or a throw completion | ||
</h1> | ||
<dl class="header"> | ||
|
@@ -126,7 +130,7 @@ <h1> | |
1. Let _mnsd_ be ? Get(_options_, *"minimumSignificantDigits"*). | ||
1. Let _mxsd_ be ? Get(_options_, *"maximumSignificantDigits"*). | ||
1. Set _intlObj_.[[MinimumIntegerDigits]] to _mnid_. | ||
1. Let _roundingIncrement_ be ? GetNumberOption(_options_, *"roundingIncrement"*, 1, 5000, 1). | ||
1. Let _roundingIncrement_ be ? GetNumberOption(_options_, *"roundingIncrement"*, 1, 5000, _defaultRoundingIncrement_). | ||
1. If _roundingIncrement_ is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000 », throw a *RangeError* exception. | ||
1. Let _roundingMode_ be ? GetOption(_options_, *"roundingMode"*, ~string~, « *"ceil"*, *"floor"*, *"expand"*, *"trunc"*, *"halfCeil"*, *"halfFloor"*, *"halfExpand"*, *"halfTrunc"*, *"halfEven"* », *"halfExpand"*). | ||
1. Let _roundingPriority_ be ? GetOption(_options_, *"roundingPriority"*, ~string~, « *"auto"*, *"morePrecision"*, *"lessPrecision"* », *"auto"*). | ||
|
@@ -215,6 +219,7 @@ <h1> | |
1. Else, | ||
1. If IsWellFormedCurrencyCode(_currency_) is *false*, throw a *RangeError* exception. | ||
1. Let _currencyDisplay_ be ? GetOption(_options_, *"currencyDisplay"*, ~string~, « *"code"*, *"symbol"*, *"narrowSymbol"*, *"name"* », *"symbol"*). | ||
1. Let _currencyPrecision_ be ? GetOption(_options_, *"currencyPrecision"*, ~string~, « *"cash"*, *"financial"* », *"financial"*). | ||
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. Just a reminder to bikeshed all parts of this: the option name and both string options. 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. I'm wondering about the difference between "financial" here vs. "accounting" for 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. My sense is no, since there's situations where you'd want to use one and not the other. I can easily imagine someone using (for example) a personal finance application wanting debts represented by wrapping them in parentheses, but also not caring about anything smaller than a cent. Avoiding confusion between the options is one reason why I think "financial" would be a better name than "accounting" for this option. |
||
1. Let _currencySign_ be ? GetOption(_options_, *"currencySign"*, ~string~, « *"standard"*, *"accounting"* », *"standard"*). | ||
1. Let _unit_ be ? GetOption(_options_, *"unit"*, ~string~, ~empty~, *undefined*). | ||
1. If _unit_ is *undefined*, then | ||
|
@@ -225,6 +230,7 @@ <h1> | |
1. If _style_ is *"currency"*, then | ||
1. Set _intlObj_.[[Currency]] to the ASCII-uppercase of _currency_. | ||
1. Set _intlObj_.[[CurrencyDisplay]] to _currencyDisplay_. | ||
1. Set _intlObj_.[[CurrencyPrecision]] to _currencyPrecision_. | ||
1. Set _intlObj_.[[CurrencySign]] to _currencySign_. | ||
1. If _style_ is *"unit"*, then | ||
1. Set _intlObj_.[[Unit]] to _unit_. | ||
|
@@ -470,6 +476,11 @@ <h1>Intl.NumberFormat.prototype.resolvedOptions ( )</h1> | |
<td>*"currencyDisplay"*</td> | ||
<td></td> | ||
</tr> | ||
<tr> | ||
<td>[[CurrencyPrecision]]</td> | ||
<td>*"currencyPrecision"*</td> | ||
<td></td> | ||
</tr> | ||
<tr> | ||
<td>[[CurrencySign]]</td> | ||
<td>*"currencySign"*</td> | ||
|
@@ -577,6 +588,7 @@ <h1>Properties of Intl.NumberFormat Instances</h1> | |
<li>[[Style]] is one of the String values *"decimal"*, *"currency"*, *"percent"*, or *"unit"*, identifying the type of quantity being measured.</li> | ||
<li>[[Currency]] is a String value with the currency code identifying the currency to be used if formatting with the *"currency"* unit type. It is only used when [[Style]] has the value *"currency"*.</li> | ||
<li>[[CurrencyDisplay]] is one of the String values *"code"*, *"symbol"*, *"narrowSymbol"*, or *"name"*, specifying whether to display the currency as an ISO 4217 alphabetic currency code, a localized currency symbol, or a localized currency name if formatting with the *"currency"* style. It is only used when [[Style]] has the value *"currency"*.</li> | ||
<li>[[CurrencyPrecision]] is one of the String values *"cash"* or *"financial"*, specifying whether to round currency values to the smallest denomination of the currency in common usage, or instead to round to the number of fractional digits used for the currency in accounting and by financial institutions. It is only used when [[Style]] has the value *"currency"*.</li> | ||
<li>[[CurrencySign]] is one of the String values *"standard"* or *"accounting"*, specifying whether to render negative numbers in accounting format, often signified by parenthesis. It is only used when [[Style]] has the value *"currency"* and when [[SignDisplay]] is not *"never"*.</li> | ||
<li>[[Unit]] is a core unit identifier. It is only used when [[Style]] has the value *"unit"*.</li> | ||
<li>[[UnitDisplay]] is one of the String values *"short"*, *"narrow"*, or *"long"*, specifying whether to display the unit as a symbol, narrow symbol, or localized long name if formatting with the *"unit"* style. It is only used when [[Style]] has the value *"unit"*.</li> | ||
|
@@ -713,14 +725,35 @@ <h1>Abstract Operations for NumberFormat Objects</h1> | |
<h1> | ||
CurrencyDigits ( | ||
_currency_: a String, | ||
_currencyPrecision_: a String, | ||
): a non-negative integer | ||
</h1> | ||
<dl class="header"> | ||
</dl> | ||
<emu-alg> | ||
1. Assert: IsWellFormedCurrencyCode(_currency_) is *true*. | ||
1. Assert: _currency_ is equal to the ASCII-uppercase of _currency_. | ||
1. If the Common Locale Data Repository supplemental data pertaining to fractional currency values contains an element with _currency_ as the value of its iso4217 attribute, then | ||
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. I'm comfortable with generalizing beyond ISO 4217, but this seems like the kind of data for which an implementation could conceivably have reasons to diverge from CLDR (i.e., superior local knowledge). So I'd rather have the reference be a recommendation rather than a requirement (aligning with other sections of the spec). |
||
1. If _currencyPrecision_ is *"cash"* and the element with _currency_ as the value of its iso4217 attribute has a cashDigits attribute, return the value of that attribute; otherwise, return the value of that element's digits attribute. | ||
1. Return 2. | ||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-currencyrounding" type="abstract operation"> | ||
<h1> | ||
CurrencyRounding ( | ||
sffc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_currency_: a String, | ||
_currencyPrecision_: a String, | ||
): a non-negative integer | ||
</h1> | ||
<dl class="header"> | ||
</dl> | ||
<emu-alg> | ||
1. Assert: IsWellFormedCurrencyCode(_currency_) is *true*. | ||
1. Assert: _currency_ is equal to the ASCII-uppercase of _currency_. | ||
1. If the ISO 4217 currency and funds code list contains _currency_ as an alphabetic code, return the minor unit value corresponding to the _currency_ from the list; otherwise, return 2. | ||
1. If the Common Locale Repository supplemental data pertaining to fractional currency values contains an element with _currency_ as the value of its iso4217 attribute, then | ||
1. If _currencyPrecision_ is *"cash"* and the element with _currency_ as the value of its iso4217 attribute has a cashRounding attribute, return the value of that attribute. | ||
1. Return 1. | ||
</emu-alg> | ||
</emu-clause> | ||
|
||
|
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.
Observation: not sure how the spec scopes variables but this
Let
is not in the same lexical scope as the code below that reads from it. If it is a problem, you couldLet
it be 1 further up and thenSet
it to something else when needed.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.
This is fine; aliases are implicitly scoped to the containing algorithm: https://tc39.es/ecma262/multipage/notational-conventions.html#sec-algorithm-conventions (emphasis mine)