Skip to content

Commit

Permalink
Normative: Look up calendar methods only once
Browse files Browse the repository at this point in the history
Introduce a new spec type, Calendar Methods Record, which stores a
calendar object's methods once they have been observably looked up. The
record is passed around into abstract operations instead of the calendar
object itself.

The mechanism doesn't currently allow for storing all Temporal.Calendar
methods, only the ones that are explicitly called internally in other
places than the Zoned and Plain types' methods of the same name, as part
of a different API call.

Some methods not included in the record are only called by the same-named
methods on other Temporal types (example, Calendar.p.dayOfWeek is only
called by ZonedDateTime.p.dayOfWeek, PlainDateTime.p.dayOfWeek, and
PlainDate.p.dayOfWeek.)

Other methods not included in the record are potentially called through
lookups in PrepareTemporalFields, so it's not possible to pre-fetch them
without making polyfilling difficult. (Example, Calendar.p.monthCode is
called in `plainYearMonth.toPlainDate(...)` by looking up the
`plainYearMonth.monthCode` property in PrepareTemporalFields. Shortcutting
that lookup would mean that PlainDate.p.monthCode could never be
polyfilled.)

This is a large commit but most of it is mechanical replacement of
Temporal.Calendar variables with Calendar Methods Record variables.
  • Loading branch information
ptomato committed Oct 4, 2023
1 parent 6e2acfd commit 584fd67
Show file tree
Hide file tree
Showing 16 changed files with 1,129 additions and 672 deletions.
81 changes: 72 additions & 9 deletions polyfill/lib/duration.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import bigInt from 'big-integer';

