From 90a175890c4e1a63ff4b9c05145c18b217f7d1fb Mon Sep 17 00:00:00 2001 From: Justin Grant Date: Fri, 12 Nov 2021 16:56:15 -0800 Subject: [PATCH] Allow strings as req'd param in `round` / `total` This PR addresses reviwer feedback to use a string, not only an options bag, as the required parameter of `round` and `total` methods in various Temporal types. For advanced use cases, the string can be substituted with a property bag containing one required property and other optional properties. This is a normative change but not a breaking change. Examples: `duration.total(unit)` is same as `duration.total({unit})` `time.round(smallestUnit) is same as `time.round({smallestUnit})` --- docs/duration.md | 67 +++--- docs/instant.md | 31 ++- docs/plaindatetime.md | 31 ++- docs/plaintime.md | 31 ++- docs/zoneddatetime.md | 30 ++- polyfill/index.d.ts | 384 +++++++++++++++++++------------- polyfill/lib/duration.mjs | 40 ++-- polyfill/lib/instant.mjs | 20 +- polyfill/lib/plaindatetime.mjs | 20 +- polyfill/lib/plaintime.mjs | 19 +- polyfill/lib/zoneddatetime.mjs | 19 +- polyfill/test/duration.mjs | 110 +++++++-- polyfill/test/instant.mjs | 19 +- polyfill/test/plaindatetime.mjs | 22 +- polyfill/test/plaintime.mjs | 23 +- polyfill/test/zoneddatetime.mjs | 33 +-- polyfill/test262 | 2 +- spec/duration.html | 40 ++-- spec/instant.html | 19 +- spec/plaindatetime.html | 19 +- spec/plaintime.html | 19 +- spec/zoneddatetime.html | 19 +- 22 files changed, 649 insertions(+), 368 deletions(-) diff --git a/docs/duration.md b/docs/duration.md index 0ad5687724..a62340018c 100644 --- a/docs/duration.md +++ b/docs/duration.md @@ -386,25 +386,28 @@ d = Temporal.Duration.from('-PT8H30M'); d.abs(); // PT8H30M ``` -### duration.**round**(_options_: object) : Temporal.Duration +### duration.**round**(_roundTo_: string | object) : Temporal.Duration **Parameters:** -- `options` (object): An object with properties representing options for the operation. - The following options are recognized: - - `largestUnit` (string): The largest unit of time to allow in the resulting `Temporal.Duration` object. - Valid values are `'auto'`, `'year'`, `'month'`, `'week'`, `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. - The default is `'auto'`. - - `smallestUnit` (string): The smallest unit of time to round to in the resulting `Temporal.Duration` object. - Valid values are `'year'`, `'month'`, `'week'`, `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. - The default is `'nanosecond'`, i.e. no rounding. - - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. - The default is 1. - - `roundingMode` (string): How to handle the remainder, if rounding. - Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. - The default is `'halfExpand'`. - - `relativeTo` (`Temporal.PlainDateTime`): The starting point to use when converting between years, months, weeks, and days. - It must be a `Temporal.PlainDateTime`, or a value that can be passed to `Temporal.PlainDateTime.from()`. +- `roundTo` (string | object): A required string or object to control the operation. + - If a string is provided, the resulting `Temporal.Duration` object will be rounded to that unit. + Valid values are `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + A string parameter is treated the same as an object whose `smallestUnit` property value is that string. + - If an object is passed, the following properties are recognized: + - `largestUnit` (string): The largest unit of time to allow in the resulting `Temporal.Duration` object. + Valid values are `'auto'`, `'year'`, `'month'`, `'week'`, `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + The default is `'auto'`. + - `smallestUnit` (string): The smallest unit of time to round to in the resulting `Temporal.Duration` object. + Valid values are `'year'`, `'month'`, `'week'`, `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + The default is `'nanosecond'`, i.e. no rounding. + - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. + The default is 1. + - `roundingMode` (string): How to handle the remainder, if rounding. + Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. + The default is `'halfExpand'`. + - `relativeTo` (`Temporal.PlainDateTime`): The starting point to use when converting between years, months, weeks, and days. + It must be a `Temporal.PlainDateTime`, or a value that can be passed to `Temporal.PlainDateTime.from()`. **Returns:** a new `Temporal.Duration` object which is `duration`, rounded and/or balanced. @@ -418,15 +421,15 @@ This operation is called "balancing." For usage examples and a more complete explanation of how balancing works, see [Duration balancing](./balancing.md). -A `largestUnit` value of `'auto'`, which is the default if only `smallestUnit` is given, means that `largestUnit` should be the largest nonzero unit in the duration that is larger than `smallestUnit`. +A `largestUnit` value of `'auto'`, which is the default if only `smallestUnit` is given (or if `roundTo` is a string), means that `largestUnit` should be the largest nonzero unit in the duration that is larger than `smallestUnit`. For example, in a duration of 3 days and 12 hours, `largestUnit: 'auto'` would mean the same as `largestUnit: 'day'`. This behavior implies that the default balancing behavior of this method to not "grow" the duration beyond its current largest unit unless needed for rounding. -The `smallestUnit` option determines the unit to round to. +The `smallestUnit` option (or the value of `roundTo` if a string parameter is used) determines the unit to round to. For example, to round to the nearest minute, use `smallestUnit: 'minute'`. -The default, if only `largestUnit` is given, is to do no rounding. +The default, if only `largestUnit` is given, is to do no rounding of smaller units. -At least one of `largestUnit` or `smallestUnit` is required. +If an object parameter is used, at least one of `largestUnit` or `smallestUnit` is required. Converting between years, months, weeks, and other units requires a reference point. If `largestUnit` or `smallestUnit` is years, months, or weeks, or the duration has nonzero years, months, or weeks, then the `relativeTo` option is required. @@ -516,17 +519,20 @@ quarters = d.months / 3; quarters; // => 3 ``` -### duration.**total**(_options_: object) : number +### duration.**total**(_totalOf_: string | object) : number **Parameters:** -- `options` (object): An object with properties representing options for the operation. - The following options are recognized: - - `unit` (string): The unit of time that will be returned. - Valid values are `'year'`, `'month'`, `'week'`, `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. - There is no default; `unit` is required. - - `relativeTo` (`Temporal.PlainDateTime`): The starting point to use when converting between years, months, weeks, and days. - It must be a `Temporal.PlainDateTime`, or a value that can be passed to `Temporal.PlainDateTime.from()`. +- `totalOf` (string | object): A required string or object to control the operation. + - If a string is passed, it represents the unit of time that will be returned. + Valid values are `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + A string parameter is treated the same as an object whose `unit` property value is that string. + - If an object is passed, the following properties are recognized: + - `unit` (string): The unit of time that will be returned. + Valid values are `'year'`, `'month'`, `'week'`, `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + There is no default; `unit` is required. + - `relativeTo` (`Temporal.PlainDateTime` ): The starting point to use when converting between years, months, weeks, and days. + It must be a `Temporal.PlainDateTime`, or a value that can be passed to `Temporal.PlainDateTime.from()`. **Returns:** a floating-point number representing the number of desired units in the `Temporal.Duration`. @@ -535,10 +541,11 @@ If the duration IS NOT evenly divisible by the desired unit, then a fractional r If the duration IS evenly divisible by the desired unit, then the integer result will be identical to `duration.round({ smallestUnit: unit, largestUnit: unit, relativeTo })[unit]`. Interpreting years, months, or weeks requires a reference point. -Therefore, `unit` is `'year'`, `'month'`, or `'week'`, or the duration has nonzero 'year', 'month', or 'week', then the `relativeTo` option is required. +Therefore, if `unit` is `'year'`, `'month'`, or `'week'`, or the duration has nonzero 'year', 'month', or 'week', then the `relativeTo` option is required. +For this reason, it's required to use the object (not string) form of `totalOf` in these cases. The `relativeTo` option gives the starting point used when converting between or rounding to years, months, weeks, or days. -It is a `Temporal.PlainDateTime` instance. +It is a `Temporal.PlainDateTime` or `Temporal.ZonedDateTime` instance. If any other type is provided, then it will be converted to a `Temporal.PlainDateTime` as if it were passed to `Temporal.PlainDateTime.from(..., { overflow: 'reject' })`. A `Temporal.PlainDate` or a date string like `2020-01-01` is also accepted because time is optional when creating a `Temporal.PlainDateTime`. diff --git a/docs/instant.md b/docs/instant.md index 05d3a31d1b..5a30ed2d14 100644 --- a/docs/instant.md +++ b/docs/instant.md @@ -474,25 +474,28 @@ billion = Temporal.Instant.fromEpochSeconds(1e9); billion.since(epoch); // => PT1000000000S ``` -### instant.**round**(_options_: object) : Temporal.Instant +### instant.**round**(_roundTo_: string | object) : Temporal.Instant **Parameters:** -- `options` (object): An object with properties representing options for the operation. - The following options are recognized: - - `smallestUnit` (required string): The unit to round to. +- `roundTo` (string | object): A required string or object to control the operation. + - If a string is provided, the resulting `Temporal.Instant` object will be rounded to that unit. Valid values are `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. - - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. - The default is 1. - - `roundingMode` (string): How to handle the remainder. - Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. - The default is `'halfExpand'`. - -**Returns:** a new `Temporal.Instant` object which is `instant` rounded to `roundingIncrement` of `smallestUnit`. + A string parameter is treated the same as an object whose `smallestUnit` property value is that string. + - If an object is passed, the following properties are recognized: + - `smallestUnit` (required string): The unit to round to. + Valid values are `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. + The default is 1. + - `roundingMode` (string): How to handle the remainder. + Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. + The default is `'halfExpand'`. + +**Returns:** a new `Temporal.Instant` object which is `instant` rounded to `roundTo` (if a string parameter is used) or `roundingIncrement` of `smallestUnit` (if an object parameter is used). Rounds `instant` to the given unit and increment, and returns the result as a new `Temporal.Instant` object. -The `smallestUnit` option determines the unit to round to. +The `smallestUnit` option (or the value of `roundTo` if a string parameter is used) determines the unit to round to. For example, to round to the nearest minute, use `smallestUnit: 'minute'`. This option is required. @@ -511,6 +514,10 @@ The `roundingMode` option controls how the rounding is performed. - `halfExpand`: Round to the nearest of the values allowed by `roundingIncrement` and `smallestUnit`. When there is a tie, round up, like `ceil`. +As expected for a method named "round", the default rounding mode is `'halfExpand'` to match the behavior of `Math.round`. +Note that this is different than the `'trunc'` default used by `until` and `since` options because rounding up would be an unexpected default for those operations. +Other properties behave identically between these methods. + Example usage: diff --git a/docs/plaindatetime.md b/docs/plaindatetime.md index 6037fb0c62..bd0b79ba02 100644 --- a/docs/plaindatetime.md +++ b/docs/plaindatetime.md @@ -750,25 +750,28 @@ dt2 = Temporal.PlainDateTime.from('2019-01-31T15:30'); dt2.since(dt1); // => P8456DT12H5M29.9999965S ``` -### datetime.**round**(_options_: object) : Temporal.PlainDateTime +### datetime.**round**(_roundTo_: string | object) : Temporal.PlainDateTime **Parameters:** -- `options` (object): An object with properties representing options for the operation. - The following options are recognized: - - `smallestUnit` (required string): The unit to round to. +- `roundTo` (string | object): A required string or object to control the operation. + - If a string is provided, the resulting `Temporal.PlainDateTime` object will be rounded to that unit. Valid values are `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. - - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. - The default is 1. - - `roundingMode` (string): How to handle the remainder. - Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. - The default is `'halfExpand'`. - -**Returns:** a new `Temporal.PlainDateTime` object which is `datetime` rounded to `roundingIncrement` of `smallestUnit`. + A string parameter is treated the same as an object whose `smallestUnit` property value is that string. + - If an object is passed, the following properties are recognized: + - `smallestUnit` (required string): The unit to round to. + Valid values are `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. + The default is 1. + - `roundingMode` (string): How to handle the remainder. + Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. + The default is `'halfExpand'`. + +**Returns:** a new `Temporal.PlainDateTime` object which is `datetime` rounded to `roundTo` (if a string parameter is used) or `roundingIncrement` of `smallestUnit` (if an object parameter is used). Rounds `datetime` to the given unit and increment, and returns the result as a new `Temporal.PlainDateTime` object. -The `smallestUnit` option determines the unit to round to. +The `smallestUnit` option (or the value of `roundTo` if a string parameter is used) determines the unit to round to. For example, to round to the nearest minute, use `smallestUnit: 'minute'`. This option is required. @@ -790,6 +793,10 @@ The `roundingMode` option controls how the rounding is performed. - `halfExpand`: Round to the nearest of the values allowed by `roundingIncrement` and `smallestUnit`. When there is a tie, round up, like `ceil`. +As expected for a method named "round", the default rounding mode is `'halfExpand'` to match the behavior of `Math.round`. +Note that this is different than the `'trunc'` default used by `until` and `since` options because rounding up would be an unexpected default for those operations. +Other properties behave identically between these methods. + Example usage: diff --git a/docs/plaintime.md b/docs/plaintime.md index b027b5206f..5abe61d171 100644 --- a/docs/plaintime.md +++ b/docs/plaintime.md @@ -374,25 +374,28 @@ time.since(Temporal.PlainTime.from('19:39:09.068346205')); // => PT34M11.9030518 time.since(Temporal.PlainTime.from('22:39:09.068346205')); // => -PT2H25M48.096948106S ``` -### time.**round**(_options_: object) : Temporal.PlainTime +### time.**round**(_roundTo_: string | object) : Temporal.PlainTime **Parameters:** -- `options` (object): An object with properties representing options for the operation. - The following options are recognized: - - `smallestUnit` (required string): The unit to round to. +- `roundTo` (string | object): A required string or object to control the operation. + - If a string is provided, the resulting `Temporal.PlainTime` object will be rounded to that unit. Valid values are `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. - - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. - The default is 1. - - `roundingMode` (string): How to handle the remainder. - Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. - The default is `'halfExpand'`. - -**Returns:** a new `Temporal.PlainTime` object which is `time` rounded to `roundingIncrement` of `smallestUnit`. + A string parameter is treated the same as an object whose `smallestUnit` property value is that string. + - If an object is passed, the following properties are recognized: + - `smallestUnit` (required string): The unit to round to. + Valid values are `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. + The default is 1. + - `roundingMode` (string): How to handle the remainder. + Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. + The default is `'halfExpand'`. + +**Returns:** a new `Temporal.PlainTime` object which is `time` rounded to `roundTo` (if a string parameter is used) or `roundingIncrement` of `smallestUnit` (if an object parameter is used). Rounds `time` to the given unit and increment, and returns the result as a new `Temporal.PlainTime` object. -The `smallestUnit` option determines the unit to round to. +The `smallestUnit` option (or the value of `roundTo` if a string parameter is used) determines the unit to round to. For example, to round to the nearest minute, use `smallestUnit: 'minute'`. This option is required. @@ -412,6 +415,10 @@ The `roundingMode` option controls how the rounding is performed. - `halfExpand`: Round to the nearest of the values allowed by `roundingIncrement` and `smallestUnit`. When there is a tie, round up, like `ceil`. +As expected for a method named "round", the default rounding mode is `'halfExpand'` to match the behavior of `Math.round`. +Note that this is different than the `'trunc'` default used by `until` and `since` options because rounding up would be an unexpected default for those operations. +Other properties behave identically between these methods. + Example usage: diff --git a/docs/zoneddatetime.md b/docs/zoneddatetime.md index f4241f4328..7fa4528933 100644 --- a/docs/zoneddatetime.md +++ b/docs/zoneddatetime.md @@ -1132,24 +1132,28 @@ zdt2 = Temporal.ZonedDateTime.from('2019-01-31T15:30+05:30[Asia/Kolkata]'); zdt2.since(zdt1); // => PT202956H5M29.9999965S ``` -### zonedDateTime.**round**(_options_: object) : Temporal.ZonedDateTime +### zonedDateTime.**round**(_roundTo_: string | object) : Temporal.ZonedDateTime **Parameters:** -- `options` (object): An object which may have some or all of the following properties: - - `smallestUnit` (required string): The unit to round to. +- `roundTo` (string | object): A required string or object to control the operation. + - If a string is provided, the resulting `Temporal.ZonedDateTime` object will be rounded to that unit. Valid values are `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. - - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. - The default is 1. - - `roundingMode` (string): How to handle the remainder. - Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. - The default is `'halfExpand'`. - -**Returns:** a new `Temporal.ZonedDateTime` object which is `zonedDateTime` rounded to `roundingIncrement` of `smallestUnit`. + A string parameter is treated the same as an object whose `smallestUnit` property value is that string. + - If an object is passed, the following properties are recognized: + - `smallestUnit` (required string): The unit to round to. + Valid values are `'day'`, `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, and `'nanosecond'`. + - `roundingIncrement` (number): The granularity to round to, of the unit given by `smallestUnit`. + The default is 1. + - `roundingMode` (string): How to handle the remainder. + Valid values are `'halfExpand'`, `'ceil'`, `'trunc'`, and `'floor'`. + The default is `'halfExpand'`. + +**Returns:** a new `Temporal.ZonedDateTime` object which is `zonedDateTime` rounded to `roundTo` (if a string parameter is used) or `roundingIncrement` of `smallestUnit` (if an object parameter is used). Rounds `zonedDateTime` to the given unit and increment, and returns the result as a new `Temporal.ZonedDateTime` object. -The `smallestUnit` option determines the unit to round to. +The `smallestUnit` option (or the value of `roundTo` if a string parameter is used) determines the unit to round to. For example, to round to the nearest minute, use `smallestUnit: 'minute'`. This option is required. @@ -1171,6 +1175,10 @@ The `roundingMode` option controls how the rounding is performed. - `'halfExpand'`: Round to the nearest of the values allowed by `roundingIncrement` and `smallestUnit`. When there is a tie, round up, like `'ceil'`. +As expected for a method named "round", the default rounding mode is `'halfExpand'` to match the behavior of `Math.round`. +Note that this is different than the `'trunc'` default used by `until` and `since` options because rounding up would be an unexpected default for those operations. +Other properties behave identically between these methods. + Example usage: diff --git a/polyfill/index.d.ts b/polyfill/index.d.ts index edd8a3e8af..002cb4d1ec 100644 --- a/polyfill/index.d.ts +++ b/polyfill/index.d.ts @@ -146,16 +146,16 @@ export namespace Temporal { nanosecond: 'nanoseconds'; }[T]; - export type LargestUnitOption = 'auto' | T | PluralUnit; - export type SmallestUnitOption = T | PluralUnit; - export type TotalUnitOption = T | PluralUnit; + export type LargestUnit = 'auto' | T | PluralUnit; + export type SmallestUnit = T | PluralUnit; + export type TotalUnit = T | PluralUnit; /** * Options for outputting precision in toString() on types with seconds */ export type ToStringPrecisionOptions = { fractionalSecondDigits?: 'auto' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9; - smallestUnit?: SmallestUnitOption<'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'>; + smallestUnit?: SmallestUnit<'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'>; /** * Controls how rounding is performed: @@ -198,9 +198,21 @@ export namespace Temporal { * `Temporal` types. */ export interface DifferenceOptions { + /** + * The unit to round to. For example, to round to the nearest minute, use + * `smallestUnit: 'minute'`. This property is optional for `until()` and + * `since()`, because those methods default behavior is not to round. + * However, the same property is required for `round()`. + */ + smallestUnit?: SmallestUnit; + /** * The largest unit to allow in the resulting `Temporal.Duration` object. * + * Larger units will be "balanced" into smaller units. For example, if + * `largestUnit` is `'minute'` then a two-hour duration will be output as a + * 120-minute duration. + * * Valid values may include `'year'`, `'month'`, `'week'`, `'day'`, * `'hour'`, `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, * `'nanosecond'` and `'auto'`, although some types may throw an exception @@ -210,14 +222,7 @@ export namespace Temporal { * The default is always `'auto'`, though the meaning of this depends on the * type being used. */ - largestUnit?: LargestUnitOption; - - /** - * The unit to round to. For example, to round to the nearest minute, use - * `smallestUnit: 'minute'`. This option is required for `round()` and - * optional for `until()` and `since()`. - */ - smallestUnit?: SmallestUnitOption; + largestUnit?: LargestUnit; /** * Allows rounding to an integer number of units. For example, to round to @@ -246,152 +251,213 @@ export namespace Temporal { } /** - * Options to control rounding behavior + * `round` methods take one required parameter. If a string is provided, the + * resulting `Temporal.Duration` object will be rounded to that unit. If an + * object is provided, its `smallestUnit` property is required while other + * properties are optional. A string is treated the same as an object whose + * `smallestUnit` property value is that string. */ - export interface RoundOptions { - /** - * The unit to round to. For example, to round to the nearest minute, use - * `smallestUnit: 'minute'`. This option is required. - */ - smallestUnit: T; - - /** - * Allows rounding to an integer number of units. For example, to round to - * increments of a half hour, use `{ smallestUnit: 'minute', - * roundingIncrement: 30 }`. - */ - roundingIncrement?: number; - - /** - * Controls how rounding is performed: - * - `halfExpand`: Round to the nearest of the values allowed by - * `roundingIncrement` and `smallestUnit`. When there is a tie, round up. - * This mode is the default. - * - `ceil`: Always round up, towards the end of time. - * - `trunc`: Always round down, towards the beginning of time. - * - `floor`: Also round down, towards the beginning of time. This mode acts - * the same as `trunc`, but it's included for consistency with - * `Temporal.Duration.round()` where negative values are allowed and - * `trunc` rounds towards zero, unlike `floor` which rounds towards - * negative infinity which is usually unexpected. For this reason, `trunc` - * is recommended for most use cases. - */ - roundingMode?: RoundingMode; - } - - export interface DurationRoundOptions { - /** - * The largest unit to allow in the resulting `Temporal.Duration` object. - * - * Valid values include `'year'`, `'month'`, `'week'`, `'day'`, `'hour'`, - * `'minute'`, `'second'`, `'millisecond'`, `'microsecond'`, `'nanosecond'` - * and `'auto'`. - * - * The default is `'auto'`, which means "the largest nonzero unit in the - * input duration". This default prevents expanding durations to larger - * units unless the caller opts into this behavior. - * - * If `smallestUnit` is larger, then `smallestUnit` will be used as - * `largestUnit`, superseding a caller-supplied or default value. - */ - largestUnit: LargestUnitOption; - - /** - * The unit to round to. For example, to round to the nearest minute, use - * `smallestUnit: 'minute'`. This option is required. - */ - smallestUnit: SmallestUnitOption; - /** - * Allows rounding to an integer number of units. For example, to round to - * increments of a half hour, use `{ smallestUnit: 'minute', - * roundingIncrement: 30 }`. - */ - roundingIncrement?: number; - - /** - * Controls how rounding is performed: - * - `halfExpand`: Round to the nearest of the values allowed by - * `roundingIncrement` and `smallestUnit`. When there is a tie, round away - * from zero like `ceil` for positive durations and like `floor` for - * negative durations. This mode is the default. - * - `ceil`: Always round towards positive infinity. For negative durations - * this option will decrease the absolute value of the duration which may - * be unexpected. To round away from zero, use `ceil` for positive - * durations and `floor` for negative durations. - * - `trunc`: Always round down towards zero. - * - `floor`: Always round towards negative infinity. This mode acts the - * same as `trunc` for positive durations but for negative durations it - * will increase the absolute value of the result which may be unexpected. - * For this reason, `trunc` is recommended for most "round down" use - * cases. - */ - roundingMode?: RoundingMode; + export type RoundTo = + | T + | { + /** + * The unit to round to. For example, to round to the nearest minute, + * use `smallestUnit: 'minute'`. This option is required. Note that the + * same-named property is optional when passed to `until` or `since` + * methods, because those methods do no rounding by default. + */ + smallestUnit: T; + + /** + * Allows rounding to an integer number of units. For example, to round to + * increments of a half hour, use `{ smallestUnit: 'minute', + * roundingIncrement: 30 }`. + */ + roundingIncrement?: number; + + /** + * Controls how rounding is performed: + * - `halfExpand`: Round to the nearest of the values allowed by + * `roundingIncrement` and `smallestUnit`. When there is a tie, round up. + * This mode is the default. + * - `ceil`: Always round up, towards the end of time. + * - `trunc`: Always round down, towards the beginning of time. + * - `floor`: Also round down, towards the beginning of time. This mode acts + * the same as `trunc`, but it's included for consistency with + * `Temporal.Duration.round()` where negative values are allowed and + * `trunc` rounds towards zero, unlike `floor` which rounds towards + * negative infinity which is usually unexpected. For this reason, `trunc` + * is recommended for most use cases. + */ + roundingMode?: RoundingMode; + }; - /** - * The starting point to use for rounding and conversions when - * variable-length units (years, months, weeks depending on the calendar) - * are involved. This option is required if any of the following are true: - * - `unit` is `'week'` or larger units - * - `this` has a nonzero value for `weeks` or larger units - * - * This value must be either a `Temporal.PlainDateTime`, a - * `Temporal.ZonedDateTime`, or a string or object value that can be passed - * to `from()` of those types. Examples: - * - `'2020-01'01T00:00-08:00[America/Los_Angeles]'` - * - `'2020-01'01'` - * - `Temporal.PlainDate.from('2020-01-01')` - * - * `Temporal.ZonedDateTime` will be tried first because it's more - * specific, with `Temporal.PlainDateTime` as a fallback. - * - * If the value resolves to a `Temporal.ZonedDateTime`, then operation will - * adjust for DST and other time zone transitions. Otherwise (including if - * this option is omitted), then the operation will ignore time zone - * transitions and all days will be assumed to be 24 hours long. - */ - relativeTo?: Temporal.PlainDateTime | Temporal.ZonedDateTime | PlainDateTimeLike | ZonedDateTimeLike | string; - } + /** + * The `round` method of the `Temporal.Duration` accepts one required + * parameter. If a string is provided, the resulting `Temporal.Duration` + * object will be rounded to that unit. If an object is provided, the + * `smallestUnit` and/or `largestUnit` property is required, while other + * properties are optional. A string parameter is treated the same as an + * object whose `smallestUnit` property value is that string. + */ + export type DurationRoundTo = + | DateTimeUnit + | (( + | { + /** + * The unit to round to. For example, to round to the nearest + * minute, use `smallestUnit: 'minute'`. This property is normally + * required, but is optional if `largestUnit` is provided and not + * undefined. + */ + smallestUnit: SmallestUnit; + + /** + * The largest unit to allow in the resulting `Temporal.Duration` + * object. + * + * Larger units will be "balanced" into smaller units. For example, + * if `largestUnit` is `'minute'` then a two-hour duration will be + * output as a 120-minute duration. + * + * Valid values include `'year'`, `'month'`, `'week'`, `'day'`, + * `'hour'`, `'minute'`, `'second'`, `'millisecond'`, + * `'microsecond'`, `'nanosecond'` and `'auto'`. + * + * The default is `'auto'`, which means "the largest nonzero unit in + * the input duration". This default prevents expanding durations to + * larger units unless the caller opts into this behavior. + * + * If `smallestUnit` is larger, then `smallestUnit` will be used as + * `largestUnit`, superseding a caller-supplied or default value. + */ + largestUnit?: LargestUnit; + } + | { + /** + * The unit to round to. For example, to round to the nearest + * minute, use `smallestUnit: 'minute'`. This property is normally + * required, but is optional if `largestUnit` is provided and not + * undefined. + */ + smallestUnit?: SmallestUnit; + + /** + * The largest unit to allow in the resulting `Temporal.Duration` + * object. + * + * Larger units will be "balanced" into smaller units. For example, + * if `largestUnit` is `'minute'` then a two-hour duration will be + * output as a 120-minute duration. + * + * Valid values include `'year'`, `'month'`, `'week'`, `'day'`, + * `'hour'`, `'minute'`, `'second'`, `'millisecond'`, + * `'microsecond'`, `'nanosecond'` and `'auto'`. + * + * The default is `'auto'`, which means "the largest nonzero unit in + * the input duration". This default prevents expanding durations to + * larger units unless the caller opts into this behavior. + * + * If `smallestUnit` is larger, then `smallestUnit` will be used as + * `largestUnit`, superseding a caller-supplied or default value. + */ + largestUnit: LargestUnit; + } + ) & { + /** + * Allows rounding to an integer number of units. For example, to round + * to increments of a half hour, use `{ smallestUnit: 'minute', + * roundingIncrement: 30 }`. + */ + roundingIncrement?: number; + + /** + * Controls how rounding is performed: + * - `halfExpand`: Round to the nearest of the values allowed by + * `roundingIncrement` and `smallestUnit`. When there is a tie, round + * away from zero like `ceil` for positive durations and like `floor` + * for negative durations. This mode is the default. + * - `ceil`: Always round towards positive infinity. For negative + * durations this option will decrease the absolute value of the + * duration which may be unexpected. To round away from zero, use + * `ceil` for positive durations and `floor` for negative durations. + * - `trunc`: Always round down towards zero. + * - `floor`: Always round towards negative infinity. This mode acts the + * same as `trunc` for positive durations but for negative durations + * it will increase the absolute value of the result which may be + * unexpected. For this reason, `trunc` is recommended for most "round + * down" use cases. + */ + roundingMode?: RoundingMode; + + /** + * The starting point to use for rounding and conversions when + * variable-length units (years, months, weeks depending on the + * calendar) are involved. This option is required if any of the + * following are true: + * - `unit` is `'week'` or larger units + * - `this` has a nonzero value for `weeks` or larger units + * + * This value must be either a `Temporal.PlainDateTime`, a + * `Temporal.ZonedDateTime`, or a string or object value that can be + * passed to `from()` of those types. Examples: + * - `'2020-01'01T00:00-08:00[America/Los_Angeles]'` + * - `'2020-01'01'` + * - `Temporal.PlainDate.from('2020-01-01')` + * + * `Temporal.ZonedDateTime` will be tried first because it's more + * specific, with `Temporal.PlainDateTime` as a fallback. + * + * If the value resolves to a `Temporal.ZonedDateTime`, then operation + * will adjust for DST and other time zone transitions. Otherwise + * (including if this option is omitted), then the operation will ignore + * time zone transitions and all days will be assumed to be 24 hours + * long. + */ + relativeTo?: Temporal.PlainDateTime | Temporal.ZonedDateTime | PlainDateTimeLike | ZonedDateTimeLike | string; + }); /** * Options to control behavior of `Duration.prototype.total()` */ - export interface DurationTotalOptions { - /** - * The unit to convert the duration to. This option is required. - */ - unit: TotalUnitOption< - 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond' - >; - - /** - * The starting point to use when variable-length units (years, months, - * weeks depending on the calendar) are involved. This option is required if - * any of the following are true: - * - `unit` is `'week'` or larger units - * - `this` has a nonzero value for `weeks` or larger units - * - * This value must be either a `Temporal.PlainDateTime`, a - * `Temporal.ZonedDateTime`, or a string or object value that can be passed - * to `from()` of those types. Examples: - * - `'2020-01'01T00:00-08:00[America/Los_Angeles]'` - * - `'2020-01'01'` - * - `Temporal.PlainDate.from('2020-01-01')` - * - * `Temporal.ZonedDateTime` will be tried first because it's more - * specific, with `Temporal.PlainDateTime` as a fallback. - * - * If the value resolves to a `Temporal.ZonedDateTime`, then operation will - * adjust for DST and other time zone transitions. Otherwise (including if - * this option is omitted), then the operation will ignore time zone - * transitions and all days will be assumed to be 24 hours long. - */ - relativeTo?: Temporal.PlainDateTime | PlainDateTimeLike | string; - } + export type DurationTotalOf = + | DateTimeUnit + | { + /** + * The unit to convert the duration to. This option is required. + */ + unit: TotalUnit; + + /** + * The starting point to use when variable-length units (years, months, + * weeks depending on the calendar) are involved. This option is required if + * any of the following are true: + * - `unit` is `'week'` or larger units + * - `this` has a nonzero value for `weeks` or larger units + * + * This value must be either a `Temporal.PlainDateTime`, a + * `Temporal.ZonedDateTime`, or a string or object value that can be passed + * to `from()` of those types. Examples: + * - `'2020-01'01T00:00-08:00[America/Los_Angeles]'` + * - `'2020-01'01'` + * - `Temporal.PlainDate.from('2020-01-01')` + * + * `Temporal.ZonedDateTime` will be tried first because it's more + * specific, with `Temporal.PlainDateTime` as a fallback. + * + * If the value resolves to a `Temporal.ZonedDateTime`, then operation will + * adjust for DST and other time zone transitions. Otherwise (including if + * this option is omitted), then the operation will ignore time zone + * transitions and all days will be assumed to be 24 hours long. + */ + relativeTo?: Temporal.ZonedDateTime | Temporal.PlainDateTime | ZonedDateTimeLike | PlainDateTimeLike | string; + }; /** - * Options to control behavior of `Duration.compare()` + * Options to control behavior of `Duration.compare()`, `Duration.add()`, and + * `Duration.subtract()` */ - export interface DurationCompareOptions { + export interface DurationArithmeticOptions { /** * The starting point to use when variable-length units (years, months, * weeks depending on the calendar) are involved. This option is required if @@ -440,7 +506,7 @@ export namespace Temporal { static compare( one: Temporal.Duration | DurationLike | string, two: Temporal.Duration | DurationLike | string, - options?: DurationCompareOptions + options?: DurationArithmeticOptions ): ComparisonResult; constructor( years?: number, @@ -469,10 +535,10 @@ export namespace Temporal { negated(): Temporal.Duration; abs(): Temporal.Duration; with(durationLike: DurationLike): Temporal.Duration; - add(other: Temporal.Duration | DurationLike | string, options?: DurationRoundOptions): Temporal.Duration; - subtract(other: Temporal.Duration | DurationLike | string, options?: DurationRoundOptions): Temporal.Duration; - round(options: DurationRoundOptions): Temporal.Duration; - total(options: DurationTotalOptions): number; + add(other: Temporal.Duration | DurationLike | string, options?: DurationArithmeticOptions): Temporal.Duration; + subtract(other: Temporal.Duration | DurationLike | string, options?: DurationArithmeticOptions): Temporal.Duration; + round(roundTo: DurationRoundTo): Temporal.Duration; + total(totalOf: DurationTotalOf): number; toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string; toJSON(): string; toString(options?: ToStringPrecisionOptions): string; @@ -521,7 +587,7 @@ export namespace Temporal { options?: DifferenceOptions<'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> ): Temporal.Duration; round( - options: RoundOptions<'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> + roundTo: RoundTo<'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> ): Temporal.Instant; toZonedDateTime(calendarAndTimeZone: { timeZone: TimeZoneProtocol | string; @@ -873,7 +939,7 @@ export namespace Temporal { > ): Temporal.Duration; round( - options: RoundOptions<'day' | 'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> + roundTo: RoundTo<'day' | 'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> ): Temporal.PlainDateTime; toZonedDateTime(tzLike: TimeZoneProtocol | string, options?: ToInstantOptions): Temporal.ZonedDateTime; toPlainDate(): Temporal.PlainDate; @@ -1003,7 +1069,7 @@ export namespace Temporal { options?: DifferenceOptions<'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> ): Temporal.Duration; round( - options: RoundOptions<'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> + roundTo: RoundTo<'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> ): Temporal.PlainTime; toPlainDateTime(temporalDate: Temporal.PlainDate | PlainDateLike | string): Temporal.PlainDateTime; toZonedDateTime(timeZoneAndDate: { @@ -1232,9 +1298,7 @@ export namespace Temporal { > ): Temporal.Duration; round( - options: Temporal.RoundOptions< - 'day' | 'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond' - > + roundTo: RoundTo<'day' | 'hour' | 'minute' | 'second' | 'millisecond' | 'microsecond' | 'nanosecond'> ): Temporal.ZonedDateTime; startOfDay(): Temporal.ZonedDateTime; toInstant(): Temporal.Instant; diff --git a/polyfill/lib/duration.mjs b/polyfill/lib/duration.mjs index 6e77a7196a..bd20c71042 100644 --- a/polyfill/lib/duration.mjs +++ b/polyfill/lib/duration.mjs @@ -18,6 +18,8 @@ import { SetSlot } from './slots.mjs'; +const ObjectCreate = Object.create; + export class Duration { constructor( years = 0, @@ -265,9 +267,9 @@ export class Duration { )); return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); } - round(options) { + round(roundTo) { if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver'); - if (options === undefined) throw new TypeError('options parameter is required'); + if (roundTo === undefined) throw new TypeError('options parameter is required'); let years = GetSlot(this, YEARS); let months = GetSlot(this, MONTHS); let weeks = GetSlot(this, WEEKS); @@ -291,15 +293,21 @@ export class Duration { microseconds, nanoseconds ); - options = ES.GetOptionsObject(options); - let smallestUnit = ES.ToSmallestTemporalUnit(options, undefined); + if (ES.Type(roundTo) === 'String') { + const stringParam = roundTo; + roundTo = ObjectCreate(null); + roundTo.smallestUnit = stringParam; + } else { + roundTo = ES.GetOptionsObject(roundTo); + } + let smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined); let smallestUnitPresent = true; if (!smallestUnit) { smallestUnitPresent = false; smallestUnit = 'nanosecond'; } defaultLargestUnit = ES.LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit); - let largestUnit = ES.ToLargestTemporalUnit(options, undefined); + let largestUnit = ES.ToLargestTemporalUnit(roundTo, undefined); let largestUnitPresent = true; if (!largestUnit) { largestUnitPresent = false; @@ -310,9 +318,9 @@ export class Duration { throw new RangeError('at least one of smallestUnit or largestUnit is required'); } ES.ValidateTemporalUnitRange(largestUnit, smallestUnit); - const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); - const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(options, smallestUnit); - let relativeTo = ES.ToRelativeTemporalObject(options); + const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand'); + const roundingIncrement = ES.ToTemporalDateTimeRoundingIncrement(roundTo, smallestUnit); + let relativeTo = ES.ToRelativeTemporalObject(roundTo); ({ years, months, weeks, days } = ES.UnbalanceDurationRelative( years, @@ -374,7 +382,7 @@ export class Duration { return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); } - total(options) { + total(totalOf) { if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver'); let years = GetSlot(this, YEARS); let months = GetSlot(this, MONTHS); @@ -387,11 +395,17 @@ export class Duration { let microseconds = GetSlot(this, MICROSECONDS); let nanoseconds = GetSlot(this, NANOSECONDS); - if (options === undefined) throw new TypeError('options argument is required'); - options = ES.GetOptionsObject(options); - const unit = ES.ToTemporalDurationTotalUnit(options, undefined); + if (totalOf === undefined) throw new TypeError('options argument is required'); + if (ES.Type(totalOf) === 'String') { + const stringParam = totalOf; + totalOf = ObjectCreate(null); + totalOf.unit = stringParam; + } else { + totalOf = ES.GetOptionsObject(totalOf); + } + const unit = ES.ToTemporalDurationTotalUnit(totalOf, undefined); if (unit === undefined) throw new RangeError('unit option is required'); - const relativeTo = ES.ToRelativeTemporalObject(options); + const relativeTo = ES.ToRelativeTemporalObject(totalOf); // Convert larger units down to days ({ years, months, weeks, days } = ES.UnbalanceDurationRelative(years, months, weeks, days, unit, relativeTo)); diff --git a/polyfill/lib/instant.mjs b/polyfill/lib/instant.mjs index f65162c3ea..c0a52d47e3 100644 --- a/polyfill/lib/instant.mjs +++ b/polyfill/lib/instant.mjs @@ -7,6 +7,8 @@ import { EPOCHNANOSECONDS, CreateSlots, GetSlot, SetSlot } from './slots.mjs'; import bigInt from 'big-integer'; +const ObjectCreate = Object.create; + const DISALLOWED_UNITS = ['year', 'month', 'week', 'day']; const MAX_DIFFERENCE_INCREMENTS = { hour: 24, @@ -161,13 +163,19 @@ export class Instant { const Duration = GetIntrinsic('%Temporal.Duration%'); return new Duration(0, 0, 0, 0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); } - round(options) { + round(roundTo) { if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver'); - if (options === undefined) throw new TypeError('options parameter is required'); - options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, DISALLOWED_UNITS); + if (roundTo === undefined) throw new TypeError('options parameter is required'); + if (ES.Type(roundTo) === 'String') { + const stringParam = roundTo; + roundTo = ObjectCreate(null); + roundTo.smallestUnit = stringParam; + } else { + roundTo = ES.GetOptionsObject(roundTo); + } + const smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined, DISALLOWED_UNITS); if (smallestUnit === undefined) throw new RangeError('smallestUnit is required'); - const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); + const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand'); const maximumIncrements = { hour: 24, minute: 1440, @@ -176,7 +184,7 @@ export class Instant { microsecond: 86400e6, nanosecond: 86400e9 }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], true); + const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo, maximumIncrements[smallestUnit], true); const ns = GetSlot(this, EPOCHNANOSECONDS); const roundedNs = ES.RoundInstant(ns, roundingIncrement, smallestUnit, roundingMode); return new Instant(roundedNs); diff --git a/polyfill/lib/plaindatetime.mjs b/polyfill/lib/plaindatetime.mjs index 1f35dc0041..8340183927 100644 --- a/polyfill/lib/plaindatetime.mjs +++ b/polyfill/lib/plaindatetime.mjs @@ -17,6 +17,8 @@ import { GetSlot } from './slots.mjs'; +const ObjectCreate = Object.create; + export class PlainDateTime { constructor( isoYear, @@ -510,13 +512,19 @@ export class PlainDateTime { -nanoseconds ); } - round(options) { + round(roundTo) { if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver'); - if (options === undefined) throw new TypeError('options parameter is required'); - options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, ['year', 'month', 'week']); + if (roundTo === undefined) throw new TypeError('options parameter is required'); + if (ES.Type(roundTo) === 'String') { + const stringParam = roundTo; + roundTo = ObjectCreate(null); + roundTo.smallestUnit = stringParam; + } else { + roundTo = ES.GetOptionsObject(roundTo); + } + const smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined, ['year', 'month', 'week']); if (smallestUnit === undefined) throw new RangeError('smallestUnit is required'); - const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); + const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand'); const maximumIncrements = { day: 1, hour: 24, @@ -526,7 +534,7 @@ export class PlainDateTime { microsecond: 1000, nanosecond: 1000 }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); + const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo, maximumIncrements[smallestUnit], false); let year = GetSlot(this, ISO_YEAR); let month = GetSlot(this, ISO_MONTH); diff --git a/polyfill/lib/plaintime.mjs b/polyfill/lib/plaintime.mjs index 1ac29f758a..f2a5f5007a 100644 --- a/polyfill/lib/plaintime.mjs +++ b/polyfill/lib/plaintime.mjs @@ -22,6 +22,7 @@ import { } from './slots.mjs'; const ObjectAssign = Object.assign; +const ObjectCreate = Object.create; const DISALLOWED_UNITS = ['year', 'month', 'week', 'day']; const MAX_INCREMENTS = { @@ -332,14 +333,20 @@ export class PlainTime { const Duration = GetIntrinsic('%Temporal.Duration%'); return new Duration(0, 0, 0, 0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds); } - round(options) { + round(roundTo) { if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver'); - if (options === undefined) throw new TypeError('options parameter is required'); - options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, DISALLOWED_UNITS); + if (roundTo === undefined) throw new TypeError('options parameter is required'); + if (ES.Type(roundTo) === 'String') { + const stringParam = roundTo; + roundTo = ObjectCreate(null); + roundTo.smallestUnit = stringParam; + } else { + roundTo = ES.GetOptionsObject(roundTo); + } + const smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined, DISALLOWED_UNITS); if (smallestUnit === undefined) throw new RangeError('smallestUnit is required'); - const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, MAX_INCREMENTS[smallestUnit], false); + const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand'); + const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo, MAX_INCREMENTS[smallestUnit], false); let hour = GetSlot(this, ISO_HOUR); let minute = GetSlot(this, ISO_MINUTE); diff --git a/polyfill/lib/zoneddatetime.mjs b/polyfill/lib/zoneddatetime.mjs index 6a49f1dedf..3fc2780016 100644 --- a/polyfill/lib/zoneddatetime.mjs +++ b/polyfill/lib/zoneddatetime.mjs @@ -21,6 +21,7 @@ import { import bigInt from 'big-integer'; const ArrayPrototypePush = Array.prototype.push; +const ObjectCreate = Object.create; export class ZonedDateTime { constructor(epochNanoseconds, timeZone, calendar = ES.GetISO8601Calendar()) { @@ -573,13 +574,19 @@ export class ZonedDateTime { -nanoseconds ); } - round(options) { + round(roundTo) { if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver'); - if (options === undefined) throw new TypeError('options parameter is required'); - options = ES.GetOptionsObject(options); - const smallestUnit = ES.ToSmallestTemporalUnit(options, undefined, ['year', 'month', 'week']); + if (roundTo === undefined) throw new TypeError('options parameter is required'); + if (ES.Type(roundTo) === 'String') { + const stringParam = roundTo; + roundTo = ObjectCreate(null); + roundTo.smallestUnit = stringParam; + } else { + roundTo = ES.GetOptionsObject(roundTo); + } + const smallestUnit = ES.ToSmallestTemporalUnit(roundTo, undefined, ['year', 'month', 'week']); if (smallestUnit === undefined) throw new RangeError('smallestUnit is required'); - const roundingMode = ES.ToTemporalRoundingMode(options, 'halfExpand'); + const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand'); const maximumIncrements = { day: 1, hour: 24, @@ -589,7 +596,7 @@ export class ZonedDateTime { microsecond: 1000, nanosecond: 1000 }; - const roundingIncrement = ES.ToTemporalRoundingIncrement(options, maximumIncrements[smallestUnit], false); + const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo, maximumIncrements[smallestUnit], false); // first, round the underlying DateTime fields const dt = dateTime(this); diff --git a/polyfill/test/duration.mjs b/polyfill/test/duration.mjs index 6f2d3fa65f..824a118b77 100644 --- a/polyfill/test/duration.mjs +++ b/polyfill/test/duration.mjs @@ -977,8 +977,8 @@ describe('Duration', () => { const d = new Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5); const d2 = new Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5); const relativeTo = Temporal.PlainDateTime.from('2020-01-01T00:00'); - it('options may only be an object', () => { - [null, 1, 'hello', true, Symbol('foo'), 1n].forEach((badOptions) => throws(() => d.round(badOptions), TypeError)); + it('parameter may only be an object or string', () => { + [null, 1, true, Symbol('foo'), 1n].forEach((badOptions) => throws(() => d.round(badOptions), TypeError)); }); it('throws without parameter', () => { throws(() => d.round(), TypeError); @@ -989,11 +989,16 @@ describe('Duration', () => { it("succeeds with largestUnit: 'auto'", () => { equal(`${Duration.from({ hours: 25 }).round({ largestUnit: 'auto' })}`, 'PT25H'); }); - it('throws on disallowed or invalid smallestUnit', () => { + it('throws on disallowed or invalid smallestUnit (object param)', () => { ['era', 'nonsense'].forEach((smallestUnit) => { throws(() => d.round({ smallestUnit }), RangeError); }); }); + it('throws on disallowed or invalid smallestUnit (string param)', () => { + ['era', 'nonsense'].forEach((smallestUnit) => { + throws(() => d.round(smallestUnit), RangeError); + }); + }); it('throws if smallestUnit is larger than largestUnit', () => { const units = [ 'years', @@ -1015,6 +1020,24 @@ describe('Duration', () => { } } }); + it('accepts string parameter as a shortcut for {smallestUnit}', () => { + const d = Temporal.Duration.from({ + days: 1, + hours: 2, + minutes: 3, + seconds: 4, + milliseconds: 5, + microseconds: 6, + nanoseconds: 7 + }); + equal(d.round('day').toString(), 'P1D'); + equal(d.round('hour').toString(), 'P1DT2H'); + equal(d.round('minute').toString(), 'P1DT2H3M'); + equal(d.round('second').toString(), 'P1DT2H3M4S'); + equal(d.round('millisecond').toString(), 'P1DT2H3M4.005S'); + equal(d.round('microsecond').toString(), 'P1DT2H3M4.005006S'); + equal(d.round('nanosecond').toString(), 'P1DT2H3M4.005006007S'); + }); it('assumes a different default for largestUnit if smallestUnit is larger than the default', () => { const almostYear = Duration.from({ days: 364 }); equal(`${almostYear.round({ smallestUnit: 'years', relativeTo })}`, 'P1Y'); @@ -1183,12 +1206,26 @@ describe('Duration', () => { }); it('throws if neither one of largestUnit or smallestUnit is given', () => { const hoursOnly = new Duration(0, 0, 0, 0, 1); - [{}, () => {}, { roundingMode: 'ceil' }].forEach((options) => { - throws(() => d.round(options), RangeError); - throws(() => hoursOnly.round(options), RangeError); - }); - }); - it('relativeTo is not required for rounding non-calendar units in durations without calendar units', () => { + [{}, () => {}, { roundingMode: 'ceil' }].forEach((roundTo) => { + throws(() => d.round(roundTo), RangeError); + throws(() => hoursOnly.round(roundTo), RangeError); + }); + }); + it('relativeTo not required to round non-calendar units in durations w/o calendar units (string param)', () => { + equal(`${d2.round('days')}`, 'P5D'); + equal(`${d2.round('hours')}`, 'P5DT5H'); + equal(`${d2.round('minutes')}`, 'P5DT5H5M'); + equal(`${d2.round('seconds')}`, 'P5DT5H5M5S'); + equal(`${d2.round('milliseconds')}`, 'P5DT5H5M5.005S'); + equal(`${d2.round('microseconds')}`, 'P5DT5H5M5.005005S'); + equal(`${d2.round('nanoseconds')}`, 'P5DT5H5M5.005005005S'); + }); + it('relativeTo is required to round calendar units even in durations w/o calendar units (string param)', () => { + throws(() => d2.round('years'), RangeError); + throws(() => d2.round('months'), RangeError); + throws(() => d2.round('weeks'), RangeError); + }); + it('relativeTo not required to round non-calendar units in durations w/o calendar units (object param)', () => { equal(`${d2.round({ smallestUnit: 'days' })}`, 'P5D'); equal(`${d2.round({ smallestUnit: 'hours' })}`, 'P5DT5H'); equal(`${d2.round({ smallestUnit: 'minutes' })}`, 'P5DT5H5M'); @@ -1197,7 +1234,7 @@ describe('Duration', () => { equal(`${d2.round({ smallestUnit: 'microseconds' })}`, 'P5DT5H5M5.005005S'); equal(`${d2.round({ smallestUnit: 'nanoseconds' })}`, 'P5DT5H5M5.005005005S'); }); - it('relativeTo is required for rounding calendar units even in durations without calendar units', () => { + it('relativeTo is required to round calendar units even in durations w/o calendar units (object param)', () => { throws(() => d2.round({ smallestUnit: 'years' }), RangeError); throws(() => d2.round({ smallestUnit: 'months' }), RangeError); throws(() => d2.round({ smallestUnit: 'weeks' }), RangeError); @@ -1390,16 +1427,16 @@ describe('Duration', () => { ['minutes', 'seconds'].forEach((smallestUnit) => { it(`valid ${smallestUnit} increments divide into 60`, () => { [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30].forEach((roundingIncrement) => { - const options = { smallestUnit, roundingIncrement, relativeTo }; - assert(d.round(options) instanceof Temporal.Duration); + const roundTo = { smallestUnit, roundingIncrement, relativeTo }; + assert(d.round(roundTo) instanceof Temporal.Duration); }); }); }); ['milliseconds', 'microseconds', 'nanoseconds'].forEach((smallestUnit) => { it(`valid ${smallestUnit} increments divide into 1000`, () => { [1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 125, 200, 250, 500].forEach((roundingIncrement) => { - const options = { smallestUnit, roundingIncrement, relativeTo }; - assert(d.round(options) instanceof Temporal.Duration); + const roundTo = { smallestUnit, roundingIncrement, relativeTo }; + assert(d.round(roundTo) instanceof Temporal.Duration); }); }); }); @@ -1480,14 +1517,28 @@ describe('Duration', () => { const d = new Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5); const d2 = new Duration(0, 0, 0, 5, 5, 5, 5, 5, 5, 5); const relativeTo = Temporal.PlainDateTime.from('2020-01-01T00:00'); - it('options may only be an object', () => { - [null, 1, 'hello', true, Symbol('foo'), 1n].forEach((badOptions) => throws(() => d.total(badOptions), TypeError)); - }); - it('throws on disallowed or invalid smallestUnit', () => { + it('parameter may only be an object or string', () => { + [null, 1, true, Symbol('foo'), 1n].forEach((badOptions) => throws(() => d.total(badOptions), TypeError)); + }); + it('accepts string parameter as shortcut for {unit}', () => { + equal(d2.total({ unit: 'days' }).toString(), d2.total('days').toString()); + equal(d2.total({ unit: 'hours' }).toString(), d2.total('hours').toString()); + equal(d2.total({ unit: 'minutes' }).toString(), d2.total('minutes').toString()); + equal(d2.total({ unit: 'seconds' }).toString(), d2.total('seconds').toString()); + equal(d2.total({ unit: 'milliseconds' }).toString(), d2.total('milliseconds').toString()); + equal(d2.total({ unit: 'microseconds' }).toString(), d2.total('microseconds').toString()); + equal(d2.total({ unit: 'nanoseconds' }).toString(), d2.total('nanoseconds').toString()); + }); + it('throws on disallowed or invalid unit (object param)', () => { ['era', 'nonsense'].forEach((unit) => { throws(() => d.total({ unit }), RangeError); }); }); + it('throws on disallowed or invalid unit (string param)', () => { + ['era', 'nonsense'].forEach((unit) => { + throws(() => d.total(unit), RangeError); + }); + }); it('does not lose precision for seconds and smaller units', () => { const s = Temporal.Duration.from({ milliseconds: 2, microseconds: 31 }).total({ unit: 'seconds' }); equal(s, 0.002031); @@ -1545,14 +1596,19 @@ describe('Duration', () => { equal(oneMonth.total({ unit: 'months', relativeTo: { year: 2020, month: 1, day: 1, months: 2 } }), 1); }); it('throws RangeError if unit property is missing', () => { - [{}, () => {}, { roundingMode: 'ceil' }].forEach((options) => throws(() => d.total(options), RangeError)); + [{}, () => {}, { roundingMode: 'ceil' }].forEach((roundTo) => throws(() => d.total(roundTo), RangeError)); }); - it('relativeTo is required for rounding calendar units even in durations without calendar units', () => { + it('relativeTo required to round calendar units even in durations w/o calendar units (object param)', () => { throws(() => d2.total({ unit: 'years' }), RangeError); throws(() => d2.total({ unit: 'months' }), RangeError); throws(() => d2.total({ unit: 'weeks' }), RangeError); }); - it('relativeTo is required for rounding durations with calendar units', () => { + it('relativeTo required to round calendar units even in durations w/o calendar units (string param)', () => { + throws(() => d2.total('years'), RangeError); + throws(() => d2.total('months'), RangeError); + throws(() => d2.total('weeks'), RangeError); + }); + it('relativeTo is required to round durations with calendar units (object param)', () => { throws(() => d.total({ unit: 'years' }), RangeError); throws(() => d.total({ unit: 'months' }), RangeError); throws(() => d.total({ unit: 'weeks' }), RangeError); @@ -1564,6 +1620,18 @@ describe('Duration', () => { throws(() => d.total({ unit: 'microseconds' }), RangeError); throws(() => d.total({ unit: 'nanoseconds' }), RangeError); }); + it('relativeTo is required to round durations with calendar units (string param)', () => { + throws(() => d.total('years'), RangeError); + throws(() => d.total('months'), RangeError); + throws(() => d.total('weeks'), RangeError); + throws(() => d.total('days'), RangeError); + throws(() => d.total('hours'), RangeError); + throws(() => d.total('minutes'), RangeError); + throws(() => d.total('seconds'), RangeError); + throws(() => d.total('milliseconds'), RangeError); + throws(() => d.total('microseconds'), RangeError); + throws(() => d.total('nanoseconds'), RangeError); + }); const d2Nanoseconds = d2.days * 24 * 3.6e12 + d2.hours * 3.6e12 + diff --git a/polyfill/test/instant.mjs b/polyfill/test/instant.mjs index 619540b41e..f7eee43637 100644 --- a/polyfill/test/instant.mjs +++ b/polyfill/test/instant.mjs @@ -829,13 +829,20 @@ describe('Instant', () => { throws(() => inst.round({}), RangeError); throws(() => inst.round({ roundingIncrement: 1, roundingMode: 'ceil' }), RangeError); }); - it('throws on disallowed or invalid smallestUnit', () => { + it('throws on disallowed or invalid smallestUnit (object param)', () => { ['era', 'year', 'month', 'week', 'day', 'years', 'months', 'weeks', 'days', 'nonsense'].forEach( (smallestUnit) => { throws(() => inst.round({ smallestUnit }), RangeError); } ); }); + it('throws on disallowed or invalid smallestUnit (string param)', () => { + ['era', 'year', 'month', 'week', 'day', 'years', 'months', 'weeks', 'days', 'nonsense'].forEach( + (smallestUnit) => { + throws(() => inst.round(smallestUnit), RangeError); + } + ); + }); const incrementOneNearest = [ ['hour', '1976-11-18T14:00:00Z'], ['minute', '1976-11-18T14:24:00Z'], @@ -920,6 +927,16 @@ describe('Instant', () => { throws(() => inst.round({ smallestUnit: 'microsecond', roundingIncrement: 29 }), RangeError); throws(() => inst.round({ smallestUnit: 'nanosecond', roundingIncrement: 29 }), RangeError); }); + it('accepts plural units', () => { + ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'].forEach((smallestUnit) => { + assert(inst.round({ smallestUnit }).equals(inst.round({ smallestUnit: `${smallestUnit}s` }))); + }); + }); + it('accepts string parameter as shortcut for {smallestUnit}', () => { + ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'].forEach((smallestUnit) => { + assert(inst.round(smallestUnit).equals(inst.round({ smallestUnit }))); + }); + }); }); describe('Min/max range', () => { it('constructing from ns', () => { diff --git a/polyfill/test/plaindatetime.mjs b/polyfill/test/plaindatetime.mjs index 8f361c50e7..0d401c7b55 100644 --- a/polyfill/test/plaindatetime.mjs +++ b/polyfill/test/plaindatetime.mjs @@ -1104,11 +1104,16 @@ describe('DateTime', () => { throws(() => dt.round({}), RangeError); throws(() => dt.round({ roundingIncrement: 1, roundingMode: 'ceil' }), RangeError); }); - it('throws on disallowed or invalid smallestUnit', () => { + it('throws on disallowed or invalid smallestUnit (object param)', () => { ['era', 'year', 'month', 'week', 'years', 'months', 'weeks', 'nonsense'].forEach((smallestUnit) => { throws(() => dt.round({ smallestUnit }), RangeError); }); }); + it('throws on disallowed or invalid smallestUnit (string param)', () => { + ['era', 'year', 'month', 'week', 'years', 'months', 'weeks', 'nonsense'].forEach((smallestUnit) => { + throws(() => dt.round(smallestUnit), RangeError); + }); + }); it('throws on invalid roundingMode', () => { throws(() => dt.round({ smallestUnit: 'second', roundingMode: 'cile' }), RangeError); }); @@ -1228,13 +1233,14 @@ describe('DateTime', () => { }); }); it('accepts plural units', () => { - assert(dt.round({ smallestUnit: 'days' }).equals(dt.round({ smallestUnit: 'day' }))); - assert(dt.round({ smallestUnit: 'hours' }).equals(dt.round({ smallestUnit: 'hour' }))); - assert(dt.round({ smallestUnit: 'minutes' }).equals(dt.round({ smallestUnit: 'minute' }))); - assert(dt.round({ smallestUnit: 'seconds' }).equals(dt.round({ smallestUnit: 'second' }))); - assert(dt.round({ smallestUnit: 'milliseconds' }).equals(dt.round({ smallestUnit: 'millisecond' }))); - assert(dt.round({ smallestUnit: 'microseconds' }).equals(dt.round({ smallestUnit: 'microsecond' }))); - assert(dt.round({ smallestUnit: 'nanoseconds' }).equals(dt.round({ smallestUnit: 'nanosecond' }))); + ['day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'].forEach((smallestUnit) => { + assert(dt.round({ smallestUnit }).equals(dt.round({ smallestUnit: `${smallestUnit}s` }))); + }); + }); + it('accepts string parameter as shortcut for {smallestUnit}', () => { + ['day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'].forEach((smallestUnit) => { + assert(dt.round(smallestUnit).equals(dt.round({ smallestUnit }))); + }); }); }); describe('DateTime.from() works', () => { diff --git a/polyfill/test/plaintime.mjs b/polyfill/test/plaintime.mjs index b1a58a81b0..a741a74656 100644 --- a/polyfill/test/plaintime.mjs +++ b/polyfill/test/plaintime.mjs @@ -563,13 +563,20 @@ describe('Time', () => { throws(() => time.round({}), RangeError); throws(() => time.round({ roundingIncrement: 1, roundingMode: 'ceil' }), RangeError); }); - it('throws on disallowed or invalid smallestUnit', () => { + it('throws on disallowed or invalid smallestUnit (object param)', () => { ['era', 'year', 'month', 'week', 'day', 'years', 'months', 'weeks', 'days', 'nonsense'].forEach( (smallestUnit) => { throws(() => time.round({ smallestUnit }), RangeError); } ); }); + it('throws on disallowed or invalid smallestUnit (string param)', () => { + ['era', 'year', 'month', 'week', 'day', 'years', 'months', 'weeks', 'days', 'nonsense'].forEach( + (smallestUnit) => { + throws(() => time.round(smallestUnit), RangeError); + } + ); + }); it('throws on invalid roundingMode', () => { throws(() => time.round({ smallestUnit: 'second', roundingMode: 'cile' }), RangeError); }); @@ -676,12 +683,14 @@ describe('Time', () => { }); }); it('accepts plural units', () => { - assert(time.round({ smallestUnit: 'hours' }).equals(time.round({ smallestUnit: 'hour' }))); - assert(time.round({ smallestUnit: 'minutes' }).equals(time.round({ smallestUnit: 'minute' }))); - assert(time.round({ smallestUnit: 'seconds' }).equals(time.round({ smallestUnit: 'second' }))); - assert(time.round({ smallestUnit: 'milliseconds' }).equals(time.round({ smallestUnit: 'millisecond' }))); - assert(time.round({ smallestUnit: 'microseconds' }).equals(time.round({ smallestUnit: 'microsecond' }))); - assert(time.round({ smallestUnit: 'nanoseconds' }).equals(time.round({ smallestUnit: 'nanosecond' }))); + ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'].forEach((smallestUnit) => { + assert(time.round({ smallestUnit }).equals(time.round({ smallestUnit: `${smallestUnit}s` }))); + }); + }); + it('accepts string parameter as shortcut for {smallestUnit}', () => { + ['hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'].forEach((smallestUnit) => { + assert(time.round(smallestUnit).equals(time.round({ smallestUnit }))); + }); }); }); describe('Time.compare() works', () => { diff --git a/polyfill/test/zoneddatetime.mjs b/polyfill/test/zoneddatetime.mjs index c5df60dba9..d8f3e96322 100644 --- a/polyfill/test/zoneddatetime.mjs +++ b/polyfill/test/zoneddatetime.mjs @@ -1959,11 +1959,16 @@ describe('ZonedDateTime', () => { throws(() => zdt.round({}), RangeError); throws(() => zdt.round({ roundingIncrement: 1, roundingMode: 'ceil' }), RangeError); }); - it('throws on disallowed or invalid smallestUnit', () => { + it('throws on disallowed or invalid smallestUnit (object param)', () => { ['era', 'year', 'month', 'week', 'years', 'months', 'weeks', 'nonsense'].forEach((smallestUnit) => { throws(() => zdt.round({ smallestUnit }), RangeError); }); }); + it('throws on disallowed or invalid smallestUnit (string param)', () => { + ['era', 'year', 'month', 'week', 'years', 'months', 'weeks', 'nonsense'].forEach((smallestUnit) => { + throws(() => zdt.round(smallestUnit), RangeError); + }); + }); it('throws on invalid roundingMode', () => { throws(() => zdt.round({ smallestUnit: 'second', roundingMode: 'cile' }), RangeError); }); @@ -2107,26 +2112,28 @@ describe('ZonedDateTime', () => { }); }); it('accepts plural units', () => { - assert(zdt.round({ smallestUnit: 'hours' }).equals(zdt.round({ smallestUnit: 'hour' }))); - assert(zdt.round({ smallestUnit: 'minutes' }).equals(zdt.round({ smallestUnit: 'minute' }))); - assert(zdt.round({ smallestUnit: 'seconds' }).equals(zdt.round({ smallestUnit: 'second' }))); - assert(zdt.round({ smallestUnit: 'milliseconds' }).equals(zdt.round({ smallestUnit: 'millisecond' }))); - assert(zdt.round({ smallestUnit: 'microseconds' }).equals(zdt.round({ smallestUnit: 'microsecond' }))); - assert(zdt.round({ smallestUnit: 'nanoseconds' }).equals(zdt.round({ smallestUnit: 'nanosecond' }))); + ['day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'].forEach((smallestUnit) => { + assert(zdt.round({ smallestUnit }).equals(zdt.round({ smallestUnit: `${smallestUnit}s` }))); + }); + }); + it('accepts string parameter as shortcut for {smallestUnit}', () => { + ['day', 'hour', 'minute', 'second', 'millisecond', 'microsecond', 'nanosecond'].forEach((smallestUnit) => { + assert(zdt.round(smallestUnit).equals(zdt.round({ smallestUnit }))); + }); }); it('rounds correctly to a 25-hour day', () => { - const options = { smallestUnit: 'day' }; + const roundTo = { smallestUnit: 'day' }; const roundMeDown = ZonedDateTime.from('2020-11-01T12:29:59-08:00[America/Vancouver]'); - equal(`${roundMeDown.round(options)}`, '2020-11-01T00:00:00-07:00[America/Vancouver]'); + equal(`${roundMeDown.round(roundTo)}`, '2020-11-01T00:00:00-07:00[America/Vancouver]'); const roundMeUp = ZonedDateTime.from('2020-11-01T12:30:01-08:00[America/Vancouver]'); - equal(`${roundMeUp.round(options)}`, '2020-11-02T00:00:00-08:00[America/Vancouver]'); + equal(`${roundMeUp.round(roundTo)}`, '2020-11-02T00:00:00-08:00[America/Vancouver]'); }); it('rounds correctly to a 23-hour day', () => { - const options = { smallestUnit: 'day' }; + const roundTo = { smallestUnit: 'day' }; const roundMeDown = ZonedDateTime.from('2020-03-08T11:29:59-07:00[America/Vancouver]'); - equal(`${roundMeDown.round(options)}`, '2020-03-08T00:00:00-08:00[America/Vancouver]'); + equal(`${roundMeDown.round(roundTo)}`, '2020-03-08T00:00:00-08:00[America/Vancouver]'); const roundMeUp = ZonedDateTime.from('2020-03-08T11:30:01-07:00[America/Vancouver]'); - equal(`${roundMeUp.round(options)}`, '2020-03-09T00:00:00-07:00[America/Vancouver]'); + equal(`${roundMeUp.round(roundTo)}`, '2020-03-09T00:00:00-07:00[America/Vancouver]'); }); it('rounding up to a nonexistent wall-clock time', () => { const almostSkipped = ZonedDateTime.from('2018-11-03T23:59:59.999999999-03:00[America/Sao_Paulo]'); diff --git a/polyfill/test262 b/polyfill/test262 index ae53326189..cba42e88c7 160000 --- a/polyfill/test262 +++ b/polyfill/test262 @@ -1 +1 @@ -Subproject commit ae53326189f8d23380f0d5b5bcc0be0a61d33a4f +Subproject commit cba42e88c72c7df634074e8f01f6cba430210909 diff --git a/spec/duration.html b/spec/duration.html index 02fe7b54e8..bb29b633dd 100644 --- a/spec/duration.html +++ b/spec/duration.html @@ -409,26 +409,31 @@

