Skip to content

Commit

Permalink
Allow ISO strings with bracketed time zone but no offset
Browse files Browse the repository at this point in the history
Previously we considered a "time zone part" to be an offset plus an
optional bracketed name. Now an ISO string may have either of an "offset
part" and "time zone name part", independently of each other.

Closes: #933
  • Loading branch information
ptomato committed Oct 20, 2020
1 parent ff41d32 commit e3019eb
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 18 deletions.
2 changes: 1 addition & 1 deletion polyfill/lib/regex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const yearpart = /(?:[+-\u2212]\d{6}|\d{4})/;
const datesplit = new RegExp(`(${yearpart.source})(?:-(\\d{2})-(\\d{2})|(\\d{2})(\\d{2}))`);
const timesplit = /(\d{2})(?::(\d{2})(?::(\d{2})(?:[.,](\d{1,9}))?)?|(\d{2})(?:(\d{2})(?:[.,](\d{1,9}))?)?)?/;
export const offset = /([+-\u2212])([0-2][0-9])(?::?([0-5][0-9]))?/;
const zonesplit = new RegExp(`(?:([zZ])|(?:${offset.source}?(?:\\[(${timeZoneID.source})\\])?))`);
const zonesplit = new RegExp(`(?:([zZ])|(?:${offset.source})?(?:\\[(${timeZoneID.source})\\])?)`);
const calendar = new RegExp(`\\[c=(${calendarID.source})\\]`);

