Skip to content

Commit 9f7e46d

Browse files
committed
Editorial: DRY refactor of time formatting
This editorial commit removes redundant spec text dealing with time formatting: * Adds a new AO FormatTimeString, and calls it in TemporalTimeToString, TemporalDateTimeToString, and TimeString (the legacy date AO in 262). * Removes FormatSecondsStringPart and replaces it with a new AO FormatFractionalSeconds, and call it in FormatTimeString and TemporalDurationToString. The text of this new AO is aligned with similar text in GetOffsetStringFor in tc39#2607. * Replaces sub-second formatting text in TemporalDurationToString with a call to FormatFractionalSeconds. * Aligns polyfill code to these spec changes. * Adjusts polyfill code in a few places to better match the spec. Note that this commit doesn't touch spec text for formatting time zone offsets because there are several in-flight PRs dealing with offsets and I wanted to keep this PR merge-conflict-free. But once those PRs land and the dust settles, then I'll send another editorial PR to DRY offset string formatting too, by using FormatTimeString to replace bespoke formatting text for offsets.
1 parent 736d6db commit 9f7e46d

File tree

7 files changed

+124
-106
lines changed

7 files changed

+124
-106
lines changed

polyfill/lib/ecmascript.mjs

Lines changed: 41 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const StringFromCharCode = String.fromCharCode;
2222
const StringPrototypeCharCodeAt = String.prototype.charCodeAt;
2323
const StringPrototypeMatchAll = String.prototype.matchAll;
2424
const StringPrototypeReplace = String.prototype.replace;
25+
const StringPrototypeSlice = String.prototype.slice;
2526

2627
import bigInt from 'big-integer';
2728
import callBound from 'call-bind/callBound';
@@ -2344,56 +2345,52 @@ export function ISOYearString(year) {
23442345
if (year < 0 || year > 9999) {
23452346
let sign = year < 0 ? '-' : '+';
23462347
let yearNumber = MathAbs(year);
2347-
yearString = sign + `000000${yearNumber}`.slice(-6);
2348+
yearString = sign + ToZeroPaddedDecimalString(yearNumber, 6);
23482349
} else {
2349-
yearString = `0000${year}`.slice(-4);
2350+
yearString = ToZeroPaddedDecimalString(year, 4);
23502351
}
23512352
return yearString;
23522353
}
23532354

23542355
export function ISODateTimePartString(part) {
2355-
return `00${part}`.slice(-2);
2356+
return ToZeroPaddedDecimalString(part, 2);
23562357
}
23572358

