Skip to content

Commit

Permalink
Polyfill: clean up ISO parser & align to spec
Browse files Browse the repository at this point in the history
To make it easier to find actual Test262 gaps, this PR removes
unreachable and unused code in ISO parsing.

It also removes offset normalization code from ParseISODateTime,
to better mach the spec that only normalizes downstream from ISO
string parsing. Doing this exposed a test bug that's fixed in the second
commit of tc39/test262#3877.
  • Loading branch information
justingrant authored and ptomato committed Aug 9, 2023
1 parent 35ab2ad commit 5f9e901
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 28 deletions.
25 changes: 7 additions & 18 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -437,23 +437,11 @@ export function ParseISODateTime(isoString) {
if (match[13]) {
offset = undefined;
z = true;
} else if (match[14] && match[15]) {
const offsetSign = match[14] === '-' || match[14] === '\u2212' ? '-' : '+';
const offsetHours = match[15] || '00';
const offsetMinutes = match[16] || '00';
const offsetSeconds = match[17] || '00';
let offsetFraction = match[18] || '0';
offset = `${offsetSign}${offsetHours}:${offsetMinutes}`;
if (+offsetFraction) {
while (offsetFraction.endsWith('0')) offsetFraction = offsetFraction.slice(0, -1);
offset += `:${offsetSeconds}.${offsetFraction}`;
} else if (+offsetSeconds) {
offset += `:${offsetSeconds}`;
}
if (offset === '-00:00') offset = '+00:00';
} else if (match[14]) {
offset = match[14];
}
const tzName = match[19];
const calendar = processAnnotations(match[20]);
const tzName = match[15];
const calendar = processAnnotations(match[16]);
RejectDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
return {
year,
Expand Down Expand Up @@ -505,7 +493,7 @@ export function ParseTemporalTimeString(isoString) {
millisecond = ToIntegerOrInfinity(fraction.slice(0, 3));
microsecond = ToIntegerOrInfinity(fraction.slice(3, 6));
nanosecond = ToIntegerOrInfinity(fraction.slice(6, 9));
processAnnotations(match[14]); // ignore found calendar
processAnnotations(match[10]); // ignore found calendar
if (match[8]) throw new RangeError('Z designator not supported for PlainTime');
} else {
let z, hasTime;
Expand Down Expand Up @@ -2624,7 +2612,7 @@ export function IsOffsetTimeZoneIdentifier(string) {
}

export function ParseDateTimeUTCOffset(string) {
const match = OFFSET.exec(string);
const match = OFFSET_WITH_PARTS.exec(string);
if (!match) {
throw new RangeError(`invalid time zone offset: ${string}`);
}
Expand Down Expand Up @@ -5632,6 +5620,7 @@ export function ASCIILowercase(str) {
}

const OFFSET = new RegExp(`^${PARSE.offset.source}$`);
const OFFSET_WITH_PARTS = new RegExp(`^${PARSE.offsetWithParts.source}$`);

function bisect(getState, left, right, lstate = getState(left), rstate = getState(right)) {
left = bigInt(left);
Expand Down
4 changes: 3 additions & 1 deletion polyfill/lib/regex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export const datesplit = new RegExp(
`(${yearpart.source})(?:-(${monthpart.source})-(${daypart.source})|(${monthpart.source})(${daypart.source}))`
);
const timesplit = /(\d{2})(?::(\d{2})(?::(\d{2})(?:[.,](\d{1,9}))?)?|(\d{2})(?:(\d{2})(?:[.,](\d{1,9}))?)?)?/;
export const offset = /([+\u2212-])([01][0-9]|2[0-3])(?::?([0-5][0-9])(?::?([0-5][0-9])(?:[.,](\d{1,9}))?)?)?/;
export const offsetWithParts = /([+\u2212-])([01][0-9]|2[0-3])(?::?([0-5][0-9])(?::?([0-5][0-9])(?:[.,](\d{1,9}))?)?)?/;
export const offset =
/((?:[+\u2212-])(?:[01][0-9]|2[0-3])(?::?(?:[0-5][0-9])(?::?(?:[0-5][0-9])(?:[.,](?:\d{1,9}))?)?)?)/;
const offsetpart = new RegExp(`([zZ])|${offset.source}?`);
export const offsetIdentifier = /([+\u2212-])([01][0-9]|2[0-3])(?::?([0-5][0-9])?)?/;
export const annotation = /\[(!)?([a-z_][a-z0-9_-]*)=([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)\]/g;
Expand Down
16 changes: 8 additions & 8 deletions polyfill/test/validStrings.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import assert from 'assert';
import * as ES from '../lib/ecmascript.mjs';
import { Instant } from '../lib/instant.mjs';

const timezoneNames = Intl.supportedValuesOf('timeZone');
const calendarNames = Intl.supportedValuesOf('calendar');
Expand Down Expand Up @@ -249,12 +248,7 @@ const temporalSign = withCode(
);
const temporalDecimalFraction = fraction;
function saveOffset(data, result) {
// To canonicalize an offset string that may include nanoseconds, we use GetOffsetStringFor
const instant = new Instant(0n);
const fakeTimeZone = {
getOffsetNanosecondsFor: () => ES.ParseDateTimeUTCOffset(result).offsetNanoseconds
};
data.offset = ES.GetOffsetStringFor(fakeTimeZone, instant);
data.offset = ES.ParseDateTimeUTCOffset(result);
}
const utcOffsetSubMinutePrecision = withCode(
seq(
Expand Down Expand Up @@ -468,7 +462,13 @@ function fuzzMode(mode) {
for (let prop of comparisonItems[mode]) {
let expected = generatedData[prop];
if (prop !== 'tzName' && prop !== 'offset' && prop !== 'calendar') expected = expected || 0;
assert.equal(parsed[prop], expected, prop);
if (prop === 'offset' && expected) {
const parsedResult = ES.ParseDateTimeUTCOffset(parsed[prop]);
assert.equal(parsedResult.offsetNanoseconds, expected.offsetNanoseconds);
assert.equal(parsedResult.hasSubMinutePrecision, expected.hasSubMinutePrecision);
} else {
assert.equal(parsed[prop], expected, prop);
}
}
console.log(`${fuzzed} => ok`);
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion polyfill/test262
Submodule test262 updated 27 files
+3 −0 test/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Instant/prototype/toString/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Instant/prototype/toZonedDateTime/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Instant/prototype/toZonedDateTimeISO/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Now/plainDate/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Now/plainDateISO/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Now/plainDateTime/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Now/plainDateTimeISO/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Now/plainTimeISO/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Now/zonedDateTime/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/Now/zonedDateTimeISO/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/PlainDate/prototype/toZonedDateTime/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/PlainDateTime/prototype/toZonedDateTime/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/PlainTime/prototype/toZonedDateTime/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/TimeZone/from/timezone-string-datetime.js
+4 −0 test/built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/ZonedDateTime/from/argument-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/ZonedDateTime/prototype/until/argument-propertybag-timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/ZonedDateTime/prototype/withTimeZone/timezone-string-datetime.js
+3 −0 test/built-ins/Temporal/ZonedDateTime/timezone-string-datetime.js
+3 −0 test/intl402/Temporal/Now/plainDateTimeISO/timezone-string-datetime.js
+0 −1 test/staging/Temporal/Regex/old/timezone.js

0 comments on commit 5f9e901

Please sign in to comment.