Temporal.Duration.prototype.subtract ( _other_ [ , _options_ ] )

-

Temporal.Duration.prototype.round ( _options_ )

+

Temporal.Duration.prototype.round ( _roundTo_ )

- The `round` method takes one argument _options_. + The `round` method takes one argument _roundTo_. The following steps are taken:

1. Let _duration_ be the *this* value. 1. Perform ? RequireInternalSlot(_duration_, [[InitializedTemporalDuration]]). - 1. If _options_ is *undefined*, then + 1. If _roundTo_ is *undefined*, then 1. Throw a *TypeError* exception. - 1. Set _options_ to ? GetOptionsObject(_options_). + 1. If Type(_roundTo_) is String, then + 1. Let _paramString_ be _roundTo_. + 1. Set _roundTo_ to ! OrdinaryObjectCreate(*null*). + 1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"_smallestUnit_"*, _paramString_). + 1. Else, + 1. Set _roundTo_ to ? GetOptionsObject(_roundTo_). 1. Let _smallestUnitPresent_ be *true*. 1. Let _largestUnitPresent_ be *true*. - 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « », *undefined*). + 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « », *undefined*). 1. If _smallestUnit_ is *undefined*, then 1. Set _smallestUnitPresent_ to *false*. 1. Set _smallestUnit_ to *"nanosecond"*. 1. Let _defaultLargestUnit_ be ! DefaultTemporalLargestUnit(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]]). 1. Set _defaultLargestUnit_ to ! LargerOfTwoTemporalUnits(_defaultLargestUnit_, _smallestUnit_). - 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_options_, « », *undefined*). + 1. Let _largestUnit_ be ? ToLargestTemporalUnit(_roundTo_, « », *undefined*). 1. If _largestUnit_ is *undefined*, then 1. Set _largestUnitPresent_ to *false*. 1. Set _largestUnit_ to _defaultLargestUnit_. @@ -437,10 +442,10 @@