import * as ES from './ecmascript.mjs';
import { MakeIntrinsicClass } from './intrinsicclass.mjs';
import { CalendarMethodRecord } from './methodrecord.mjs';
import {
YEARS,
MONTHS,
Expand Down Expand Up @@ -329,13 +330,48 @@ export class Duration {
plainRelativeTo = ES.TemporalDateTimeToDate(precalculatedPlainDateTime);
}

let calendarRec;
if (zonedRelativeTo || plainRelativeTo) {
const calendar = GetSlot(zonedRelativeTo ?? plainRelativeTo, CALENDAR);
calendarRec = new CalendarMethodRecord(calendar);
if (
years !== 0 ||
months !== 0 ||
weeks !== 0 ||
largestUnit === 'year' ||
largestUnit === 'month' ||
largestUnit === 'week' ||
smallestUnit === 'year' ||
smallestUnit === 'month' ||
smallestUnit === 'week'
) {
calendarRec.lookup('dateAdd');
}
if (
largestUnit === 'year' ||
(largestUnit === 'month' && years !== 0) ||
smallestUnit === 'year' ||
// Edge condition in AdjustRoundedDurationDays:
(zonedRelativeTo &&
!roundingGranularityIsNoop &&
smallestUnit !== 'year' &&
smallestUnit !== 'month' &&
smallestUnit !== 'week' &&
smallestUnit !== 'day' &&
(largestUnit === 'year' || largestUnit === 'month' || largestUnit === 'week'))
) {
calendarRec.lookup('dateUntil');
}
}

({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative(
years,
months,
weeks,
days,
largestUnit,
plainRelativeTo
plainRelativeTo,
calendarRec
));
({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } =
ES.RoundDuration(
Expand All @@ -353,6 +389,7 @@ export class Duration {
smallestUnit,
roundingMode,
plainRelativeTo,
calendarRec,
zonedRelativeTo,
timeZoneRec,
precalculatedPlainDateTime
Expand All @@ -374,6 +411,7 @@ export class Duration {
smallestUnit,
roundingMode,
zonedRelativeTo,
calendarRec,
timeZoneRec,
precalculatedPlainDateTime
));
Expand Down Expand Up @@ -408,7 +446,8 @@ export class Duration {
weeks,
days,
largestUnit,
plainRelativeTo
plainRelativeTo,
calendarRec
));

return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
Expand Down Expand Up @@ -458,20 +497,38 @@ export class Duration {
plainRelativeTo = ES.TemporalDateTimeToDate(precalculatedPlainDateTime);
}

let calendar, calendarRec;
if (zonedRelativeTo) {
calendar = GetSlot(zonedRelativeTo, CALENDAR);
} else if (plainRelativeTo) {
calendar = GetSlot(plainRelativeTo, CALENDAR);
}
if (calendar) {
calendarRec = new CalendarMethodRecord(calendar);
if (years !== 0 || months !== 0 || weeks !== 0 || unit === 'year' || unit === 'month' || unit === 'week') {
calendarRec.lookup('dateAdd');
}
if (unit === 'year' || (unit === 'month' && years !== 0)) {
calendarRec.lookup('dateUntil');
}
}

// Convert larger units down to days
({ years, months, weeks, days } = ES.UnbalanceDateDurationRelative(
years,
months,
weeks,
days,
unit,
plainRelativeTo
plainRelativeTo,
calendarRec
));
// If the unit we're totalling is smaller than `days`, convert days down to that unit.
let balanceResult;
if (zonedRelativeTo) {
const intermediate = ES.MoveRelativeZonedDateTime(
zonedRelativeTo,
calendarRec,
timeZoneRec,
years,
months,
Expand Down Expand Up @@ -525,6 +582,7 @@ export class Duration {
unit,
'trunc',
plainRelativeTo,
calendarRec,
zonedRelativeTo,
timeZoneRec,
precalculatedPlainDateTime
Expand Down Expand Up @@ -668,15 +726,20 @@ export class Duration {

const calendarUnitsPresent = y1 !== 0 || y2 !== 0 || mon1 !== 0 || mon2 !== 0 || w1 !== 0 || w2 !== 0;

let calendarRec;
if (zonedRelativeTo || plainRelativeTo) {
calendarRec = new CalendarMethodRecord(GetSlot(zonedRelativeTo ?? plainRelativeTo, CALENDAR));
if (calendarUnitsPresent) calendarRec.lookup('dateAdd');
}

if (zonedRelativeTo && (calendarUnitsPresent || d1 != 0 || d2 !== 0)) {
const instant = GetSlot(zonedRelativeTo, INSTANT);
const calendar = GetSlot(zonedRelativeTo, CALENDAR);
const precalculatedPlainDateTime = ES.GetPlainDateTimeFor(timeZoneRec, instant, calendar);
const precalculatedPlainDateTime = ES.GetPlainDateTimeFor(timeZoneRec, instant, calendarRec.receiver);

const after1 = ES.AddZonedDateTime(
instant,
timeZoneRec,
calendar,
calendarRec,
y1,
mon1,
w1,
Expand All @@ -692,7 +755,7 @@ export class Duration {
const after2 = ES.AddZonedDateTime(
instant,
timeZoneRec,
calendar,
calendarRec,
y2,
mon2,
w2,
Expand All @@ -710,8 +773,8 @@ export class Duration {

if (calendarUnitsPresent) {
// plainRelativeTo may be undefined, and if so Unbalance will throw
({ days: d1 } = ES.UnbalanceDateDurationRelative(y1, mon1, w1, d1, 'day', plainRelativeTo));
({ days: d2 } = ES.UnbalanceDateDurationRelative(y2, mon2, w2, d2, 'day', plainRelativeTo));
({ days: d1 } = ES.UnbalanceDateDurationRelative(y1, mon1, w1, d1, 'day', plainRelativeTo, calendarRec));
({ days: d2 } = ES.UnbalanceDateDurationRelative(y2, mon2, w2, d2, 'day', plainRelativeTo, calendarRec));
}
h1 = bigInt(h1).add(bigInt(d1).multiply(24));
h2 = bigInt(h2).add(bigInt(d2).multiply(24));
Expand Down
Loading

0 comments on commit 584fd67

Please sign in to comment.