diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs
index e7f04c0bc6..6dad2b478e 100644
--- a/polyfill/lib/ecmascript.mjs
+++ b/polyfill/lib/ecmascript.mjs
@@ -22,6 +22,7 @@ const StringFromCharCode = String.fromCharCode;
const StringPrototypeCharCodeAt = String.prototype.charCodeAt;
const StringPrototypeMatchAll = String.prototype.matchAll;
const StringPrototypeReplace = String.prototype.replace;
+const StringPrototypeSlice = String.prototype.slice;
import bigInt from 'big-integer';
import callBound from 'call-bind/callBound';
@@ -2344,56 +2345,52 @@ export function ISOYearString(year) {
if (year < 0 || year > 9999) {
let sign = year < 0 ? '-' : '+';
let yearNumber = MathAbs(year);
- yearString = sign + `000000${yearNumber}`.slice(-6);
+ yearString = sign + ToZeroPaddedDecimalString(yearNumber, 6);
} else {
- yearString = `0000${year}`.slice(-4);
+ yearString = ToZeroPaddedDecimalString(year, 4);
}
return yearString;
}
export function ISODateTimePartString(part) {
- return `00${part}`.slice(-2);
+ return ToZeroPaddedDecimalString(part, 2);
}
-export function FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision) {
- if (precision === 'minute') return '';
-
- const secs = `:${ISODateTimePartString(second)}`;
- let fraction = millisecond * 1e6 + microsecond * 1e3 + nanosecond;
-
+export function FormatFractionalSeconds(subSecondNanoseconds, precision) {
+ let fraction;
if (precision === 'auto') {
- if (fraction === 0) return secs;
- fraction = `${fraction}`.padStart(9, '0');
- while (fraction[fraction.length - 1] === '0') fraction = fraction.slice(0, -1);
+ if (subSecondNanoseconds === 0) return '';
+ const fractionFullPrecision = ToZeroPaddedDecimalString(subSecondNanoseconds, 9);
+ // now remove any trailing zeroes
+ fraction = Call(StringPrototypeReplace, fractionFullPrecision, [/0+$/, '']);
} else {
- if (precision === 0) return secs;
- fraction = `${fraction}`.padStart(9, '0').slice(0, precision);
+ if (precision === 0) return '';
+ const fractionFullPrecision = ToZeroPaddedDecimalString(subSecondNanoseconds, 9);
+ fraction = Call(StringPrototypeSlice, fractionFullPrecision, [0, precision]);
}
- return `${secs}.${fraction}`;
+ return `.${fraction}`;
+}
+
+export function FormatTimeString(hour, minute, second, subSecondNanoseconds, precision) {
+ let result = `${ISODateTimePartString(hour)}:${ISODateTimePartString(minute)}`;
+ if (precision === 'minute') return result;
+
+ result += `:${ISODateTimePartString(second)}`;
+ result += FormatFractionalSeconds(subSecondNanoseconds, precision);
+ return result;
}
export function TemporalInstantToString(instant, timeZone, precision) {
let outputTimeZone = timeZone;
if (outputTimeZone === undefined) outputTimeZone = 'UTC';
const dateTime = GetPlainDateTimeFor(outputTimeZone, instant, 'iso8601');
- const year = ISOYearString(GetSlot(dateTime, ISO_YEAR));
- const month = ISODateTimePartString(GetSlot(dateTime, ISO_MONTH));
- const day = ISODateTimePartString(GetSlot(dateTime, ISO_DAY));
- const hour = ISODateTimePartString(GetSlot(dateTime, ISO_HOUR));
- const minute = ISODateTimePartString(GetSlot(dateTime, ISO_MINUTE));
- const seconds = FormatSecondsStringPart(
- GetSlot(dateTime, ISO_SECOND),
- GetSlot(dateTime, ISO_MILLISECOND),
- GetSlot(dateTime, ISO_MICROSECOND),
- GetSlot(dateTime, ISO_NANOSECOND),
- precision
- );
+ const dateTimeString = TemporalDateTimeToString(dateTime, precision, 'never');
let timeZoneString = 'Z';
if (timeZone !== undefined) {
const offsetNs = GetOffsetNanosecondsFor(outputTimeZone, instant);
timeZoneString = FormatDateTimeUTCOffsetRounded(offsetNs);
}
- return `${year}-${month}-${day}T${hour}:${minute}${seconds}${timeZoneString}`;
+ return `${dateTimeString}${timeZoneString}`;
}
function formatAsDecimalNumber(num) {
@@ -2439,20 +2436,11 @@ export function TemporalDurationToString(
(years === 0 && months === 0 && weeks === 0 && days === 0 && hours === 0 && minutes === 0) ||
precision !== 'auto'
) {
- const fraction = MathAbs(ms.toJSNumber()) * 1e6 + MathAbs(µs.toJSNumber()) * 1e3 + MathAbs(ns.toJSNumber());
- let decimalPart = ToZeroPaddedDecimalString(fraction, 9);
- if (precision === 'auto') {
- while (decimalPart[decimalPart.length - 1] === '0') {
- decimalPart = decimalPart.slice(0, -1);
- }
- } else if (precision === 0) {
- decimalPart = '';
- } else {
- decimalPart = decimalPart.slice(0, precision);
- }
- let secondsPart = seconds.abs().toString();
- if (decimalPart) secondsPart += `.${decimalPart}`;
- timePart += `${secondsPart}S`;
+ const secondsPart = formatAsDecimalNumber(seconds.abs());
+ const subSecondNanoseconds =
+ MathAbs(ms.toJSNumber()) * 1e6 + MathAbs(µs.toJSNumber()) * 1e3 + MathAbs(ns.toJSNumber());
+ const subSecondsPart = FormatFractionalSeconds(subSecondNanoseconds, precision);
+ timePart += `${secondsPart}${subSecondsPart}S`;
}
let result = `${sign < 0 ? '-' : ''}P${datePart}`;
if (timePart) result = `${result}T${timePart}`;
@@ -2496,14 +2484,13 @@ export function TemporalDateTimeToString(dateTime, precision, showCalendar = 'au
));
}
- year = ISOYearString(year);
- month = ISODateTimePartString(month);
- day = ISODateTimePartString(day);
- hour = ISODateTimePartString(hour);
- minute = ISODateTimePartString(minute);
- const seconds = FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision);
+ const yearString = ISOYearString(year);
+ const monthString = ISODateTimePartString(month);
+ const dayString = ISODateTimePartString(day);
+ const subSecondNanoseconds = millisecond * 1e6 + microsecond * 1e3 + nanosecond;
+ const timeString = FormatTimeString(hour, minute, second, subSecondNanoseconds, precision);
const calendar = MaybeFormatCalendarAnnotation(GetSlot(dateTime, CALENDAR), showCalendar);
- return `${year}-${month}-${day}T${hour}:${minute}${seconds}${calendar}`;
+ return `${yearString}-${monthString}-${dayString}T${timeString}${calendar}`;
}
export function TemporalMonthDayToString(monthDay, showCalendar = 'auto') {
@@ -2555,31 +2542,18 @@ export function TemporalZonedDateTimeToString(
const tz = GetSlot(zdt, TIME_ZONE);
const dateTime = GetPlainDateTimeFor(tz, instant, 'iso8601');
-
- const year = ISOYearString(GetSlot(dateTime, ISO_YEAR));
- const month = ISODateTimePartString(GetSlot(dateTime, ISO_MONTH));
- const day = ISODateTimePartString(GetSlot(dateTime, ISO_DAY));
- const hour = ISODateTimePartString(GetSlot(dateTime, ISO_HOUR));
- const minute = ISODateTimePartString(GetSlot(dateTime, ISO_MINUTE));
- const seconds = FormatSecondsStringPart(
- GetSlot(dateTime, ISO_SECOND),
- GetSlot(dateTime, ISO_MILLISECOND),
- GetSlot(dateTime, ISO_MICROSECOND),
- GetSlot(dateTime, ISO_NANOSECOND),
- precision
- );
- let result = `${year}-${month}-${day}T${hour}:${minute}${seconds}`;
+ let dateTimeString = TemporalDateTimeToString(dateTime, precision, 'never');
if (showOffset !== 'never') {
const offsetNs = GetOffsetNanosecondsFor(tz, instant);
- result += FormatDateTimeUTCOffsetRounded(offsetNs);
+ dateTimeString += FormatDateTimeUTCOffsetRounded(offsetNs);
}
if (showTimeZone !== 'never') {
const identifier = ToTemporalTimeZoneIdentifier(tz);
const flag = showTimeZone === 'critical' ? '!' : '';
- result += `[${flag}${identifier}]`;
+ dateTimeString += `[${flag}${identifier}]`;
}
- result += MaybeFormatCalendarAnnotation(GetSlot(zdt, CALENDAR), showCalendar);
- return result;
+ dateTimeString += MaybeFormatCalendarAnnotation(GetSlot(zdt, CALENDAR), showCalendar);
+ return dateTimeString;
}
export function IsOffsetTimeZoneIdentifier(string) {
diff --git a/polyfill/lib/plaintime.mjs b/polyfill/lib/plaintime.mjs
index 341874c9f5..df83d0c8e7 100644
--- a/polyfill/lib/plaintime.mjs
+++ b/polyfill/lib/plaintime.mjs
@@ -47,10 +47,8 @@ function TemporalTimeToString(time, precision, options = undefined) {
));
}
- hour = ES.ISODateTimePartString(hour);
- minute = ES.ISODateTimePartString(minute);
- const seconds = ES.FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision);
- return `${hour}:${minute}${seconds}`;
+ const subSecondNanoseconds = millisecond * 1e6 + microsecond * 1e3 + nanosecond;
+ return ES.FormatTimeString(hour, minute, second, subSecondNanoseconds, precision);
}
export class PlainTime {
diff --git a/spec/abstractops.html b/spec/abstractops.html
index cf6a027922..421b70eb72 100644
--- a/spec/abstractops.html
+++ b/spec/abstractops.html
@@ -680,22 +680,60 @@
-
- FormatSecondsStringPart ( _second_, _millisecond_, _microsecond_, _nanosecond_, _precision_ )
+
+
+ FormatFractionalSeconds (
+ _subSecondNanoseconds_: an integer,
+ _precision_: either an integer in the inclusive range 0 to 9 or *"auto"*
+ ): a String
+
+
- 1. Assert: _second_, _millisecond_, _microsecond_, and _nanosecond_ are integers.
- 1. If _precision_ is *"minute"*, return *""*.
- 1. Let _secondsString_ be the string-concatenation of the code unit 0x003A (COLON) and ToZeroPaddedDecimalString(_second_, 2).
- 1. Let _fraction_ be _millisecond_ × 106 + _microsecond_ × 103 + _nanosecond_.
1. If _precision_ is *"auto"*, then
- 1. If _fraction_ is 0, return _secondsString_.
- 1. Set _fraction_ to ToZeroPaddedDecimalString(_fraction_, 9).
- 1. Set _fraction_ to the longest possible substring of _fraction_ starting at position 0 and not ending with the code unit 0x0030 (DIGIT ZERO).
+ 1. If _subSecondNanoseconds_ is 0, return the empty String.
+ 1. Let _fractionString_ be ToZeroPaddedDecimalString(_subSecondNanoseconds_, 9).
+ 1. Set _fractionString_ to the longest prefix of _fractionString_ ending with a code unit other than 0x0030 (DIGIT ZERO).
1. Else,
- 1. If _precision_ is 0, return _secondsString_.
- 1. Set _fraction_ to ToZeroPaddedDecimalString(_fraction_, 9).
- 1. Set _fraction_ to the substring of _fraction_ from 0 to _precision_.
- 1. Return the string-concatenation of _secondsString_, the code unit 0x002E (FULL STOP), and _fraction_.
+ 1. If _precision_ is 0, return the empty String.
+ 1. Let _fractionString_ be ToZeroPaddedDecimalString(_subSecondNanoseconds_, 9).
+ 1. Set _fractionString_ to the substring of _fractionString_ from 0 to _precision_.
+ 1. Return the string-concatenation of the code unit 0x002E (FULL STOP) and _fractionString_.
+
+
+
+
+
+ FormatTimeString (
+ _hour_: an integer,
+ _minute_: an integer,
+ _second_: an integer,
+ _subSecondNanoseconds_: an integer,
+ _precision_: an integer in the inclusive range 0 to 9, *"minute"*, or *"auto"*
+ ): a String
+
+
+
+ 1. Let _hh_ be ToZeroPaddedDecimalString(_hour_, 2).
+ 1. Let _mm_ be ToZeroPaddedDecimalString(_minute_, 2).
+ 1. Let _result_ be the string-concatenation of _hh_, the code unit 0x003A (COLON), and _mm_.
+ 1. If _precision_ is *"minute"*, return _result_.
+ 1. Let _ss_ be ToZeroPaddedDecimalString(_second_, 2).
+ 1. Let _subSecondsPart_ be FormatFractionalSeconds(_subSecondNanoseconds_, _precision_).
+ 1. Set _result_ to the string-concatenation of _result_, the code unit 0x003A (COLON), _ss_, and _subSecondsPart_.
+ 1. Return _result_.
diff --git a/spec/duration.html b/spec/duration.html
index d5983b62a1..b8c1c2e43b 100644
--- a/spec/duration.html
+++ b/spec/duration.html
@@ -1936,18 +1936,10 @@
1. Let _zeroMinutesAndHigher_ be *false*.
1. If _years_ = 0, and _months_ = 0, and _weeks_ = 0, and _days_ = 0, and _hours_ = 0, and _minutes_ = 0, set _zeroMinutesAndHigher_ to *true*.
1. If _nonzeroSecondsAndLower_ is *true*, or _zeroMinutesAndHigher_ is *true*, or _precision_ is not *"auto"*, then
- 1. Let _fraction_ be abs(_milliseconds_) × 106 + abs(_microseconds_) × 103 + abs(_nanoseconds_).
- 1. Let _decimalPart_ be ToZeroPaddedDecimalString(_fraction_, 9).
- 1. If _precision_ is *"auto"*, then
- 1. Set _decimalPart_ to the longest possible substring of _decimalPart_ starting at position 0 and not ending with the code unit 0x0030 (DIGIT ZERO).
- 1. Else if _precision_ = 0, then
- 1. Set _decimalPart_ to *""*.
- 1. Else,
- 1. Set _decimalPart_ to the substring of _decimalPart_ from 0 to _precision_.
1. Let _secondsPart_ be abs(_seconds_) formatted as a decimal number.
- 1. If _decimalPart_ is not *""*, then
- 1. Set _secondsPart_ to the string-concatenation of _secondsPart_, the code unit 0x002E (FULL STOP), and _decimalPart_.
- 1. Set _timePart_ to the string concatenation of _timePart_, _secondsPart_, and the code unit 0x0053 (LATIN CAPITAL LETTER S).
+ 1. Let _subSecondNanoseconds_ be abs(_milliseconds_) × 106 + abs(_microseconds_) × 103 + abs(_nanoseconds_).
+ 1. Let _subSecondsPart_ be FormatFractionalSeconds(_subSecondNanoseconds_, _precision_).
+ 1. Set _timePart_ to the string concatenation of _timePart_, _secondsPart_, _subSecondsPart_, and the code unit 0x0053 (LATIN CAPITAL LETTER S).
1. Let _signPart_ be the code unit 0x002D (HYPHEN-MINUS) if _sign_ < 0, and otherwise the empty String.
1. Let _result_ be the string concatenation of _signPart_, the code unit 0x0050 (LATIN CAPITAL LETTER P) and _datePart_.
1. If _timePart_ is not *""*, then
diff --git a/spec/mainadditions.html b/spec/mainadditions.html
index 7c9b25a27c..a8f84b5b3c 100644
--- a/spec/mainadditions.html
+++ b/spec/mainadditions.html
@@ -415,6 +415,25 @@
[...]
+
+
+ TimeString (
+ _tv_: a Number, but not *NaN*,
+ ): a String
+
+
+
+ 1. Let _hour_ be ToZeroPaddedDecimalString(ℝ(HourFromTime(_tv_)), 2).
+ 1. Let _minute_ be ToZeroPaddedDecimalString(ℝ(MinFromTime(_tv_)), 2).
+ 1. Let _second_ be ToZeroPaddedDecimalString(ℝ(SecFromTime(_tv_)), 2).
+ 1. Let _timeString_ be FormatTimeString(ℝ(HourFromTime(_tv_)), ℝ(MinFromTime(_tv_)), ℝ(SecFromTime(_tv_)), 0, 0).
+ 1. Return the string-concatenation of _timeString_, the code unit 0x0020 (SPACE), and *"GMT"*.
+
+
+
+ [...]
+
TimeZoneString (
diff --git a/spec/plaindate.html b/spec/plaindate.html
index 00ff13f815..d797d7661e 100644
--- a/spec/plaindate.html
+++ b/spec/plaindate.html
@@ -928,7 +928,7 @@ TemporalDateToString ( _temporalDate_, _showCalendar_ )
1. Assert: Type(_temporalDate_) is Object.
1. Assert: _temporalDate_ has an [[InitializedTemporalDate]] internal slot.
- 1. Let _year_ be ! PadISOYear(_temporalDate_.[[ISOYear]]).
+ 1. Let _year_ be PadISOYear(_temporalDate_.[[ISOYear]]).
1. Let _month_ be ToZeroPaddedDecimalString(_temporalDate_.[[ISOMonth]], 2).
1. Let _day_ be ToZeroPaddedDecimalString(_temporalDate_.[[ISODay]], 2).
1. Let _calendar_ be ? MaybeFormatCalendarAnnotation(_temporalDate_.[[Calendar]], _showCalendar_).
diff --git a/spec/plaindatetime.html b/spec/plaindatetime.html
index 18d465f28e..74cf650dda 100644
--- a/spec/plaindatetime.html
+++ b/spec/plaindatetime.html
@@ -993,14 +993,13 @@
TemporalDateTimeToString ( _isoYear_, _isoMonth_, _isoDay_, _hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _calendar_, _precision_, _showCalendar_ )
1. Assert: _isoYear_, _isoMonth_, _isoDay_, _hour_, _minute_, _second_, _millisecond_, _microsecond_, and _nanosecond_ are integers.
- 1. Let _year_ be ! PadISOYear(_isoYear_).
- 1. Let _month_ be ToZeroPaddedDecimalString(_isoMonth_, 2).
- 1. Let _day_ be ToZeroPaddedDecimalString(_isoDay_, 2).
- 1. Let _hour_ be ToZeroPaddedDecimalString(_hour_, 2).
- 1. Let _minute_ be ToZeroPaddedDecimalString(_minute_, 2).
- 1. Let _seconds_ be ! FormatSecondsStringPart(_second_, _millisecond_, _microsecond_, _nanosecond_, _precision_).
+ 1. Let _yearString_ be PadISOYear(_isoYear_).
+ 1. Let _monthString_ be ToZeroPaddedDecimalString(_isoMonth_, 2).
+ 1. Let _dayString_ be ToZeroPaddedDecimalString(_isoDay_, 2).
+ 1. Let _subSecondNanoseconds_ be _millisecond_ × 106 + _microsecond_ × 103 + _nanosecond_.
+ 1. Let _timeString_ be FormatTimeString(_hour_, _minute_, _second_, _subSecondNanoseconds_, _precision_).
1. Let _calendarString_ be ? MaybeFormatCalendarAnnotation(_calendar_, _showCalendar_).
- 1. Return the string-concatenation of _year_, the code unit 0x002D (HYPHEN-MINUS), _month_, the code unit 0x002D (HYPHEN-MINUS), _day_, 0x0054 (LATIN CAPITAL LETTER T), _hour_, the code unit 0x003A (COLON), _minute_, _seconds_, and _calendarString_.
+ 1. Return the string-concatenation of _yearString_, the code unit 0x002D (HYPHEN-MINUS), _monthString_, the code unit 0x002D (HYPHEN-MINUS), _dayString_, 0x0054 (LATIN CAPITAL LETTER T), _timeString_, and _calendarString_.
diff --git a/spec/plainmonthday.html b/spec/plainmonthday.html
index 7aaad254f8..1b5fb1d935 100644
--- a/spec/plainmonthday.html
+++ b/spec/plainmonthday.html
@@ -437,7 +437,7 @@ TemporalMonthDayToString ( _monthDay_, _showCalendar_ )
1. Let _result_ be the string-concatenation of _month_, the code unit 0x002D (HYPHEN-MINUS), and _day_.
1. Let _calendarIdentifier_ be ? ToTemporalCalendarIdentifier(_monthDay_.[[Calendar]]).
1. If _showCalendar_ is one of *"always"* or *"critical"*, or if _calendarIdentifier_ is not *"iso8601"*, then
- 1. Let _year_ be ! PadISOYear(_monthDay_.[[ISOYear]]).
+ 1. Let _year_ be PadISOYear(_monthDay_.[[ISOYear]]).
1. Set _result_ to the string-concatenation of _year_, the code unit 0x002D (HYPHEN-MINUS), and _result_.
1. Let _calendarString_ be FormatCalendarAnnotation(_calendarIdentifier_, _showCalendar_).
1. Set _result_ to the string-concatenation of _result_ and _calendarString_.
diff --git a/spec/plaintime.html b/spec/plaintime.html
index f1ce8c20f4..d01a0bf57b 100644
--- a/spec/plaintime.html
+++ b/spec/plaintime.html
@@ -813,10 +813,8 @@
TemporalTimeToString ( _hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _precision_ )
1. Assert: _hour_, _minute_, _second_, _millisecond_, _microsecond_ and _nanosecond_ are integers.
- 1. Let _hour_ be ToZeroPaddedDecimalString(_hour_, 2).
- 1. Let _minute_ be ToZeroPaddedDecimalString(_minute_, 2).
- 1. Let _seconds_ be ! FormatSecondsStringPart(_second_, _millisecond_, _microsecond_, _nanosecond_, _precision_).
- 1. Return the string-concatenation of _hour_, the code unit 0x003A (COLON), _minute_, and _seconds_.
+ 1. Let _subSecondNanoseconds_ be _millisecond_ × 106 + _microsecond_ × 103 + _nanosecond_.
+ 1. Return FormatTimeString(_hour_, _minute_, _second_, _subSecondNanoseconds_, _precision_).
diff --git a/spec/plainyearmonth.html b/spec/plainyearmonth.html
index 31d6160b8e..192bb056f0 100644
--- a/spec/plainyearmonth.html
+++ b/spec/plainyearmonth.html
@@ -598,7 +598,7 @@ TemporalYearMonthToString ( _yearMonth_, _showCalendar_ )
1. Assert: Type(_yearMonth_) is Object.
1. Assert: _yearMonth_ has an [[InitializedTemporalYearMonth]] internal slot.
- 1. Let _year_ be ! PadISOYear(_yearMonth_.[[ISOYear]]).
+ 1. Let _year_ be PadISOYear(_yearMonth_.[[ISOYear]]).
1. Let _month_ be ToZeroPaddedDecimalString(_yearMonth_.[[ISOMonth]], 2).
1. Let _result_ be the string-concatenation of _year_, the code unit 0x002D (HYPHEN-MINUS), and _month_.
1. Let _calendarIdentifier_ be ? ToTemporalCalendarIdentifier(_yearMonth_.[[Calendar]]).