Temporal.Duration.prototype.round ( _options_ )

1. If _smallestUnitPresent_ is *false* and _largestUnitPresent_ is *false*, then 1. Throw a *RangeError* exception. 1. Perform ? ValidateTemporalUnitRange(_largestUnit_, _smallestUnit_). - 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"halfExpand"*). + 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*). 1. Let _maximum_ be ! MaximumTemporalDurationRoundingIncrement(_smallestUnit_). - 1. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). - 1. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + 1. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_roundTo_, _maximum_, *false*). + 1. Let _relativeTo_ be ? ToRelativeTemporalObject(_roundTo_). 1. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _relativeTo_). 1. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_[[Seconds]], _duration_[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). 1. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). @@ -453,18 +458,23 @@

Temporal.Duration.prototype.round ( _options_ )

-

Temporal.Duration.prototype.total ( _options_ )

+

Temporal.Duration.prototype.total ( _totalOf_ )

- The `total` method takes one argument _options_. + The `total` method takes one argument _totalOf_. The following steps are taken:

1. Let _duration_ be the *this* value. 1. Perform ? RequireInternalSlot(_duration_, [[InitializedTemporalDuration]]). - 1. If _options_ is *undefined*, throw a *TypeError* exception. - 1. Set _options_ to ? GetOptionsObject(_options_). - 1. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). - 1. Let _unit_ be ? ToTemporalDurationTotalUnit(_options_). + 1. If _totalOf_ is *undefined*, throw a *TypeError* exception. + 1. If Type(_totalOf_) is String, then + 1. Let _paramString_ be _totalOf_. + 1. Set _totalOf_ to ! OrdinaryObjectCreate(*null*). + 1. Perform ! CreateDataPropertyOrThrow(_totalOf_, *"unit"*, _paramString_). + 1. Else, + 1. Set _totalOf_ to ? GetOptionsObject(_totalOf_). + 1. Let _relativeTo_ be ? ToRelativeTemporalObject(_totalOf_). + 1. Let _unit_ be ? ToTemporalDurationTotalUnit(_totalOf_). 1. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _relativeTo_). 1. Let _intermediate_ be *undefined*. 1. If _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot, then diff --git a/spec/instant.html b/spec/instant.html index 9e301177a3..6cca7d6e70 100644 --- a/spec/instant.html +++ b/spec/instant.html @@ -296,20 +296,25 @@

