Skip to content

Commit

Permalink
WIP: Create time zone record from time zone objects
Browse files Browse the repository at this point in the history
Similar to calendar records, when constructing a new ZonedDateTime,
eagerly get all the required time zone methods, and save them in a Record,
which goes into the ZonedDateTime's [[TimeZoneRecord]] internal slot.

_For internal use of the intrinsic ZonedDateTime constructor only_, in the
polyfill the constructor accepts a time zone record object instead of a
Temporal.TimeZone object. This has no observable effect on user code,
because user code can never obtain a time zone record object.

Still to do:
- spec text
- same problem as calendar records where the time zone record is lost when
  calling through user code
- when calling a TimeZone method directly, we create a new time zone
  record first, which Gets all the methods. Maybe this isn't needed unless
  we're calling the method from ZonedDateTime where we have the record?

See: #1294
  • Loading branch information
ptomato committed Mar 2, 2021
1 parent 05cd622 commit bcf869a
Show file tree
Hide file tree
Showing 45 changed files with 370 additions and 310 deletions.
1 change: 1 addition & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ overrides:
assert: readonly
verifyProperty: readonly
MINIMAL_CALENDAR_OBJECT: readonly
MINIMAL_TIME_ZONE_OBJECT: readonly
# test262 code complies to a different coding style
extends: 'eslint:recommended'
rules:
Expand Down
185 changes: 107 additions & 78 deletions polyfill/lib/ecmascript.mjs

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions polyfill/lib/intl.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
ISO_MICROSECOND,
ISO_NANOSECOND,
CALENDAR_RECORD,
TIME_ZONE
TIME_ZONE_RECORD
} from './slots.mjs';
import { TimeZone } from './timezone.mjs';

Expand Down Expand Up @@ -47,7 +47,7 @@ export function DateTimeFormat(locale = IntlDateTimeFormat().resolvedOptions().l
this[TZ_GIVEN] = options.timeZone ? options.timeZone : null;

this[ORIGINAL] = new IntlDateTimeFormat(locale, options);
this[TZ_RESOLVED] = new TimeZone(this.resolvedOptions().timeZone);
this[TZ_RESOLVED] = ES.NewTimeZoneRecord(new TimeZone(this.resolvedOptions().timeZone));
this[CAL_ID] = this.resolvedOptions().calendar;
this[DATE] = new IntlDateTimeFormat(locale, dateAmend(options));
this[YM] = new IntlDateTimeFormat(locale, yearMonthAmend(options));
Expand Down Expand Up @@ -368,8 +368,8 @@ function extractOverrides(temporalObj, main) {
);
}

let timeZone = GetSlot(temporalObj, TIME_ZONE);
const objTimeZone = ES.ToString(timeZone);
let timeZoneRecord = GetSlot(temporalObj, TIME_ZONE_RECORD);
const objTimeZone = ES.TimeZoneToString(timeZoneRecord);
if (main[TZ_GIVEN] && main[TZ_GIVEN] !== objTimeZone) {
throw new RangeError(`timeZone option ${main[TZ_GIVEN]} doesn't match actual time zone ${objTimeZone}`);
}
Expand Down
6 changes: 4 additions & 2 deletions polyfill/lib/now.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ function instant() {
}
function plainDateTime(calendarLike, temporalTimeZoneLike = timeZone()) {
const timeZone = ES.ToTemporalTimeZone(temporalTimeZoneLike);
const timeZoneRecord = ES.NewTimeZoneRecord(timeZone);
const calendar = ES.ToTemporalCalendar(calendarLike);
const calendarRecord = ES.NewCalendarRecord(calendar);
const inst = instant();
return ES.BuiltinTimeZoneGetPlainDateTimeFor(timeZone, inst, calendarRecord);
return ES.BuiltinTimeZoneGetPlainDateTimeFor(timeZoneRecord, inst, calendarRecord);
}
function plainDateTimeISO(temporalTimeZoneLike = timeZone()) {
const timeZone = ES.ToTemporalTimeZone(temporalTimeZoneLike);
const timeZoneRecord = ES.NewTimeZoneRecord(timeZone);
const calendar = ES.GetISO8601Calendar();
const calendarRecord = ES.NewCalendarRecord(calendar);
const inst = instant();
return ES.BuiltinTimeZoneGetPlainDateTimeFor(timeZone, inst, calendarRecord);
return ES.BuiltinTimeZoneGetPlainDateTimeFor(timeZoneRecord, inst, calendarRecord);
}
function zonedDateTime(calendarLike, temporalTimeZoneLike = timeZone()) {
const timeZone = ES.ToTemporalTimeZone(temporalTimeZoneLike);
Expand Down
18 changes: 6 additions & 12 deletions polyfill/lib/plaindate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -355,18 +355,12 @@ export class PlainDate {
toZonedDateTime(item) {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');

let timeZone, temporalTime;
let timeZoneRecord, temporalTime;
if (ES.Type(item) === 'Object') {
let timeZoneLike = item.timeZone;
if (timeZoneLike === undefined) {
timeZone = ES.ToTemporalTimeZone(item);
} else {
timeZone = ES.ToTemporalTimeZone(timeZoneLike);
temporalTime = item.plainTime;
}
} else {
timeZone = ES.ToTemporalTimeZone(item);
timeZoneRecord = ES.GetOrCreateTimeZoneRecord(item);
if (timeZoneRecord) temporalTime = item.plainTime;
}
if (!timeZoneRecord) timeZoneRecord = ES.NewTimeZoneRecord(ES.ToTemporalTimeZone(item));

const year = GetSlot(this, ISO_YEAR);
const month = GetSlot(this, ISO_MONTH);
Expand Down Expand Up @@ -402,9 +396,9 @@ export class PlainDate {
nanosecond,
calendarRecord
);
const instant = ES.BuiltinTimeZoneGetInstantFor(timeZone, dt, 'compatible');
const instant = ES.BuiltinTimeZoneGetInstantFor(timeZoneRecord, dt, 'compatible');
const ZonedDateTime = GetIntrinsic('%Temporal.ZonedDateTime%');
return new ZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, calendarRecord);
return new ZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZoneRecord, calendarRecord);
}
toPlainYearMonth() {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
Expand Down
5 changes: 3 additions & 2 deletions polyfill/lib/plaindatetime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -728,11 +728,12 @@ export class PlainDateTime {
toZonedDateTime(temporalTimeZoneLike, options = undefined) {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const timeZone = ES.ToTemporalTimeZone(temporalTimeZoneLike);
const timeZoneRecord = ES.NewTimeZoneRecord(timeZone);
options = ES.NormalizeOptionsObject(options);
const disambiguation = ES.ToTemporalDisambiguation(options);
const instant = ES.BuiltinTimeZoneGetInstantFor(timeZone, this, disambiguation);
const instant = ES.BuiltinTimeZoneGetInstantFor(timeZoneRecord, this, disambiguation);
const ZonedDateTime = GetIntrinsic('%Temporal.ZonedDateTime%');
return new ZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, GetSlot(this, CALENDAR_RECORD));
return new ZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZoneRecord, GetSlot(this, CALENDAR_RECORD));
}
toPlainDate() {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
Expand Down
6 changes: 3 additions & 3 deletions polyfill/lib/plaintime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ export class PlainTime {
if (timeZoneLike === undefined) {
throw new TypeError('missing timeZone property');
}
const timeZone = ES.ToTemporalTimeZone(timeZoneLike);
const timeZoneRecord = ES.NewTimeZoneRecord(ES.ToTemporalTimeZone(timeZoneLike));

const year = GetSlot(temporalDate, ISO_YEAR);
const month = GetSlot(temporalDate, ISO_MONTH);
Expand All @@ -487,9 +487,9 @@ export class PlainTime {
nanosecond,
calendarRecord
);
const instant = ES.BuiltinTimeZoneGetInstantFor(timeZone, dt, 'compatible');
const instant = ES.BuiltinTimeZoneGetInstantFor(timeZoneRecord, dt, 'compatible');
const ZonedDateTime = GetIntrinsic('%Temporal.ZonedDateTime%');
return new ZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, calendarRecord);
return new ZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZoneRecord, calendarRecord);
}
getISOFields() {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
Expand Down
10 changes: 10 additions & 0 deletions polyfill/lib/record.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,13 @@ export class CalendarRecord {
this.toString = requireCallable(calendar, 'toString');
}
}

