diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs index e9f24c4463..b05c3b701c 100644 --- a/polyfill/lib/ecmascript.mjs +++ b/polyfill/lib/ecmascript.mjs @@ -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, @@ -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; @@ -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}`); } @@ -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); diff --git a/polyfill/lib/regex.mjs b/polyfill/lib/regex.mjs index 8a71c63841..62658f7f6e 100644 --- a/polyfill/lib/regex.mjs +++ b/polyfill/lib/regex.mjs @@ -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; diff --git a/polyfill/test/validStrings.mjs b/polyfill/test/validStrings.mjs index 3ba6aa9b1f..cec937b68c 100644 --- a/polyfill/test/validStrings.mjs +++ b/polyfill/test/validStrings.mjs @@ -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'); @@ -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( @@ -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) { diff --git a/polyfill/test262 b/polyfill/test262 index 29dde1ce0e..66f3959c14 160000 --- a/polyfill/test262 +++ b/polyfill/test262 @@ -1 +1 @@ -Subproject commit 29dde1ce0e97a8bd6423c4397b9d3b51df0a1d8e +Subproject commit 66f3959c14646a4caba79e91adfe976c28246bf9