Temporal.Instant.prototype.since ( _other_ [ , _options_ ] )

-

Temporal.Instant.prototype.round ( _options_ )

+

Temporal.Instant.prototype.round ( _roundTo_ )

- The `round` method takes one argument _options_. + The `round` method takes one argument _roundTo_. The following steps are taken:

1. Let _instant_ be the *this* value. 1. Perform ? RequireInternalSlot(_instant_, [[InitializedTemporalInstant]]). - 1. If _options_ is *undefined*, then + 1. If _roundTo_ is *undefined*, then 1. Throw a *TypeError* exception. - 1. Set _options_ to ? GetOptionsObject(_options_). - 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « *"year"*, *"month"*, *"week"*, *"day"* », *undefined*). + 1. If Type(_roundTo_) is String, then + 1. Let _paramString_ be _roundTo_. + 1. Set _roundTo_ to ! OrdinaryObjectCreate(*null*). + 1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"_smallestUnit_"*, _paramString_). + 1. Else, + 1. Set _roundTo_ to ? GetOptionsObject(_roundTo_). + 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « *"year"*, *"month"*, *"week"*, *"day"* », *undefined*). 1. If _smallestUnit_ is *undefined*, throw a *RangeError* exception. - 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"halfExpand"*). + 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*). 1. If _smallestUnit_ is *"hour"*, then 1. Let _maximum_ be 24. 1. Else if _smallestUnit_ is *"minute"*, then @@ -323,7 +328,7 @@