export const instant = new RegExp(
Expand Down
24 changes: 13 additions & 11 deletions polyfill/test/regex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('fromString regex', () => {
generateTest(`1976-11-18${timeSep}15:23`, 'Z', [1976, 11, 18, 15, 23, 30, 123, 456, 789])
);
// Time zone with bracketed name
['+01:00', '+01', '+0100'].forEach((zoneString) =>
['+01:00', '+01', '+0100', ''].forEach((zoneString) =>
generateTest('1976-11-18T15:23', `${zoneString}[Europe/Vienna]`, [1976, 11, 18, 14, 23, 30, 123, 456, 789])
);
// Time zone with only offset
Expand Down Expand Up @@ -101,6 +101,7 @@ describe('fromString regex', () => {
[
'+0100[Europe/Vienna]',
'+01:00[Europe/Vienna]',
'[Europe/Vienna]',
'+01:00[Custom/Vienna]',
'-0400',
'-04:00',
Expand Down Expand Up @@ -132,7 +133,7 @@ describe('fromString regex', () => {
test('1976-11-18T15', [1976, 11, 18, 15]);
test('1976-11-18', [1976, 11, 18]);
// Representations with calendar
['', 'Z', '+01:00[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
['', 'Z', '+01:00[Europe/Vienna]', '+01:00[Custom/Vienna]', '[Europe/Vienna]'].forEach((zoneString) =>
test(`1976-11-18T15:23:30.123456789${zoneString}[c=iso8601]`, [1976, 11, 18, 15, 23, 30, 123, 456, 789])
);
});
Expand All @@ -157,7 +158,7 @@ describe('fromString regex', () => {
// Time separators
['T', 't', ' '].forEach((timeSep) => generateTest(`1976-11-18${timeSep}15:23`, ''));
// Various forms of time zone
['+0100[Europe/Vienna]', '+01:00[Custom/Vienna]', '-0400', ''].forEach((zoneString) =>
['+0100[Europe/Vienna]', '[Europe/Vienna]', '+01:00[Custom/Vienna]', '-0400', ''].forEach((zoneString) =>
generateTest('1976-11-18T15:23', zoneString)
);
// Various numbers of decimal places
Expand Down Expand Up @@ -188,7 +189,7 @@ describe('fromString regex', () => {
test('1512-11-18', [1512, 11, 18]);
test('15121118', [1512, 11, 18]);
// Representations with calendar
['', 'Z', '+01:00[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
['', 'Z', '+01:00[Europe/Vienna]', '[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
test(`1976-11-18T15:23:30.123456789${zoneString}[c=iso8601]`, [1976, 11, 18])
);
test('1976-11-18[c=iso8601]', [1976, 11, 18]);
Expand Down Expand Up @@ -241,11 +242,11 @@ describe('fromString regex', () => {
test('1976-11-18', []);
// Time-only forms
generateTest('15:23', '');
['+01:00[Europe/Vienna]', '+01:00[Custom/Vienna]', '-04:00', 'Z', ''].forEach((zoneStr) =>
['+01:00[Europe/Vienna]', '[Europe/Vienna]', '+01:00[Custom/Vienna]', '-04:00', 'Z', ''].forEach((zoneStr) =>
test(`15${zoneStr}`, [15])
);
// Representations with calendar
['', 'Z', '+01:00[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
['', 'Z', '+01:00[Europe/Vienna]', '[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
test(`1976-11-18T15:23:30.123456789${zoneString}[c=iso8601]`, [15, 23, 30, 123, 456, 789])
);
test('15:23:30.123456789[c=iso8601]', [15, 23, 30, 123, 456, 789]);
Expand All @@ -270,7 +271,7 @@ describe('fromString regex', () => {
// Time separators
['T', 't', ' '].forEach((timeSep) => generateTest(`1976-11-18${timeSep}15:23`, ''));
// Various forms of time zone
['+0100[Europe/Vienna]', '+01:00[Custom/Vienna]', '-0400', ''].forEach((zoneString) =>
['+0100[Europe/Vienna]', '[Europe/Vienna]', '+01:00[Custom/Vienna]', '-0400', ''].forEach((zoneString) =>
generateTest('1976-11-18T15:23', zoneString)
);
// Various numbers of decimal places
Expand Down Expand Up @@ -310,7 +311,7 @@ describe('fromString regex', () => {
test('1512-11', [1512, 11]);
test('151211', [1512, 11]);
// Representations with calendar
['', 'Z', '+01:00[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
['', 'Z', '+01:00[Europe/Vienna]', '[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
test(`1976-11-18T15:23:30.123456789${zoneString}[c=iso8601]`, [1976, 11])
);
test('1976-11-01[c=iso8601]', [1976, 11]);
Expand All @@ -335,7 +336,7 @@ describe('fromString regex', () => {
// Time separators
['T', 't', ' '].forEach((timeSep) => generateTest(`1976-11-18${timeSep}15:23`, ''));
// Various forms of time zone
['+0100[Europe/Vienna]', '+01:00[Custom/Vienna]', '-0400', ''].forEach((zoneString) =>
['+0100[Europe/Vienna]', '[Europe/Vienna]', '+01:00[Custom/Vienna]', '-0400', ''].forEach((zoneString) =>
generateTest('1976-11-18T15:23', zoneString)
);
// Various numbers of decimal places
Expand Down Expand Up @@ -378,7 +379,7 @@ describe('fromString regex', () => {
test('--11-18', [11, 18]);
test('--1118', [11, 18]);
// Representations with calendar
['', 'Z', '+01:00[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
['', 'Z', '+01:00[Europe/Vienna]', '[Europe/Vienna]', '+01:00[Custom/Vienna]'].forEach((zoneString) =>
test(`1976-11-18T15:23:30.123456789${zoneString}[c=iso8601]`, [11, 18])
);
test('1972-11-18[c=iso8601]', [11, 18]);
Expand Down Expand Up @@ -424,7 +425,7 @@ describe('fromString regex', () => {
// Representations with reduced precision
'1976-11-18T15'
].forEach((dateTimeString) => {
['+01:00', '+01', '+0100'].forEach((zoneString) =>
['+01:00', '+01', '+0100', ''].forEach((zoneString) =>
test(`${dateTimeString}${zoneString}[Europe/Vienna]`, 'Europe/Vienna')
);
['-04:00', '-04', '-0400'].forEach((zoneString) => test(`${dateTimeString}${zoneString}`, '-04:00'));
Expand Down Expand Up @@ -452,6 +453,7 @@ describe('fromString regex', () => {
// Representations with calendar
test('1976-11-18T15:23:30.123456789Z[c=iso8601]', 'UTC');
test('1976-11-18T15:23:30.123456789-04:00[c=iso8601]', '-04:00');
test('1976-11-18T15:23:30.123456789[Europe/Vienna][c=iso8601]', 'Europe/Vienna');
test('1976-11-18T15:23:30.123456789+01:00[Europe/Vienna][c=iso8601]', 'Europe/Vienna');
});

Expand Down
9 changes: 6 additions & 3 deletions polyfill/test/validStrings.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,12 @@ const timeZoneIANAName = withCode(
choice(...timezoneNames),
(data, result) => (data.ianaName = ES.GetCanonicalTimeZoneIdentifier(result).toString())
);
const timeZone = withCode(choice(utcDesignator, seq(timeZoneUTCOffset, ['[', timeZoneIANAName, ']'])), (data) => {
if (!('offset' in data)) data.offset = undefined;
});
const timeZone = withCode(
choice(utcDesignator, timeZoneUTCOffset, seq([timeZoneUTCOffset], '[', timeZoneIANAName, ']')),
(data) => {
if (!('offset' in data)) data.offset = undefined;
}
);
const temporalTimeZoneIdentifier = withCode(choice(timeZoneUTCOffset, timeZoneIANAName), (data) => {
if (!('offset' in data)) data.offset = undefined;
});
Expand Down
7 changes: 4 additions & 3 deletions spec/abstractops.html
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ <h1>ISO 8601 grammar</h1>
TimeZone :
UTCDesignator
TimeZoneUTCOffset
TimeZoneUTCOffset `[` TimeZoneIANAName `]`
TimeZoneUTCOffset? `[` TimeZoneIANAName `]`

CalChar :
Alpha
Expand Down Expand Up @@ -1007,8 +1007,9 @@ <h1>ParseTemporalTimeZoneString ( _isoString_ )</h1>
1. Else,
1. Set _sign_ to 1.
1. Set _minute_ to ! ToInteger(_minute_).
1. Let _offsetNanoseconds_ be _sign_ × (_hour_ × 60 + _minute_) × 60 × 10<sup>9</sup>.
1. Let _name_ be ! FormatTimeZoneOffsetString(_offsetNanoseconds_).
1. If _name_ is *undefined*, then
1. Let _offsetNanoseconds_ be _sign_ × (_hour_ × 60 + _minute_) × 60 × 10<sup>9</sup>.
1. Let _name_ be ! FormatTimeZoneOffsetString(_offsetNanoseconds_).
1. Assert: ! IsValidTimeZoneName(_name_) is *true*.
1. Let _name_ be ! CanonicalizeTimeZoneName(_name_).
1. Return the new Record: {
Expand Down

0 comments on commit e3019eb

Please sign in to comment.