2358-
export function FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision) {
2359-
if (precision === 'minute') return '';
2360-
2361-
const secs = `:${ISODateTimePartString(second)}`;
2362-
let fraction = millisecond * 1e6 + microsecond * 1e3 + nanosecond;
2363-
2359+
export function FormatFractionalSeconds(subSecondNanoseconds, precision) {
2360+
let fraction;
23642361
if (precision === 'auto') {
2365-
if (fraction === 0) return secs;
2366-
fraction = `${fraction}`.padStart(9, '0');
2367-
while (fraction[fraction.length - 1] === '0') fraction = fraction.slice(0, -1);
2362+
if (subSecondNanoseconds === 0) return '';
2363+
const fractionFullPrecision = ToZeroPaddedDecimalString(subSecondNanoseconds, 9);
2364+
// now remove any trailing zeroes
2365+
fraction = Call(StringPrototypeReplace, fractionFullPrecision, [/0+$/, '']);
23682366
} else {
2369-
if (precision === 0) return secs;
2370-
fraction = `${fraction}`.padStart(9, '0').slice(0, precision);
2367+
if (precision === 0) return '';
2368+
const fractionFullPrecision = ToZeroPaddedDecimalString(subSecondNanoseconds, 9);
2369+
fraction = Call(StringPrototypeSlice, fractionFullPrecision, [0, precision]);
23712370
}
2372-
return `${secs}.${fraction}`;
2371+
return `.${fraction}`;
2372+
}
2373+
2374+
export function FormatTimeString(hour, minute, second, subSecondNanoseconds, precision) {
2375+
let result = `${ISODateTimePartString(hour)}:${ISODateTimePartString(minute)}`;
2376+
if (precision === 'minute') return result;
2377+
2378+
result += `:${ISODateTimePartString(second)}`;
2379+
result += FormatFractionalSeconds(subSecondNanoseconds, precision);
2380+
return result;
23732381
}
23742382

23752383
export function TemporalInstantToString(instant, timeZone, precision) {
23762384
let outputTimeZone = timeZone;
23772385
if (outputTimeZone === undefined) outputTimeZone = 'UTC';
23782386
const dateTime = GetPlainDateTimeFor(outputTimeZone, instant, 'iso8601');
2379-
const year = ISOYearString(GetSlot(dateTime, ISO_YEAR));
2380-
const month = ISODateTimePartString(GetSlot(dateTime, ISO_MONTH));
2381-
const day = ISODateTimePartString(GetSlot(dateTime, ISO_DAY));
2382-
const hour = ISODateTimePartString(GetSlot(dateTime, ISO_HOUR));
2383-
const minute = ISODateTimePartString(GetSlot(dateTime, ISO_MINUTE));
2384-
const seconds = FormatSecondsStringPart(
2385-
GetSlot(dateTime, ISO_SECOND),
2386-
GetSlot(dateTime, ISO_MILLISECOND),
2387-
GetSlot(dateTime, ISO_MICROSECOND),
2388-
GetSlot(dateTime, ISO_NANOSECOND),
2389-
precision
2390-
);
2387+
const dateTimeString = TemporalDateTimeToString(dateTime, precision, 'never');
23912388
let timeZoneString = 'Z';
23922389
if (timeZone !== undefined) {
23932390
const offsetNs = GetOffsetNanosecondsFor(outputTimeZone, instant);
23942391
timeZoneString = FormatDateTimeUTCOffsetRounded(offsetNs);
23952392
}
2396-
return `${year}-${month}-${day}T${hour}:${minute}${seconds}${timeZoneString}`;
2393+
return `${dateTimeString}${timeZoneString}`;
23972394
}
23982395

23992396
function formatAsDecimalNumber(num) {
@@ -2439,20 +2436,11 @@ export function TemporalDurationToString(
24392436
(years === 0 && months === 0 && weeks === 0 && days === 0 && hours === 0 && minutes === 0) ||
24402437
precision !== 'auto'
24412438
) {
2442-
const fraction = MathAbs(ms.toJSNumber()) * 1e6 + MathAbs(µs.toJSNumber()) * 1e3 + MathAbs(ns.toJSNumber());
2443-
let decimalPart = ToZeroPaddedDecimalString(fraction, 9);
2444-
if (precision === 'auto') {
2445-
while (decimalPart[decimalPart.length - 1] === '0') {
2446-
decimalPart = decimalPart.slice(0, -1);
2447-
}
2448-
} else if (precision === 0) {
2449-
decimalPart = '';
2450-
} else {
2451-
decimalPart = decimalPart.slice(0, precision);
2452-
}
2453-
let secondsPart = seconds.abs().toString();
2454-
if (decimalPart) secondsPart += `.${decimalPart}`;
2455-
timePart += `${secondsPart}S`;
2439+
const secondsPart = formatAsDecimalNumber(seconds.abs());
2440+
const subSecondNanoseconds =
2441+
MathAbs(ms.toJSNumber()) * 1e6 + MathAbs(µs.toJSNumber()) * 1e3 + MathAbs(ns.toJSNumber());
2442+
const subSecondsPart = FormatFractionalSeconds(subSecondNanoseconds, precision);
2443+
timePart += `${secondsPart}${subSecondsPart}S`;
24562444
}
24572445
let result = `${sign < 0 ? '-' : ''}P${datePart}`;
24582446
if (timePart) result = `${result}T${timePart}`;
@@ -2496,14 +2484,13 @@ export function TemporalDateTimeToString(dateTime, precision, showCalendar = 'au
24962484
));
24972485
}
24982486