export class TimeZoneRecord {
constructor(timeZone) {
if (Type(timeZone) !== 'Object') throw new TypeError('time zone must be an object');
this.object = timeZone;
this.getOffsetNanosecondsFor = requireCallable(timeZone, 'getOffsetNanosecondsFor');
this.getPossibleInstantsFor = requireCallableOrUndefined(timeZone, 'getPossibleInstantsFor');
this.toString = requireCallable(timeZone, 'toString');
}
}
2 changes: 1 addition & 1 deletion polyfill/lib/slots.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const MONTH_DAY_BRAND = 'slot-month-day-brand';

// ZonedDateTime
export const INSTANT = 'slot-cached-instant';
export const TIME_ZONE = 'slot-time-zone';
export const TIME_ZONE_RECORD = 'slot-time-zone-record';

// Duration
export const YEARS = 'slot-years';
Expand Down
9 changes: 6 additions & 3 deletions polyfill/lib/timezone.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,22 @@ export class TimeZone {
}
getOffsetStringFor(instant) {
instant = ES.ToTemporalInstant(instant, GetIntrinsic('%Temporal.Instant%'));
return ES.BuiltinTimeZoneGetOffsetStringFor(this, instant);
const timeZoneRecord = ES.NewTimeZoneRecord(this);
return ES.BuiltinTimeZoneGetOffsetStringFor(timeZoneRecord, instant);
}
getPlainDateTimeFor(instant, calendar = ES.GetISO8601Calendar()) {
instant = ES.ToTemporalInstant(instant, GetIntrinsic('%Temporal.Instant%'));
calendar = ES.ToTemporalCalendar(calendar);
const timeZoneRecord = ES.NewTimeZoneRecord(this);
const calendarRecord = ES.NewCalendarRecord(calendar);
return ES.BuiltinTimeZoneGetPlainDateTimeFor(this, instant, calendarRecord);
return ES.BuiltinTimeZoneGetPlainDateTimeFor(timeZoneRecord, instant, calendarRecord);
}
getInstantFor(dateTime, options = undefined) {
dateTime = ES.ToTemporalDateTime(dateTime, GetIntrinsic('%Temporal.PlainDateTime%'));
options = ES.NormalizeOptionsObject(options);
const disambiguation = ES.ToTemporalDisambiguation(options);
return ES.BuiltinTimeZoneGetInstantFor(this, dateTime, disambiguation);
const timeZoneRecord = ES.NewTimeZoneRecord(this);
return ES.BuiltinTimeZoneGetInstantFor(timeZoneRecord, dateTime, disambiguation);
}
getPossibleInstantsFor(dateTime) {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
Expand Down
Loading

0 comments on commit bcf869a

Please sign in to comment.