Temporal.Instant.prototype.round ( _options_ )

1. Else, 1. Assert: _smallestUnit_ is *"nanosecond"*. 1. Let _maximum_ be 8.64 × 1013. - 1. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *true*). + 1. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_roundTo_, _maximum_, *true*). 1. Let _roundedNs_ be ! RoundTemporalInstant(_instant_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_). 1. Return ! CreateTemporalInstant(_roundedNs_).
diff --git a/spec/plaindatetime.html b/spec/plaindatetime.html index 0e44e142cc..82a8ae662b 100644 --- a/spec/plaindatetime.html +++ b/spec/plaindatetime.html @@ -533,21 +533,26 @@

Temporal.PlainDateTime.prototype.since ( _other_ [ , _options_ ] )

-

Temporal.PlainDateTime.prototype.round ( _options_ )

+

Temporal.PlainDateTime.prototype.round ( _roundTo_ )

- The `round` method takes one argument _options_. + The `round` method takes one argument _roundTo_. The following steps are taken:

1. Let _dateTime_ be the *this* value. 1. Perform ? RequireInternalSlot(_dateTime_, [[InitializedTemporalDateTime]]). - 1. If _options_ is *undefined*, then + 1. If _roundTo_ is *undefined*, then 1. Throw a *TypeError* exception. - 1. Set _options_ to ? GetOptionsObject(_options_). - 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « *"year"*, *"month"*, *"week"* », *undefined*). + 1. If Type(_roundTo_) is String, then + 1. Let _paramString_ be _roundTo_. + 1. Set _roundTo_ to ! OrdinaryObjectCreate(*null*). + 1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"_smallestUnit_"*, _paramString_). + 1. Else, + 1. Set _roundTo_ to ? GetOptionsObject(_roundTo_). + 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « *"year"*, *"month"*, *"week"* », *undefined*). 1. If _smallestUnit_ is *undefined*, throw a *RangeError* exception. - 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"halfExpand"*). - 1. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_). + 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*). + 1. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_roundTo_, _smallestUnit_). 1. Let _result_ be ! RoundISODateTime(_dateTime_.[[ISOYear]], _dateTime_.[[ISOMonth]], _dateTime_.[[ISODay]], _dateTime_.[[ISOHour]], _dateTime_.[[ISOMinute]], _dateTime_.[[ISOSecond]], _dateTime_.[[ISOMillisecond]], _dateTime_.[[ISOMicrosecond]], _dateTime_.[[ISONanosecond]], _roundingIncrement_, _smallestUnit_, _roundingMode_). 1. Return ? CreateTemporalDateTime(_result_.[[Year]], _result_.[[Month]], _result_.[[Day]], _result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]], _dateTime_.[[Calendar]]). diff --git a/spec/plaintime.html b/spec/plaintime.html index aeffecb9a9..f1a4531693 100644 --- a/spec/plaintime.html +++ b/spec/plaintime.html @@ -327,27 +327,32 @@