2499-
year = ISOYearString(year);
2500-
month = ISODateTimePartString(month);
2501-
day = ISODateTimePartString(day);
2502-
hour = ISODateTimePartString(hour);
2503-
minute = ISODateTimePartString(minute);
2504-
const seconds = FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision);
2487+
const yearString = ISOYearString(year);
2488+
const monthString = ISODateTimePartString(month);
2489+
const dayString = ISODateTimePartString(day);
2490+
const subSecondNanoseconds = millisecond * 1e6 + microsecond * 1e3 + nanosecond;
2491+
const timeString = FormatTimeString(hour, minute, second, subSecondNanoseconds, precision);
25052492
const calendar = MaybeFormatCalendarAnnotation(GetSlot(dateTime, CALENDAR), showCalendar);
2506-
return `${year}-${month}-${day}T${hour}:${minute}${seconds}${calendar}`;
2493+
return `${yearString}-${monthString}-${dayString}T${timeString}${calendar}`;
25072494
}
25082495

25092496
export function TemporalMonthDayToString(monthDay, showCalendar = 'auto') {
@@ -2555,31 +2542,18 @@ export function TemporalZonedDateTimeToString(
25552542

25562543
const tz = GetSlot(zdt, TIME_ZONE);
25572544
const dateTime = GetPlainDateTimeFor(tz, instant, 'iso8601');
2558-
2559-
const year = ISOYearString(GetSlot(dateTime, ISO_YEAR));
2560-
const month = ISODateTimePartString(GetSlot(dateTime, ISO_MONTH));
2561-
const day = ISODateTimePartString(GetSlot(dateTime, ISO_DAY));
2562-
const hour = ISODateTimePartString(GetSlot(dateTime, ISO_HOUR));
2563-
const minute = ISODateTimePartString(GetSlot(dateTime, ISO_MINUTE));
2564-
const seconds = FormatSecondsStringPart(
2565-
GetSlot(dateTime, ISO_SECOND),
2566-
GetSlot(dateTime, ISO_MILLISECOND),
2567-
GetSlot(dateTime, ISO_MICROSECOND),
2568-
GetSlot(dateTime, ISO_NANOSECOND),
2569-
precision
2570-
);
2571-
let result = `${year}-${month}-${day}T${hour}:${minute}${seconds}`;
2545+
let dateTimeString = TemporalDateTimeToString(dateTime, precision, 'never');
25722546
if (showOffset !== 'never') {
25732547
const offsetNs = GetOffsetNanosecondsFor(tz, instant);
2574-
result += FormatDateTimeUTCOffsetRounded(offsetNs);
2548+
dateTimeString += FormatDateTimeUTCOffsetRounded(offsetNs);
25752549
}
25762550
if (showTimeZone !== 'never') {
25772551
const identifier = ToTemporalTimeZoneIdentifier(tz);
25782552
const flag = showTimeZone === 'critical' ? '!' : '';
2579-
result += `[${flag}${identifier}]`;
2553+
dateTimeString += `[${flag}${identifier}]`;
25802554
}
2581-
result += MaybeFormatCalendarAnnotation(GetSlot(zdt, CALENDAR), showCalendar);
2582-
return result;
2555+
dateTimeString += MaybeFormatCalendarAnnotation(GetSlot(zdt, CALENDAR), showCalendar);
2556+
return dateTimeString;
25832557
}
25842558

25852559
export function IsOffsetTimeZoneIdentifier(string) {

polyfill/lib/plaintime.mjs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ function TemporalTimeToString(time, precision, options = undefined) {
4747
));
4848
}
4949

50-
hour = ES.ISODateTimePartString(hour);
51-
minute = ES.ISODateTimePartString(minute);
52-
const seconds = ES.FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision);
53-
return `${hour}:${minute}${seconds}`;
50+
const subSecondNanoseconds = millisecond * 1e6 + microsecond * 1e3 + nanosecond;
51+
return ES.FormatTimeString(hour, minute, second, subSecondNanoseconds, precision);
5452
}
5553

