Skip to content

Commit

Permalink
WIP - [c=...] annotation in ISO strings
Browse files Browse the repository at this point in the history
TODO:
- failing tests
- docs
  • Loading branch information
ptomato committed Jun 3, 2020
1 parent 799cc50 commit b3d6d5a
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 24 deletions.
7 changes: 5 additions & 2 deletions polyfill/lib/date.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ export class Date {
let month = ES.ISODateTimePartString(GetSlot(this, ISO_MONTH));
let day = ES.ISODateTimePartString(GetSlot(this, ISO_DAY));
let resultString = `${year}-${month}-${day}`;
const calendarId = GetSlot(this, CALENDAR).id;
if (calendarId !== 'iso8601') resultString += `[c=${calendarId}]`;
return resultString;
}
toLocaleString(...args) {
Expand Down Expand Up @@ -210,9 +212,10 @@ export class Date {
result = calendar.dateFromFields(item, options, this);
}
} else {
({ year, month, day } = ES.ParseTemporalDateString(ES.ToString(item)));
({ year, month, day, calendar } = ES.ParseTemporalDateString(ES.ToString(item)));
({ year, month, day } = ES.RegulateDate(year, month, day, disambiguation));
calendar = ES.GetDefaultCalendar();
if (!calendar) calendar = ES.GetDefaultCalendar();
calendar = ES.ToTemporalCalendar(calendar);
result = new this(year, month, day, calendar);
}
if (!ES.IsTemporalDate(result)) throw new TypeError('invalid result');
Expand Down
8 changes: 6 additions & 2 deletions polyfill/lib/datetime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ export class DateTime {
GetSlot(this, NANOSECOND)
);
let resultString = `${year}-${month}-${day}T${hour}:${minute}${second ? `:${second}` : ''}`;
const calendarId = GetSlot(this, CALENDAR).id;
if (calendarId !== 'iso8601') resultString += `[c=${calendarId}]`;
return resultString;
}
toLocaleString(...args) {
Expand Down Expand Up @@ -540,9 +542,11 @@ export class DateTime {
second,
millisecond,
microsecond,
nanosecond
nanosecond,
calendar
} = ES.ParseTemporalDateTimeString(ES.ToString(item)));
calendar = ES.GetDefaultCalendar();
if (!calendar) calendar = ES.GetDefaultCalendar();
calendar = ES.ToTemporalCalendar(calendar);
}
({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = ES.RegulateDateTime(
year,
Expand Down
34 changes: 26 additions & 8 deletions polyfill/lib/ecmascript.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,22 @@ export const ES = ObjectAssign({}, ES2019, {
let ianaName = match[16];
if (ianaName) ianaName = ES.GetCanonicalTimeZoneIdentifier(ianaName).toString();
const zone = match[13] ? 'UTC' : ianaName || offset;
return { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, zone, ianaName, offset };
const calendar = match[17] || null;
return {
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
zone,
ianaName,
offset,
calendar
};
},
ParseTemporalAbsoluteString: (isoString) => {
return ES.ParseISODateTime(isoString, { zoneRequired: true });
Expand Down Expand Up @@ -142,25 +157,26 @@ export const ES = ObjectAssign({}, ES2019, {
},
ParseTemporalYearMonthString: (isoString) => {
const match = PARSE.yearmonth.exec(isoString);
let year, month;
let year, month, calendar, refISODay;
if (match) {
year = ES.ToInteger(match[1]);
month = ES.ToInteger(match[2]);
calendar = match[3] || null;
} else {
({ year, month } = ES.ParseISODateTime(isoString, { zoneRequired: false }));
({ year, month, calendar, day: refISODay } = ES.ParseISODateTime(isoString, { zoneRequired: false }));
}
return { year, month };
return { year, month, calendar, refISODay };
},
ParseTemporalMonthDayString: (isoString) => {
const match = PARSE.monthday.exec(isoString);
let month, day;
let month, day, calendar, refISOYear;
if (match) {
month = ES.ToInteger(match[1]);
day = ES.ToInteger(match[2]);
} else {
({ month, day } = ES.ParseISODateTime(isoString, { zoneRequired: false }));
({ month, day, calendar, year: refISOYear } = ES.ParseISODateTime(isoString, { zoneRequired: false }));
}
return { month, day };
return { month, day, calendar, refISOYear };
},
ParseTemporalTimeZoneString: (stringIdent) => {
try {
Expand Down Expand Up @@ -200,8 +216,10 @@ export const ES = ObjectAssign({}, ES2019, {
nanosecond,
ianaName,
offset,
zone
zone,
calendar
} = ES.ParseTemporalAbsoluteString(isoString);
if (calendar !== null) throw new RangeError('Temporal.Absolute has no calendar');
const possibleEpochNs = ES.GetTimeZoneEpochValue(
zone,
year,
Expand Down
12 changes: 9 additions & 3 deletions polyfill/lib/monthday.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export class MonthDay {
let month = ES.ISODateTimePartString(GetSlot(this, ISO_MONTH));
let day = ES.ISODateTimePartString(GetSlot(this, ISO_DAY));
let resultString = `${month}-${day}`;
const calendarId = GetSlot(this, CALENDAR).id;
if (calendarId !== 'iso8601') {
const year = ES.ISOYearString(GetSlot(this, REF_ISO_YEAR));
resultString = `${year}-${resultString}[c=${calendarId}]`;
}
return resultString;
}
toLocaleString(...args) {
Expand Down Expand Up @@ -113,10 +118,11 @@ export class MonthDay {
result = calendar.monthDayFromFields(item, options, this);
}
} else {
({ month, day } = ES.ParseTemporalMonthDayString(ES.ToString(item)));
({ month, day, refISOYear, calendar } = ES.ParseTemporalMonthDayString(ES.ToString(item)));
({ month, day } = ES.RegulateMonthDay(month, day, disambiguation));
calendar = ES.GetDefaultCalendar();
refISOYear = 1972;
if (!calendar) calendar = ES.GetDefaultCalendar();
calendar = ES.ToTemporalCalendar(calendar);
if (refISOYear === undefined) refISOYear = 1972;
result = new this(month, day, calendar, refISOYear);
}
if (!ES.IsTemporalMonthDay(result)) throw new TypeError('invalid result');
Expand Down
14 changes: 11 additions & 3 deletions polyfill/lib/regex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ const yearpart = /(?:[+-]\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}))?)?)?/;
const zonesplit = /(?:(Z)|(?:([+-]\d{2})(?::?(\d{2}))?(?:\[([^\]\s]+)\])?))/i;
const calendar = /\[c=([^\]\s]+)\]/;

export const absolute = new RegExp(`^${datesplit.source}(?:T|\\s+)${timesplit.source}${zonesplit.source}$`, 'i');
export const absolute = new RegExp(
`^${datesplit.source}(?:T|\\s+)${timesplit.source}${zonesplit.source}(?:${calendar.source})?$`,
'i'
);
export const datetime = new RegExp(
`^${datesplit.source}(?:(?:T|\\s+)${timesplit.source}(?:${zonesplit.source})?)?$`,
`^${datesplit.source}(?:(?:T|\\s+)${timesplit.source}(?:${zonesplit.source})?(?:${calendar.source})?)?$`,
'i'
);

export const time = new RegExp(`^${timesplit.source}(?:${zonesplit.source})?$`, 'i');
export const time = new RegExp(`^${timesplit.source}(?:${zonesplit.source})?(?:${calendar.source})?$`, 'i');

// The short forms of YearMonth and MonthDay are only for the ISO calendar.
// Non-ISO calendar YearMonth and MonthDay have to parse as a Temporal.Date,
// with the reference fields.
// YYYYMM forbidden by ISO 8601, but since it is not ambiguous with anything
// else we could parse in a YearMonth context, we allow it
export const yearmonth = new RegExp(`^(${yearpart.source})-?(\\d{2})$`);
Expand Down
2 changes: 2 additions & 0 deletions polyfill/test/absolute.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ describe('Absolute', () => {
equal(`${Absolute.from('1976-11-18T15:23:30+00')}`, '1976-11-18T15:23:30Z');
equal(`${Absolute.from('1976-11-18T15Z')}`, '1976-11-18T15:00Z');
});
it('throws if specifying a calendar', () =>
throws(() => Temporal.Absolute.from('1976-11-18T15:23:30.123456789Z[c=gregory]'), RangeError));
});
describe('Absolute.plus works', () => {
const abs = Absolute.from('1969-12-25T12:23:45.678901234Z');
Expand Down
44 changes: 38 additions & 6 deletions polyfill/test/regex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Pretty from '@pipobscure/demitasse-pretty';
const { reporter } = Pretty;

import { strict as assert } from 'assert';
const { equal } = assert;
const { equal, throws } = assert;

import * as Temporal from 'tc39-temporal';

Expand Down Expand Up @@ -70,7 +70,7 @@ describe('fromString regex', () => {
describe('datetime', () => {
function test(isoString, components) {
it(isoString, () => {
const [y, mon, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0] = components;
const [y, mon, d, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0, cid = 'iso8601'] = components;
const datetime = Temporal.DateTime.from(isoString);
equal(datetime.year, y);
equal(datetime.month, mon);
Expand All @@ -81,6 +81,7 @@ describe('fromString regex', () => {
equal(datetime.millisecond, ms);
equal(datetime.microsecond, µs);
equal(datetime.nanosecond, ns);
equal(datetime.calendar.id, cid);
});
}
function generateTest(dateTimeString, zoneString) {
Expand Down Expand Up @@ -115,16 +116,21 @@ describe('fromString regex', () => {
// Representations with reduced precision
test('1976-11-18T15', [1976, 11, 18, 15]);
test('1976-11-18', [1976, 11, 18]);
// Representations with calendar
['', 'Z', '+01:00[Europe/Vienna]'].forEach((zoneString) =>
test(`1976-11-18T15:23:30.123456789${zoneString}[c=iso8601]`, [1976, 11, 18, 15, 23, 30, 123, 456, 789])
);
});

describe('date', () => {
function test(isoString, components) {
it(isoString, () => {
const [y, m, d] = components;
const [y, m, d, cid = 'iso8601'] = components;
const date = Temporal.Date.from(isoString);
equal(date.year, y);
equal(date.month, m);
equal(date.day, d);
equal(date.calendar.id, cid);
});
}
function generateTest(dateTimeString, zoneString) {
Expand Down Expand Up @@ -152,7 +158,7 @@ describe('fromString regex', () => {
'19761118T152330.1234'
].forEach((str) => test(str, [1976, 11, 18]));
// Representations with reduced precision
test('1976-11-18T15', [1976, 11, 18, 15]);
test('1976-11-18T15', [1976, 11, 18]);
// Date-only forms
test('1976-11-18', [1976, 11, 18]);
test('19761118', [1976, 11, 18]);
Expand All @@ -162,6 +168,11 @@ describe('fromString regex', () => {
test('-0003001118', [-300, 11, 18]);
test('1512-11-18', [1512, 11, 18]);
test('15121118', [1512, 11, 18]);
// Representations with calendar
['', 'Z', '+01:00[Europe/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]);
});

describe('time', () => {
Expand Down Expand Up @@ -210,15 +221,21 @@ describe('fromString regex', () => {
// Time-only forms
generateTest('15:23', '');
['+01:00[Europe/Vienna]', '-04:00', 'Z', ''].forEach((zoneStr) => test(`15${zoneStr}`, [15]));
// Representations with calendar
['', 'Z', '+01:00[Europe/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]);
});

describe('yearmonth', () => {
function test(isoString, components) {
it(isoString, () => {
const [y, m] = components;
const [y, m, cid = 'iso8601'] = components;
const yearMonth = Temporal.YearMonth.from(isoString);
equal(yearMonth.year, y);
equal(yearMonth.month, m);
equal(yearMonth.calendar.id, cid);
});
}
function generateTest(dateTimeString, zoneString) {
Expand Down Expand Up @@ -265,15 +282,21 @@ describe('fromString regex', () => {
test('-00030011', [-300, 11]);
test('1512-11', [1512, 11]);
test('151211', [1512, 11]);
// Representations with calendar
['', 'Z', '+01:00[Europe/Vienna]'].forEach((zoneString) =>
test(`1976-11-18T15:23:30.123456789${zoneString}[c=iso8601]`, [1976, 11])
);
test('1976-11-01[c=iso8601]', [1976, 11]);
});

describe('monthday', () => {
function test(isoString, components) {
it(isoString, () => {
const [m, d] = components;
const [m, d, cid = 'iso8601'] = components;
const monthDay = Temporal.MonthDay.from(isoString);
equal(monthDay.month, m);
equal(monthDay.day, d);
equal(monthDay.calendar.id, cid);
});
}
function generateTest(dateTimeString, zoneString) {
Expand Down Expand Up @@ -323,6 +346,11 @@ describe('fromString regex', () => {
// RFC 3339 month-day form
test('--11-18', [11, 18]);
test('--1118', [11, 18]);
// Representations with calendar
['', 'Z', '+01:00[Europe/Vienna]'].forEach((zoneString) =>
test(`1976-11-18T15:23:30.123456789${zoneString}[c=iso8601]`, [11, 18])
);
test('1972-11-18[c=iso8601]', [11, 18]);
});

describe('timezone', () => {
Expand Down Expand Up @@ -382,6 +410,10 @@ describe('fromString regex', () => {
test('-03:00', '-03:00');
test('+03', '+03:00');
test('-03', '-03:00');
// 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+01:00[Europe/Vienna][c=iso8601]', 'Europe/Vienna');
});

describe('duration', () => {
Expand Down

0 comments on commit b3d6d5a

Please sign in to comment.