Temporal.PlainTime.prototype.since ( _other_ [ , _options_ ] )

-

Temporal.PlainTime.prototype.round ( _options_ )

+

Temporal.PlainTime.prototype.round ( _roundTo_ )

- The `round` method takes one argument _options_. + The `round` method takes one argument _roundTo_. The following steps are taken:

1. Let _temporalTime_ be the *this* value. 1. Perform ? RequireInternalSlot(_temporalTime_, [[InitializedTemporalTime]]). - 1. If _options_ is *undefined*, then + 1. If _roundTo_ is *undefined*, then 1. Throw a *TypeError* exception. - 1. Set _options_ to ? GetOptionsObject(_options_). - 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « *"year"*, *"month"*, *"week"*, *"day"* », *undefined*). + 1. If Type(_roundTo_) is String, then + 1. Let _paramString_ be _roundTo_. + 1. Set _roundTo_ to ! OrdinaryObjectCreate(*null*). + 1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"_smallestUnit_"*, _paramString_). + 1. Else, + 1. Set _roundTo_ to ? GetOptionsObject(_roundTo_). + 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « *"year"*, *"month"*, *"week"*, *"day"* », *undefined*). 1. If _smallestUnit_ is *undefined*, throw a *RangeError* exception. - 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"halfExpand"*). + 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*). 1. If _smallestUnit_ is *"hour"*, then 1. Let _maximum_ be 24. 1. Else if _smallestUnit_ is *"minute"* or *"second"*, then 1. Let _maximum_ be 60. 1. Else, 1. Let _maximum_ be 1000. - 1. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). + 1. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_roundTo_, _maximum_, *false*). 1. Let _result_ be ! RoundTime(_temporalTime_.[[ISOHour]], _temporalTime_.[[ISOMinute]], _temporalTime_.[[ISOSecond]], _temporalTime_.[[ISOMillisecond]], _temporalTime_.[[ISOMicrosecond]], _temporalTime_.[[ISONanosecond]], _roundingIncrement_, _smallestUnit_, _roundingMode_). 1. Return ? CreateTemporalTime(_result_.[[Hour]], _result_.[[Minute]], _result_.[[Second]], _result_.[[Millisecond]], _result_.[[Microsecond]], _result_.[[Nanosecond]]). diff --git a/spec/zoneddatetime.html b/spec/zoneddatetime.html index 47d8ef08aa..407aca461c 100644 --- a/spec/zoneddatetime.html +++ b/spec/zoneddatetime.html @@ -764,21 +764,26 @@