5654
export class PlainTime {

spec/abstractops.html

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -680,22 +680,60 @@ <h1>
680680
</emu-alg>
681681
</emu-clause>
682682

683-
<emu-clause id="sec-temporal-formatsecondsstringpart" aoid="FormatSecondsStringPart">
684-
<h1>FormatSecondsStringPart ( _second_, _millisecond_, _microsecond_, _nanosecond_, _precision_ )</h1>
683+
<emu-clause id="sec-temporal-formatfractionalseconds" type="abstract operation">
684+
<h1>
685+
FormatFractionalSeconds (
686+
_subSecondNanoseconds_: an integer,
687+
_precision_: either an integer in the inclusive range 0 to 9 or *"auto"*
688+
): a String
689+
</h1>
690+
<dl class="header">
691+
<dt>description</dt>
692+
<dd>
693+
If _precision_ is zero, or if _precision_ is *"auto"* and _subSecondNanoseconds_ is zero, then an empty String will be returned.
694+
Otherwise, the output will be a decimal point followed by a sequence of fractional seconds digits, either with no trailing zeroes if _precision_ is *"auto"* or truncated to _precision_ digits.
695+
</dd>
696+
</dl>
685697
<emu-alg>
686-
1. Assert: _second_, _millisecond_, _microsecond_, and _nanosecond_ are integers.
687-
1. If _precision_ is *"minute"*, return *""*.
688-
1. Let _secondsString_ be the string-concatenation of the code unit 0x003A (COLON) and ToZeroPaddedDecimalString(_second_, 2).
689-
1. Let _fraction_ be _millisecond_ &times; 10<sup>6</sup> + _microsecond_ &times; 10<sup>3</sup> + _nanosecond_.
690698
1. If _precision_ is *"auto"*, then
691-
1. If _fraction_ is 0, return _secondsString_.
692-
1. Set _fraction_ to ToZeroPaddedDecimalString(_fraction_, 9).
693-
1. Set _fraction_ to the longest possible substring of _fraction_ starting at position 0 and not ending with the code unit 0x0030 (DIGIT ZERO).
699+
1. If _subSecondNanoseconds_ is 0, return the empty String.
700+
1. Let _fractionString_ be ToZeroPaddedDecimalString(_subSecondNanoseconds_, 9).
701+
1. Set _fractionString_ to the longest prefix of _fractionString_ ending with a code unit other than 0x0030 (DIGIT ZERO).
694702
1. Else,
695-
1. If _precision_ is 0, return _secondsString_.
696-
1. Set _fraction_ to ToZeroPaddedDecimalString(_fraction_, 9).
697-
1. Set _fraction_ to the substring of _fraction_ from 0 to _precision_.
698-
1. Return the string-concatenation of _secondsString_, the code unit 0x002E (FULL STOP), and _fraction_.
703+
1. If _precision_ is 0, return the empty String.
704+
1. Let _fractionString_ be ToZeroPaddedDecimalString(_subSecondNanoseconds_, 9).
705+
1. Set _fractionString_ to the substring of _fractionString_ from 0 to _precision_.
706+
1. Return the string-concatenation of the code unit 0x002E (FULL STOP), and _fractionString_.
707+
</emu-alg>
708+
</emu-clause>
709+
710+
<emu-clause id="sec-temporal-formattimestring" type="abstract operation">
711+
<h1>
712+
FormatTimeString (
713+
_hour_: an integer,
714+
_minute_: an integer,
715+
_second_: an integer,
716+
_subSecondNanoseconds_: an integer,
717+
_precision_: an integer in the inclusive range 0 to 9, *"minute"*, or *"auto"*
718+
): a String
719+
</h1>
720+
<dl class="header">
721+
<dt>description</dt>
722+
<dd>
723+
The output will be formatted like ±HH:MM if _precision_ is *"minute"*.
724+
Otherwise, the output will be formatted like ±HH:MM:SS if _precision_ is zero, or if _subSecondNanoseconds_ is zero and _precision is *"auto"*.
725+
Otherwise, the output will be formatted like ±HH:MM:SS.fff where "fff" is a sequence of fractional seconds digits, either with no trailing zeroes if _precision_ is *"auto"* or truncated to _precision_ digits.
726+
</dd>
727+
</dl>
728+
<emu-alg>
729+
1. Let _hh_ be ToZeroPaddedDecimalString(_hour_, 2).
730+
1. Let _mm_ be ToZeroPaddedDecimalString(_minute_, 2).
731+
1. Let _result_ be the string-concatenation of _hh_, the code unit 0x003A (COLON), and _mm_.
732+
1. If _precision_ is *"minute"*, return _result_.
733+
1. Let _ss_ be ToZeroPaddedDecimalString(_second_, 2).
734+
1. Let _subSecondsPart_ be FormatFractionalSeconds(_subSecondNanoseconds_, _precision_).
735+
1. Set _result_ to the string-concatenation of _result_, the code unit 0x003A (COLON), _ss_, and _subSecondsPart_.
736+
1. Return _result_.
699737
</emu-alg>
700738
</emu-clause>
701739

spec/duration.html

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1936,18 +1936,10 @@ <h1>
19361936
1. Let _zeroMinutesAndHigher_ be *false*.
19371937
1. If _years_ = 0, and _months_ = 0, and _weeks_ = 0, and _days_ = 0, and _hours_ = 0, and _minutes_ = 0, set _zeroMinutesAndHigher_ to *true*.
19381938
1. If _nonzeroSecondsAndLower_ is *true*, or _zeroMinutesAndHigher_ is *true*, or _precision_ is not *"auto"*, then
1939-
1. Let _fraction_ be abs(_milliseconds_) &times; 10<sup>6</sup> + abs(_microseconds_) &times; 10<sup>3</sup> + abs(_nanoseconds_).
1940-
1. Let _decimalPart_ be ToZeroPaddedDecimalString(_fraction_, 9).
1941-
1. If _precision_ is *"auto"*, then
1942-
1. Set _decimalPart_ to the longest possible substring of _decimalPart_ starting at position 0 and not ending with the code unit 0x0030 (DIGIT ZERO).
1943-
1. Else if _precision_ = 0, then
1944-
1. Set _decimalPart_ to *""*.
1945-
1. Else,
1946-
1. Set _decimalPart_ to the substring of _decimalPart_ from 0 to _precision_.
19471939
1. Let _secondsPart_ be abs(_seconds_) formatted as a decimal number.
1948-
1. If _decimalPart_ is not *""*, then
1949-
1. Set _secondsPart_ to the string-concatenation of _secondsPart_, the code unit 0x002E (FULL STOP), and _decimalPart_.
1950-
1. Set _timePart_ to the string concatenation of _timePart_, _secondsPart_, and the code unit 0x0053 (LATIN CAPITAL LETTER S).
1940+
1. Let _subSecondNanoseconds_ be abs(_milliseconds_) &times; 10<sup>6</sup> + abs(_microseconds_) &times; 10<sup>3</sup> + abs(_nanoseconds_).
1941+
1. Let _subSecondsPart_ be FormatFractionalSeconds(_subSecondNanoseconds_, _precision).
1942+
1. Set _timePart_ to the string concatenation of _timePart_, _secondsPart_, _subSecondsPart_, and the code unit 0x0053 (LATIN CAPITAL LETTER S).
19511943
1. Let _signPart_ be the code unit 0x002D (HYPHEN-MINUS) if _sign_ &lt; 0, and otherwise the empty String.
19521944
1. Let _result_ be the string concatenation of _signPart_, the code unit 0x0050 (LATIN CAPITAL LETTER P) and _datePart_.
19531945
1. If _timePart_ is not *""*, then

spec/mainadditions.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,25 @@ <h1>
415415

416416
<p>[...]</p>
417417

418+
<emu-clause id="sec-timestring" type="abstract operation">
419+
<h1>
420+
TimeString (
421+
_tv_: a Number, but not *NaN*,
422+
): a String
423+
</h1>
424+
<dl class="header">
425+
</dl>
426+
<emu-alg>
427+
1. <del>Let _hour_ be ToZeroPaddedDecimalString(ℝ(HourFromTime(_tv_)), 2).</del>
428+
1. <del>Let _minute_ be ToZeroPaddedDecimalString(ℝ(MinFromTime(_tv_)), 2).</del>
429+
1. <del>Let _second_ be ToZeroPaddedDecimalString(ℝ(SecFromTime(_tv_)), 2).</del>
430+
1. <ins>Let _timeString_ be FormatTimeString(ℝ(HourFromTime(_tv_)), ℝ(MinFromTime(_tv_)), ℝ(SecFromTime(_tv_)), 0, 0).</ins>
431+
1. Return the string-concatenation of _timeString_, the code unit 0x0020 (SPACE), and *"GMT"*.
432+
</emu-alg>
433+
</emu-clause>
434+
435+
<p>[...]</p>
436+
418437
<emu-clause id="sec-timezoneestring" type="abstract operation">
419438
<h1>
420439
TimeZoneString (

spec/plaindatetime.html

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -993,14 +993,13 @@ <h1>
993993
<h1>TemporalDateTimeToString ( _isoYear_, _isoMonth_, _isoDay_, _hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _calendar_, _precision_, _showCalendar_ )</h1>
994994
<emu-alg>
995995
1. Assert: _isoYear_, _isoMonth_, _isoDay_, _hour_, _minute_, _second_, _millisecond_, _microsecond_, and _nanosecond_ are integers.
996-
1. Let _year_ be ! PadISOYear(_isoYear_).
997-
1. Let _month_ be ToZeroPaddedDecimalString(_isoMonth_, 2).
998-
1. Let _day_ be ToZeroPaddedDecimalString(_isoDay_, 2).
999-
1. Let _hour_ be ToZeroPaddedDecimalString(_hour_, 2).
1000-
1. Let _minute_ be ToZeroPaddedDecimalString(_minute_, 2).
1001-
1. Let _seconds_ be ! FormatSecondsStringPart(_second_, _millisecond_, _microsecond_, _nanosecond_, _precision_).
996+
1. Let _yearString_ be ! PadISOYear(_isoYear_).
997+
1. Let _monthString_ be ToZeroPaddedDecimalString(_isoMonth_, 2).
998+
1. Let _dayString_ be ToZeroPaddedDecimalString(_isoDay_, 2).
999+
1. Let _subSecondNanoseconds_ be _millisecond_ &times; 10<sup>6</sup> + _microsecond_ &times; 10<sup>3</sup> + _nanosecond_.
1000+
1. Let _timeString_ be FormatTimeString(_hour_, _minute_, _second_, _subSecondNanoseconds_, _precision_).
10021001
1. Let _calendarString_ be ? MaybeFormatCalendarAnnotation(_calendar_, _showCalendar_).
1003-
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_.
1002+
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_.
10041003
</emu-alg>
10051004
</emu-clause>
10061005

spec/plaintime.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -813,10 +813,8 @@ <h1>
813813
<h1>TemporalTimeToString ( _hour_, _minute_, _second_, _millisecond_, _microsecond_, _nanosecond_, _precision_ )</h1>
814814
<emu-alg>
815815
1. Assert: _hour_, _minute_, _second_, _millisecond_, _microsecond_ and _nanosecond_ are integers.
816-
1. Let _hour_ be ToZeroPaddedDecimalString(_hour_, 2).
817-
1. Let _minute_ be ToZeroPaddedDecimalString(_minute_, 2).
818-
1. Let _seconds_ be ! FormatSecondsStringPart(_second_, _millisecond_, _microsecond_, _nanosecond_, _precision_).
819-
1. Return the string-concatenation of _hour_, the code unit 0x003A (COLON), _minute_, and _seconds_.
816+
1. Let _subSecondNanoseconds_ be _millisecond_ &times; 10<sup>6</sup> + _microsecond_ &times; 10<sup>3</sup> + _nanosecond_.
817+
1. Return FormatTimeString(_hour_, _minute_, _second_, _subSecondNanoseconds_, _precision_).
820818
</emu-alg>
821819
</emu-clause>
822820

0 commit comments

Comments
 (0)