Temporal.ZonedDateTime.prototype.since ( _other_ [ , _options_ ] )

-

Temporal.ZonedDateTime.prototype.round ( _options_ )

+

Temporal.ZonedDateTime.prototype.round ( _roundTo_ )

- The `round` method takes one argument _options_. + The `round` method takes one argument _roundTo_. The following steps are taken:

1. Let _zonedDateTime_ be the *this* value. 1. Perform ? RequireInternalSlot(_zonedDateTime_, [[InitializedTemporalZonedDateTime]]). - 1. If _options_ is *undefined*, then + 1. If _roundTo_ is *undefined*, then 1. Throw a *TypeError* exception. - 1. Set _options_ to ? GetOptionsObject(_options_). - 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_options_, « *"year"*, *"month"*, *"week"* », *undefined*). + 1. If Type(_roundTo_) is String, then + 1. Let _paramString_ be _roundTo_. + 1. Set _roundTo_ to ! OrdinaryObjectCreate(*null*). + 1. Perform ! CreateDataPropertyOrThrow(_roundTo_, *"_smallestUnit_"*, _paramString_). + 1. Else, + 1. Set _roundTo_ to ? GetOptionsObject(_roundTo_). + 1. Let _smallestUnit_ be ? ToSmallestTemporalUnit(_roundTo_, « *"year"*, *"month"*, *"week"* », *undefined*). 1. If _smallestUnit_ is *undefined*, throw a *RangeError* exception. - 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_options_, *"halfExpand"*). - 1. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_options_, _smallestUnit_). + 1. Let _roundingMode_ be ? ToTemporalRoundingMode(_roundTo_, *"halfExpand"*). + 1. Let _roundingIncrement_ be ? ToTemporalDateTimeRoundingIncrement(_roundTo_, _smallestUnit_). 1. Let _timeZone_ be _zonedDateTime_.[[TimeZone]]. 1. Let _instant_ be ! CreateTemporalInstant(_zonedDateTime_.[[Nanoseconds]]). 1. Let _calendar_ be _zonedDateTime_.[[Calendar]].