From 34ce4bd53ad0ebeb3dd4afceb4718e4e6b631c2c Mon Sep 17 00:00:00 2001
From: Philip Chimento <pchimento@igalia.com>
Date: Wed, 8 Mar 2023 21:59:52 -0800
Subject: [PATCH] Temporal: Fast-path dateAdd() when adding only days

---
 .../prototype/add/order-of-operations.js      | 97 ++++++++++++++++++
 .../prototype/round/order-of-operations.js    | 99 ++++++++++++++++---
 .../prototype/subtract/order-of-operations.js | 97 ++++++++++++++++++
 .../prototype/total/order-of-operations.js    | 79 +++++++++++++--
 .../prototype/add/balance-smaller-units.js    | 28 +-----
 .../prototype/add/order-of-operations.js      | 53 +++++++++-
 .../prototype/since/order-of-operations.js    | 15 ++-
 .../subtract/balance-smaller-units.js         | 30 +-----
 .../prototype/subtract/order-of-operations.js | 53 +++++++++-
 .../prototype/until/order-of-operations.js    | 15 ++-
 .../prototype/add/order-of-operations.js      | 51 +++++++++-
 .../prototype/since/order-of-operations.js    | 19 ++--
 .../prototype/subtract/order-of-operations.js | 51 +++++++++-
 .../prototype/until/order-of-operations.js    | 19 ++--
 ...-dateadd-called-with-plaindate-instance.js |  4 +-
 .../prototype/add/order-of-operations.js      | 76 ++++++++++++++
 .../prototype/since/order-of-operations.js    | 15 ++-
 ...-dateadd-called-with-plaindate-instance.js |  4 +-
 .../prototype/subtract/order-of-operations.js | 81 +++++++++++++++
 .../prototype/until/order-of-operations.js    | 15 ++-
 .../prototype/since/order-of-operations.js    | 19 ++--
 .../prototype/until/order-of-operations.js    | 19 ++--
 22 files changed, 778 insertions(+), 161 deletions(-)

diff --git a/test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js b/test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js
index 6f3d413fb1..7c65dd8f97 100644
--- a/test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js
+++ b/test/built-ins/Temporal/Duration/prototype/add/order-of-operations.js
@@ -153,6 +153,103 @@ instance.add(fields, createOptionsObserver(plainRelativeTo));
 assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo");
 actual.splice(0); // clear
 
+const expectedOpsForPlainRelativeToNoCalendarOperations = [
+  // ToTemporalDurationRecord
+  "get fields.days",
+  "get fields.days.valueOf",
+  "call fields.days.valueOf",
+  "get fields.hours",
+  "get fields.hours.valueOf",
+  "call fields.hours.valueOf",
+  "get fields.microseconds",
+  "get fields.microseconds.valueOf",
+  "call fields.microseconds.valueOf",
+  "get fields.milliseconds",
+  "get fields.milliseconds.valueOf",
+  "call fields.milliseconds.valueOf",
+  "get fields.minutes",
+  "get fields.minutes.valueOf",
+  "call fields.minutes.valueOf",
+  "get fields.months",
+  "get fields.nanoseconds",
+  "get fields.nanoseconds.valueOf",
+  "call fields.nanoseconds.valueOf",
+  "get fields.seconds",
+  "get fields.seconds.valueOf",
+  "call fields.seconds.valueOf",
+  "get fields.weeks",
+  "get fields.years",
+  // ToRelativeTemporalObject
+  "get options.relativeTo",
+  "get options.relativeTo.calendar",
+  "has options.relativeTo.calendar.dateAdd",
+  "has options.relativeTo.calendar.dateFromFields",
+  "has options.relativeTo.calendar.dateUntil",
+  "has options.relativeTo.calendar.day",
+  "has options.relativeTo.calendar.dayOfWeek",
+  "has options.relativeTo.calendar.dayOfYear",
+  "has options.relativeTo.calendar.daysInMonth",
+  "has options.relativeTo.calendar.daysInWeek",
+  "has options.relativeTo.calendar.daysInYear",
+  "has options.relativeTo.calendar.fields",
+  "has options.relativeTo.calendar.id",
+  "has options.relativeTo.calendar.inLeapYear",
+  "has options.relativeTo.calendar.mergeFields",
+  "has options.relativeTo.calendar.month",
+  "has options.relativeTo.calendar.monthCode",
+  "has options.relativeTo.calendar.monthDayFromFields",
+  "has options.relativeTo.calendar.monthsInYear",
+  "has options.relativeTo.calendar.weekOfYear",
+  "has options.relativeTo.calendar.year",
+  "has options.relativeTo.calendar.yearMonthFromFields",
+  "has options.relativeTo.calendar.yearOfWeek",
+  "get options.relativeTo.calendar.fields",
+  "call options.relativeTo.calendar.fields",
+  // PrepareTemporalFields
+  "get options.relativeTo.day",
+  "get options.relativeTo.day.valueOf",
+  "call options.relativeTo.day.valueOf",
+  "get options.relativeTo.hour",
+  "get options.relativeTo.microsecond",
+  "get options.relativeTo.millisecond",
+  "get options.relativeTo.minute",
+  "get options.relativeTo.month",
+  "get options.relativeTo.month.valueOf",
+  "call options.relativeTo.month.valueOf",
+  "get options.relativeTo.monthCode",
+  "get options.relativeTo.monthCode.toString",
+  "call options.relativeTo.monthCode.toString",
+  "get options.relativeTo.nanosecond",
+  "get options.relativeTo.offset",
+  "get options.relativeTo.second",
+  "get options.relativeTo.timeZone",
+  "get options.relativeTo.year",
+  "get options.relativeTo.year.valueOf",
+  "call options.relativeTo.year.valueOf",
+  // InterpretTemporalDateTimeFields
+  "get options.relativeTo.calendar.dateFromFields",
+  "call options.relativeTo.calendar.dateFromFields",
+  // AddDuration
+  "get options.relativeTo.calendar.dateUntil",
+  "call options.relativeTo.calendar.dateUntil",
+];
+
+const noCalendarInstance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+  days: 1,
+  hours: 1,
+  minutes: 1,
+  seconds: 1,
+  milliseconds: 1,
+  microseconds: 1,
+  nanoseconds: 1,
+}, "fields");
+
+noCalendarInstance.add(noCalendarFields, createOptionsObserver(plainRelativeTo));
+assert.compareArray(actual, expectedOpsForPlainRelativeToNoCalendarOperations, "order of operations with PlainDate relativeTo and no calendar units");
+actual.splice(0); // clear
+
 const expectedOpsForZonedRelativeTo = expected.concat([
   // ToRelativeTemporalObject
   "get options.relativeTo.calendar",
diff --git a/test/built-ins/Temporal/Duration/prototype/round/order-of-operations.js b/test/built-ins/Temporal/Duration/prototype/round/order-of-operations.js
index 6b09b4ae15..fad6339db9 100644
--- a/test/built-ins/Temporal/Duration/prototype/round/order-of-operations.js
+++ b/test/built-ins/Temporal/Duration/prototype/round/order-of-operations.js
@@ -117,17 +117,38 @@ instance.round(createOptionsObserver({ relativeTo: plainRelativeTo }));
 assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations for PlainDate relativeTo");
 actual.splice(0); // clear
 
+// code path through RoundDuration that rounds to the nearest year, with minimal calendar calls:
+const expectedMinimalOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
+  "get options.relativeTo.calendar.dateAdd",     // 7.c
+  // 7.e and 7.g not called because years, months, weeks are 0
+  "get options.relativeTo.calendar.dateUntil",   // 7.o
+  "call options.relativeTo.calendar.dateUntil",  // 7.o
+  // 7.s not called because years, months, weeks are 0
+  "call options.relativeTo.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);
+instance.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedMinimalOpsForYearRounding, "order of operations with years = 0 and smallestUnit = years");
+actual.splice(0); // clear
+
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
   "get options.relativeTo.calendar.dateAdd",     // 7.c
-  "call options.relativeTo.calendar.dateAdd",    // 7.d
-  "call options.relativeTo.calendar.dateAdd",    // 7.f
-  "get options.relativeTo.calendar.dateUntil",   // 7.n
-  "call options.relativeTo.calendar.dateUntil",  // 7.n
-  "call options.relativeTo.calendar.dateAdd",    // 7.s
-  "call options.relativeTo.calendar.dateAdd",    // 7.x MoveRelativeDate
+  "call options.relativeTo.calendar.dateAdd",    // 7.e
+  "call options.relativeTo.calendar.dateAdd",    // 7.g
+  "get options.relativeTo.calendar.dateUntil",   // 7.o
+  "call options.relativeTo.calendar.dateUntil",  // 7.o
+  "call options.relativeTo.calendar.dateAdd",    // 7.s MoveRelativeDate
+  "call options.relativeTo.calendar.dateAdd",    // 7.y MoveRelativeDate
+  // BalanceDateDurationRelative
+  "get options.relativeTo.calendar.dateAdd",     // 11.a.i
+  "call options.relativeTo.calendar.dateAdd",    // 11.c MoveRelativeDate
+  "call options.relativeTo.calendar.dateAdd",    // 11.g MoveRelativeDate
+  "call options.relativeTo.calendar.dateAdd",    // 11.k
+  "get options.relativeTo.calendar.dateUntil",   // 11.l.i
+  "call options.relativeTo.calendar.dateUntil",  // 11.p
 ]);
-instance.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo }));
+const instanceYears = new Temporal.Duration(1, 12, 0, 0, /* hours = */ 2400);
+instanceYears.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear
 
@@ -349,8 +370,8 @@ instance.round(createOptionsObserver({ relativeTo: zonedRelativeTo }));
 assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations for ZonedDateTime relativeTo");
 actual.splice(0); // clear
 
-// code path through RoundDuration that rounds to the nearest year:
-const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
+// code path through RoundDuration that rounds to the nearest year with minimal calendar operations:
+const expectedOpsForMinimalYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
   // ToTemporalDate
   "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
   "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
@@ -367,15 +388,63 @@ const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
   // NanosecondsToDays → AddDaysToZonedDateTime
   "get options.relativeTo.timeZone.getPossibleInstantsFor",
   "call options.relativeTo.timeZone.getPossibleInstantsFor",
+  // RoundDuration
   "get options.relativeTo.calendar.dateAdd",     // 7.c
-  "call options.relativeTo.calendar.dateAdd",    // 7.d
-  "call options.relativeTo.calendar.dateAdd",    // 7.f
-  "get options.relativeTo.calendar.dateUntil",   // 7.n
-  "call options.relativeTo.calendar.dateUntil",  // 7.n
-  "call options.relativeTo.calendar.dateAdd",    // 7.s
-  "call options.relativeTo.calendar.dateAdd",    // 7.x MoveRelativeDate
+  // 7.e and 7.g not called because years, months, weeks are 0
+  "get options.relativeTo.calendar.dateUntil",   // 7.o
+  "call options.relativeTo.calendar.dateUntil",  // 7.o
+  // 7.s not called because years, months, weeks are 0
+  "call options.relativeTo.calendar.dateAdd",    // 7.y MoveRelativeDate
 ]);
 instance.round(createOptionsObserver({ smallestUnit: "years", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+  actual,
+  expectedOpsForMinimalYearRoundingZoned,
+  "order of operations with years = 0, smallestUnit = years and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
+  // ToTemporalDate
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  // MoveRelativeZonedDateTime → AddZonedDateTime
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "get options.relativeTo.calendar.dateAdd",
+  "call options.relativeTo.calendar.dateAdd",
+  "get options.relativeTo.timeZone.getPossibleInstantsFor",
+  "call options.relativeTo.timeZone.getPossibleInstantsFor",
+  // NanosecondsToDays
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",  // 7. GetPlainDateTimeFor
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",  // 11. GetPlainDateTimeFor
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "get options.relativeTo.calendar.dateUntil",                // 12. DifferenceISODateTime
+  "call options.relativeTo.calendar.dateUntil",
+  // NanosecondsToDays → AddDaysToZonedDateTime
+  "get options.relativeTo.timeZone.getPossibleInstantsFor",
+  "call options.relativeTo.timeZone.getPossibleInstantsFor",
+  // NanosecondsToDays → AddDaysToZonedDateTime
+  "get options.relativeTo.timeZone.getPossibleInstantsFor",
+  "call options.relativeTo.timeZone.getPossibleInstantsFor",
+  "get options.relativeTo.calendar.dateAdd",     // 7.c
+  "call options.relativeTo.calendar.dateAdd",    // 7.e
+  "call options.relativeTo.calendar.dateAdd",    // 7.g
+  "get options.relativeTo.calendar.dateUntil",   // 7.o
+  "call options.relativeTo.calendar.dateUntil",  // 7.o
+  "call options.relativeTo.calendar.dateAdd",    // 7.s MoveRelativeDate
+  "call options.relativeTo.calendar.dateAdd",    // 7.y MoveRelativeDate
+  // BalanceDateDurationRelative
+  "get options.relativeTo.calendar.dateAdd",     // 11.a.i
+  "call options.relativeTo.calendar.dateAdd",    // 11.c MoveRelativeDate
+  "call options.relativeTo.calendar.dateAdd",    // 11.g MoveRelativeDate
+  "call options.relativeTo.calendar.dateAdd",    // 11.k
+  "get options.relativeTo.calendar.dateUntil",   // 11.l.i
+  "call options.relativeTo.calendar.dateUntil",  // 11.p
+]);
+instanceYears.round(createOptionsObserver({ smallestUnit: "years", relativeTo: zonedRelativeTo }));
 assert.compareArray(
   actual,
   expectedOpsForYearRoundingZoned,
diff --git a/test/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js
index 903d06a167..1653736102 100644
--- a/test/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js
+++ b/test/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js
@@ -153,6 +153,103 @@ instance.subtract(fields, createOptionsObserver(plainRelativeTo));
 assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo");
 actual.splice(0); // clear
 
+const expectedOpsForPlainRelativeToNoCalendarOperations = [
+  // ToTemporalDurationRecord
+  "get fields.days",
+  "get fields.days.valueOf",
+  "call fields.days.valueOf",
+  "get fields.hours",
+  "get fields.hours.valueOf",
+  "call fields.hours.valueOf",
+  "get fields.microseconds",
+  "get fields.microseconds.valueOf",
+  "call fields.microseconds.valueOf",
+  "get fields.milliseconds",
+  "get fields.milliseconds.valueOf",
+  "call fields.milliseconds.valueOf",
+  "get fields.minutes",
+  "get fields.minutes.valueOf",
+  "call fields.minutes.valueOf",
+  "get fields.months",
+  "get fields.nanoseconds",
+  "get fields.nanoseconds.valueOf",
+  "call fields.nanoseconds.valueOf",
+  "get fields.seconds",
+  "get fields.seconds.valueOf",
+  "call fields.seconds.valueOf",
+  "get fields.weeks",
+  "get fields.years",
+  // ToRelativeTemporalObject
+  "get options.relativeTo",
+  "get options.relativeTo.calendar",
+  "has options.relativeTo.calendar.dateAdd",
+  "has options.relativeTo.calendar.dateFromFields",
+  "has options.relativeTo.calendar.dateUntil",
+  "has options.relativeTo.calendar.day",
+  "has options.relativeTo.calendar.dayOfWeek",
+  "has options.relativeTo.calendar.dayOfYear",
+  "has options.relativeTo.calendar.daysInMonth",
+  "has options.relativeTo.calendar.daysInWeek",
+  "has options.relativeTo.calendar.daysInYear",
+  "has options.relativeTo.calendar.fields",
+  "has options.relativeTo.calendar.id",
+  "has options.relativeTo.calendar.inLeapYear",
+  "has options.relativeTo.calendar.mergeFields",
+  "has options.relativeTo.calendar.month",
+  "has options.relativeTo.calendar.monthCode",
+  "has options.relativeTo.calendar.monthDayFromFields",
+  "has options.relativeTo.calendar.monthsInYear",
+  "has options.relativeTo.calendar.weekOfYear",
+  "has options.relativeTo.calendar.year",
+  "has options.relativeTo.calendar.yearMonthFromFields",
+  "has options.relativeTo.calendar.yearOfWeek",
+  "get options.relativeTo.calendar.fields",
+  "call options.relativeTo.calendar.fields",
+  // PrepareTemporalFields
+  "get options.relativeTo.day",
+  "get options.relativeTo.day.valueOf",
+  "call options.relativeTo.day.valueOf",
+  "get options.relativeTo.hour",
+  "get options.relativeTo.microsecond",
+  "get options.relativeTo.millisecond",
+  "get options.relativeTo.minute",
+  "get options.relativeTo.month",
+  "get options.relativeTo.month.valueOf",
+  "call options.relativeTo.month.valueOf",
+  "get options.relativeTo.monthCode",
+  "get options.relativeTo.monthCode.toString",
+  "call options.relativeTo.monthCode.toString",
+  "get options.relativeTo.nanosecond",
+  "get options.relativeTo.offset",
+  "get options.relativeTo.second",
+  "get options.relativeTo.timeZone",
+  "get options.relativeTo.year",
+  "get options.relativeTo.year.valueOf",
+  "call options.relativeTo.year.valueOf",
+  // InterpretTemporalDateTimeFields
+  "get options.relativeTo.calendar.dateFromFields",
+  "call options.relativeTo.calendar.dateFromFields",
+  // AddDuration
+  "get options.relativeTo.calendar.dateUntil",
+  "call options.relativeTo.calendar.dateUntil",
+];
+
+const noCalendarInstance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321);
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+  days: 1,
+  hours: 1,
+  minutes: 1,
+  seconds: 1,
+  milliseconds: 1,
+  microseconds: 1,
+  nanoseconds: 1,
+}, "fields");
+
+noCalendarInstance.subtract(noCalendarFields, createOptionsObserver(plainRelativeTo));
+assert.compareArray(actual, expectedOpsForPlainRelativeToNoCalendarOperations, "order of operations with PlainDate relativeTo and no calendar units");
+actual.splice(0); // clear
+
 const expectedOpsForZonedRelativeTo = expected.concat([
   // ToRelativeTemporalObject
   "get options.relativeTo.calendar",
diff --git a/test/built-ins/Temporal/Duration/prototype/total/order-of-operations.js b/test/built-ins/Temporal/Duration/prototype/total/order-of-operations.js
index 81e69c34aa..8ab58638a7 100644
--- a/test/built-ins/Temporal/Duration/prototype/total/order-of-operations.js
+++ b/test/built-ins/Temporal/Duration/prototype/total/order-of-operations.js
@@ -100,6 +100,19 @@ instance.total(createOptionsObserver({ unit: "nanoseconds", relativeTo: plainRel
 assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations for PlainDate relativeTo");
 actual.splice(0); // clear
 
+// code path through RoundDuration that rounds to the nearest year with minimal calendar calls:
+const expectedOpsForMinimalYearRounding = expectedOpsForPlainRelativeTo.concat([
+  "get options.relativeTo.calendar.dateAdd",     // 7.c
+  // 7.e and 7.g not called because years, months, weeks are 0
+  "get options.relativeTo.calendar.dateUntil",   // 7.o
+  "call options.relativeTo.calendar.dateUntil",  // 7.o
+  // 7.s not called because years, months, weeks are 0
+  "call options.relativeTo.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);
+instance.total(createOptionsObserver({ unit: "years", relativeTo: plainRelativeTo }));
+assert.compareArray(actual, expectedOpsForMinimalYearRounding, "order of operations with years = 0 and unit = years");
+actual.splice(0); // clear
+
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
   "get options.relativeTo.calendar.dateAdd",     // 7.c
@@ -110,7 +123,8 @@ const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([
   "call options.relativeTo.calendar.dateAdd",    // 7.s
   "call options.relativeTo.calendar.dateAdd",    // 7.x MoveRelativeDate
 ]);
-instance.total(createOptionsObserver({ unit: "years", relativeTo: plainRelativeTo }));
+const instanceYears = new Temporal.Duration(1, 12, 0, 0, /* hours = */ 2400);
+instanceYears.total(createOptionsObserver({ unit: "years", relativeTo: plainRelativeTo }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with unit = years");
 actual.splice(0); // clear
 
@@ -257,7 +271,8 @@ instance.total(createOptionsObserver({ unit: "nanoseconds", relativeTo: zonedRel
 assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations for ZonedDateTime relativeTo");
 actual.splice(0); // clear
 
-const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
+// code path through RoundDuration that rounds to the nearest year with minimal calendar operations:
+const expectedOpsForMinimalYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
   // ToTemporalDate
   "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
   "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
@@ -282,14 +297,62 @@ const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
   "get options.relativeTo.timeZone.getPossibleInstantsFor",
   "call options.relativeTo.timeZone.getPossibleInstantsFor",
   "get options.relativeTo.calendar.dateAdd",     // 7.c
-  "call options.relativeTo.calendar.dateAdd",    // 7.d
-  "call options.relativeTo.calendar.dateAdd",    // 7.f
-  "get options.relativeTo.calendar.dateUntil",   // 7.n
-  "call options.relativeTo.calendar.dateUntil",  // 7.n
-  "call options.relativeTo.calendar.dateAdd",    // 7.s
-  "call options.relativeTo.calendar.dateAdd",    // 7.x MoveRelativeDate
+  // 7.e and 7.g not called because years, months, weeks are 0
+  "get options.relativeTo.calendar.dateUntil",   // 7.o
+  "call options.relativeTo.calendar.dateUntil",  // 7.o
+  // 7.s not called because years, months, weeks are 0
+  "call options.relativeTo.calendar.dateAdd",    // 7.y MoveRelativeDate
 ]);
 instance.total(createOptionsObserver({ unit: "years", relativeTo: zonedRelativeTo }));
+assert.compareArray(
+  actual,
+  expectedOpsForMinimalYearRoundingZoned,
+  "order of operations with years = 0, unit = years and ZonedDateTime relativeTo"
+);
+actual.splice(0); // clear
+
+// code path through RoundDuration that rounds to the nearest year:
+const expectedOpsForYearRoundingZoned = expectedOpsForZonedRelativeTo.concat([
+  // ToTemporalDate
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  // MoveRelativeZonedDateTime → AddZonedDateTime
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "get options.relativeTo.calendar.dateAdd",
+  "call options.relativeTo.calendar.dateAdd",
+  "get options.relativeTo.timeZone.getPossibleInstantsFor",
+  "call options.relativeTo.timeZone.getPossibleInstantsFor",
+  // BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",  // 7. GetPlainDateTimeFor
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",  // 11. GetPlainDateTimeFor
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "get options.relativeTo.calendar.dateUntil",                // 12. DifferenceISODateTime
+  "call options.relativeTo.calendar.dateUntil",
+  // BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays → AddDaysToZonedDateTime
+  "get options.relativeTo.timeZone.getPossibleInstantsFor",
+  "call options.relativeTo.timeZone.getPossibleInstantsFor",
+  // BalancePossiblyInfiniteTimeDurationRelative → NanosecondsToDays → AddDaysToZonedDateTime
+  "get options.relativeTo.timeZone.getPossibleInstantsFor",
+  "call options.relativeTo.timeZone.getPossibleInstantsFor",
+  // RoundDuration → MoveRelativeZonedDateTime → AddZonedDateTime
+  "get options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "call options.relativeTo.timeZone.getOffsetNanosecondsFor",
+  "get options.relativeTo.calendar.dateAdd",
+  "call options.relativeTo.calendar.dateAdd",
+  "get options.relativeTo.timeZone.getPossibleInstantsFor",
+  "call options.relativeTo.timeZone.getPossibleInstantsFor",
+  // RoundDuration
+  "get options.relativeTo.calendar.dateAdd",     // 7.c
+  "call options.relativeTo.calendar.dateAdd",    // 7.e
+  "call options.relativeTo.calendar.dateAdd",    // 7.g
+  "get options.relativeTo.calendar.dateUntil",   // 7.o
+  "call options.relativeTo.calendar.dateUntil",  // 7.o
+  "call options.relativeTo.calendar.dateAdd",    // 7.s MoveRelativeDate
+  "call options.relativeTo.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);
+instanceYears.total(createOptionsObserver({ unit: "years", relativeTo: zonedRelativeTo }));
 assert.compareArray(
   actual,
   expectedOpsForYearRoundingZoned,
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js b/test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
index b0c6bd878a..8ce7dac1e4 100644
--- a/test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/balance-smaller-units.js
@@ -8,44 +8,18 @@ includes: [temporalHelpers.js]
 features: [Temporal]
 ---*/
 
-const actual = [];
-
-class DateAddCalendar extends Temporal.Calendar {
-  constructor() {
-    super("iso8601");
-  }
-
-  dateAdd(date, duration, options) {
-    actual.push(duration);
-    return super.dateAdd(date, duration, options);
-  }
-}
-
-const calendar = new DateAddCalendar();
-const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const date = new Temporal.PlainDate(2000, 5, 2);
 const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
 
 const result = date.add(duration);
 TemporalHelpers.assertPlainDate(result, 2000, 5, "M05", 9, "units smaller than days are balanced");
 
-assert.sameValue(actual.length, 1, "calendar.dateAdd called exactly once");
-assert.sameValue(actual[0], duration, "the duration is passed directly to the calendar");
-
 const resultString = date.add("P1DT24H1440M86400S");
 TemporalHelpers.assertPlainDate(resultString, 2000, 5, "M05", 6, "units smaller than days are balanced");
 
-assert.sameValue(actual.length, 2, "calendar.dateAdd called exactly once");
-TemporalHelpers.assertDuration(actual[1], 0, 0, 0, 1, 24, 1440, 86400, 0, 0, 0, "the duration is not balanced before passing to the calendar");
-
 const resultPropBag = date.add({ days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
 TemporalHelpers.assertPlainDate(resultPropBag, 2000, 5, "M05", 9, "units smaller than days are balanced");
 
-assert.sameValue(actual.length, 3, "calendar.dateAdd called exactly once");
-TemporalHelpers.assertDuration(actual[2], 0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000, "the duration is not balanced before passing to the calendar");
-
 const negativeDuration = new Temporal.Duration(0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000);
 const resultNegative = date.add(negativeDuration);
 TemporalHelpers.assertPlainDate(resultNegative, 2000, 4, "M04", 25, "units smaller than days are balanced");
-
-assert.sameValue(actual.length, 4, "calendar.dateAdd called exactly once");
-assert.sameValue(actual[3], negativeDuration, "the duration is passed directly to the calendar");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js b/test/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
index 37622353d7..137fa40103 100644
--- a/test/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainDate/prototype/add/order-of-operations.js
@@ -9,7 +9,7 @@ features: [Temporal]
 ---*/
 
 const expected = [
-  // ToTemporalDuration
+  // ToTemporalDurationRecord
   "get fields.days",
   "get fields.days.valueOf",
   "call fields.days.valueOf",
@@ -40,7 +40,7 @@ const expected = [
   "get fields.years",
   "get fields.years.valueOf",
   "call fields.years.valueOf",
-  // CalendarDateAdd
+  // AddDate
   "get this.calendar.dateAdd",
   "call this.calendar.dateAdd",
   // inside Calendar.p.dateAdd
@@ -74,3 +74,52 @@ const options = TemporalHelpers.propertyBagObserver(actual, {
 
 instance.add(fields, options);
 assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+  // ToTemporalDurationRecord
+  "get fields.days",
+  "get fields.days.valueOf",
+  "call fields.days.valueOf",
+  "get fields.hours",
+  "get fields.hours.valueOf",
+  "call fields.hours.valueOf",
+  "get fields.microseconds",
+  "get fields.microseconds.valueOf",
+  "call fields.microseconds.valueOf",
+  "get fields.milliseconds",
+  "get fields.milliseconds.valueOf",
+  "call fields.milliseconds.valueOf",
+  "get fields.minutes",
+  "get fields.minutes.valueOf",
+  "call fields.minutes.valueOf",
+  "get fields.months",
+  "get fields.nanoseconds",
+  "get fields.nanoseconds.valueOf",
+  "call fields.nanoseconds.valueOf",
+  "get fields.seconds",
+  "get fields.seconds.valueOf",
+  "call fields.seconds.valueOf",
+  "get fields.weeks",
+  "get fields.years",
+  // AddDate
+  "get options.overflow",
+  "get options.overflow.toString",
+  "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+  days: 1,
+  hours: 1,
+  minutes: 1,
+  seconds: 1,
+  milliseconds: 1,
+  microseconds: 1,
+  nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js b/test/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js
index 19753ea868..2900169cdc 100644
--- a/test/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainDate/prototype/since/order-of-operations.js
@@ -111,14 +111,13 @@ actual.splice(0); // clear
 
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expected.concat([
-  "get this.calendar.dateAdd",     // 7.c
-  "call this.calendar.dateAdd",    // 7.d
-  "call this.calendar.dateAdd",    // 7.f
-  "get this.calendar.dateUntil",   // 7.n
-  "call this.calendar.dateUntil",  // 7.n
-  "call this.calendar.dateAdd",    // 7.s
-  "call this.calendar.dateAdd",    // 7.x MoveRelativeDate
-]);
+  "get this.calendar.dateAdd",     // 7.c.i
+  "call this.calendar.dateAdd",    // 7.e
+  "call this.calendar.dateAdd",    // 7.g
+  "get this.calendar.dateUntil",   // 7.o
+  "call this.calendar.dateUntil",  // 7.o
+  "call this.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);  // (7.s not called because other units can't add up to >1 year at this point)
 instance.since(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
index aa49cb1cd6..2e530ddd18 100644
--- a/test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/balance-smaller-units.js
@@ -8,46 +8,18 @@ includes: [temporalHelpers.js]
 features: [Temporal]
 ---*/
 
-const actual = [];
-
-class DateAddCalendar extends Temporal.Calendar {
-  constructor() {
-    super("iso8601");
-  }
-
-  dateAdd(date, duration, options) {
-    actual.push(duration);
-    return super.dateAdd(date, duration, options);
-  }
-}
-
-const calendar = new DateAddCalendar();
-const date = new Temporal.PlainDate(2000, 5, 2, calendar);
+const date = new Temporal.PlainDate(2000, 5, 2);
 const duration = new Temporal.Duration(0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000);
 
 const result = date.subtract(duration);
 TemporalHelpers.assertPlainDate(result, 2000, 4, "M04", 25, "units smaller than days are balanced");
 
-assert.sameValue(actual.length, 1, "calendar.dateAdd called exactly once");
-TemporalHelpers.assertDuration(actual[0], 0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000, "the duration is negated but not balanced before passing to the calendar");
-assert.notSameValue(actual[0], duration, "the duration is not passed directly to the calendar");
-
 const resultString = date.subtract("P1DT24H1440M86400S");
 TemporalHelpers.assertPlainDate(resultString, 2000, 4, "M04", 28, "units smaller than days are balanced");
 
-assert.sameValue(actual.length, 2, "calendar.dateAdd called exactly once");
-TemporalHelpers.assertDuration(actual[1], 0, 0, 0, -1, -24, -1440, -86400, 0, 0, 0, "the duration is negated but not balanced before passing to the calendar");
-
 const resultPropBag = date.subtract({ days: 1, hours: 24, minutes: 1440, seconds: 86400, milliseconds: 86400_000, microseconds: 86400_000_000, nanoseconds: 86400_000_000_000 });
 TemporalHelpers.assertPlainDate(resultPropBag, 2000, 4, "M04", 25, "units smaller than days are balanced");
 
-assert.sameValue(actual.length, 3, "calendar.dateAdd called exactly once");
-TemporalHelpers.assertDuration(actual[2], 0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000, "the duration is not balanced before passing to the calendar");
-
 const negativeDuration = new Temporal.Duration(0, 0, 0, -1, -24, -1440, -86400, -86400_000, -86400_000_000, -86400_000_000_000);
 const resultNegative = date.subtract(negativeDuration);
 TemporalHelpers.assertPlainDate(resultNegative, 2000, 5, "M05", 9, "units smaller than days are balanced");
-
-assert.sameValue(actual.length, 4, "calendar.dateAdd called exactly once");
-TemporalHelpers.assertDuration(actual[3], 0, 0, 0, 1, 24, 1440, 86400, 86400_000, 86400_000_000, 86400_000_000_000, "the duration is negated but not balanced before passing to the calendar");
-assert.notSameValue(actual[3], negativeDuration, "the duration is not passed directly to the calendar");
diff --git a/test/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
index a4ecc2774e..cdf0f2d089 100644
--- a/test/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainDate/prototype/subtract/order-of-operations.js
@@ -9,7 +9,7 @@ features: [Temporal]
 ---*/
 
 const expected = [
-  // ToTemporalDuration
+  // ToTemporalDurationRecord
   "get fields.days",
   "get fields.days.valueOf",
   "call fields.days.valueOf",
@@ -40,7 +40,7 @@ const expected = [
   "get fields.years",
   "get fields.years.valueOf",
   "call fields.years.valueOf",
-  // CalendarDateAdd
+  // AddDate
   "get this.calendar.dateAdd",
   "call this.calendar.dateAdd",
   // inside Calendar.p.dateAdd
@@ -74,3 +74,52 @@ const options = TemporalHelpers.propertyBagObserver(actual, {
 
 instance.subtract(fields, options);
 assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+  // ToTemporalDurationRecord
+  "get fields.days",
+  "get fields.days.valueOf",
+  "call fields.days.valueOf",
+  "get fields.hours",
+  "get fields.hours.valueOf",
+  "call fields.hours.valueOf",
+  "get fields.microseconds",
+  "get fields.microseconds.valueOf",
+  "call fields.microseconds.valueOf",
+  "get fields.milliseconds",
+  "get fields.milliseconds.valueOf",
+  "call fields.milliseconds.valueOf",
+  "get fields.minutes",
+  "get fields.minutes.valueOf",
+  "call fields.minutes.valueOf",
+  "get fields.months",
+  "get fields.nanoseconds",
+  "get fields.nanoseconds.valueOf",
+  "call fields.nanoseconds.valueOf",
+  "get fields.seconds",
+  "get fields.seconds.valueOf",
+  "call fields.seconds.valueOf",
+  "get fields.weeks",
+  "get fields.years",
+  // AddDate
+  "get options.overflow",
+  "get options.overflow.toString",
+  "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+  days: 1,
+  hours: 1,
+  minutes: 1,
+  seconds: 1,
+  milliseconds: 1,
+  microseconds: 1,
+  nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js b/test/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js
index 92b61a07d3..c87bbf931d 100644
--- a/test/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainDate/prototype/until/order-of-operations.js
@@ -111,14 +111,13 @@ actual.splice(0); // clear
 
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expected.concat([
-  "get this.calendar.dateAdd",     // 7.c
-  "call this.calendar.dateAdd",    // 7.d
-  "call this.calendar.dateAdd",    // 7.f
-  "get this.calendar.dateUntil",   // 7.n
-  "call this.calendar.dateUntil",  // 7.n
-  "call this.calendar.dateAdd",    // 7.s
-  "call this.calendar.dateAdd",    // 7.x MoveRelativeDate
-]);
+  "get this.calendar.dateAdd",     // 7.c.i
+  "call this.calendar.dateAdd",    // 7.e
+  "call this.calendar.dateAdd",    // 7.g
+  "get this.calendar.dateUntil",   // 7.o
+  "call this.calendar.dateUntil",  // 7.o
+  "call this.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);  // (7.s not called because other units can't add up to >1 year at this point)
 instance.until(otherDatePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
index e92472a1d6..33cff70280 100644
--- a/test/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/add/order-of-operations.js
@@ -40,7 +40,7 @@ const expected = [
   "get fields.years",
   "get fields.years.valueOf",
   "call fields.years.valueOf",
-  // AddDateTime
+  // AddDateTime -> AddDate
   "get this.calendar.dateAdd",
   "call this.calendar.dateAdd",
   // inside Calendar.p.dateAdd
@@ -72,3 +72,52 @@ const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constra
 
 instance.add(fields, options);
 assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+  // ToTemporalDurationRecord
+  "get fields.days",
+  "get fields.days.valueOf",
+  "call fields.days.valueOf",
+  "get fields.hours",
+  "get fields.hours.valueOf",
+  "call fields.hours.valueOf",
+  "get fields.microseconds",
+  "get fields.microseconds.valueOf",
+  "call fields.microseconds.valueOf",
+  "get fields.milliseconds",
+  "get fields.milliseconds.valueOf",
+  "call fields.milliseconds.valueOf",
+  "get fields.minutes",
+  "get fields.minutes.valueOf",
+  "call fields.minutes.valueOf",
+  "get fields.months",
+  "get fields.nanoseconds",
+  "get fields.nanoseconds.valueOf",
+  "call fields.nanoseconds.valueOf",
+  "get fields.seconds",
+  "get fields.seconds.valueOf",
+  "call fields.seconds.valueOf",
+  "get fields.weeks",
+  "get fields.years",
+  // AddDateTime -> AddDate
+  "get options.overflow",
+  "get options.overflow.toString",
+  "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+  days: 1,
+  hours: 1,
+  minutes: 1,
+  seconds: 1,
+  milliseconds: 1,
+  microseconds: 1,
+  nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js
index f7c54c7e77..bdb43ae21e 100644
--- a/test/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/since/order-of-operations.js
@@ -101,8 +101,8 @@ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 32
 
 const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
   year: 2001,
-  month: 5,
-  monthCode: "M05",
+  month: 6,
+  monthCode: "M06",
   day: 2,
   hour: 1,
   minute: 46,
@@ -135,14 +135,13 @@ actual.splice(0); // clear
 
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expected.concat([
-  "get this.calendar.dateAdd",     // 7.c
-  "call this.calendar.dateAdd",    // 7.d
-  "call this.calendar.dateAdd",    // 7.f
-  "get this.calendar.dateUntil",   // 7.n
-  "call this.calendar.dateUntil",  // 7.n
-  "call this.calendar.dateAdd",    // 7.s
-  "call this.calendar.dateAdd",    // 7.x MoveRelativeDate
-]);
+  "get this.calendar.dateAdd",     // 7.c.i
+  "call this.calendar.dateAdd",    // 7.e
+  "call this.calendar.dateAdd",    // 7.g
+  "get this.calendar.dateUntil",   // 7.o
+  "call this.calendar.dateUntil",  // 7.o
+  "call this.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);  // (7.s not called because other units can't add up to >1 year at this point)
 instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
index d28bfcd743..42a149df20 100644
--- a/test/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/subtract/order-of-operations.js
@@ -40,7 +40,7 @@ const expected = [
   "get fields.years",
   "get fields.years.valueOf",
   "call fields.years.valueOf",
-  // AddDateTime
+  // AddDateTime -> AddDate
   "get this.calendar.dateAdd",
   "call this.calendar.dateAdd",
   // inside Calendar.p.dateAdd
@@ -72,3 +72,52 @@ const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constra
 
 instance.subtract(fields, options);
 assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+  // ToTemporalDurationRecord
+  "get fields.days",
+  "get fields.days.valueOf",
+  "call fields.days.valueOf",
+  "get fields.hours",
+  "get fields.hours.valueOf",
+  "call fields.hours.valueOf",
+  "get fields.microseconds",
+  "get fields.microseconds.valueOf",
+  "call fields.microseconds.valueOf",
+  "get fields.milliseconds",
+  "get fields.milliseconds.valueOf",
+  "call fields.milliseconds.valueOf",
+  "get fields.minutes",
+  "get fields.minutes.valueOf",
+  "call fields.minutes.valueOf",
+  "get fields.months",
+  "get fields.nanoseconds",
+  "get fields.nanoseconds.valueOf",
+  "call fields.nanoseconds.valueOf",
+  "get fields.seconds",
+  "get fields.seconds.valueOf",
+  "call fields.seconds.valueOf",
+  "get fields.weeks",
+  "get fields.years",
+  // AddDateTime -> AddDate
+  "get options.overflow",
+  "get options.overflow.toString",
+  "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+  days: 1,
+  hours: 1,
+  minutes: 1,
+  seconds: 1,
+  milliseconds: 1,
+  microseconds: 1,
+  nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar operation");
+
+actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js b/test/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js
index cf681eaea9..9a45033ccc 100644
--- a/test/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainDateTime/prototype/until/order-of-operations.js
@@ -101,8 +101,8 @@ const instance = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 32
 
 const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
   year: 2001,
-  month: 5,
-  monthCode: "M05",
+  month: 6,
+  monthCode: "M06",
   day: 2,
   hour: 1,
   minute: 46,
@@ -135,14 +135,13 @@ actual.splice(0); // clear
 
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expected.concat([
-  "get this.calendar.dateAdd",     // 7.c
-  "call this.calendar.dateAdd",    // 7.d
-  "call this.calendar.dateAdd",    // 7.f
-  "get this.calendar.dateUntil",   // 7.n
-  "call this.calendar.dateUntil",  // 7.n
-  "call this.calendar.dateAdd",    // 7.s
-  "call this.calendar.dateAdd",    // 7.x MoveRelativeDate
-]);
+  "get this.calendar.dateAdd",     // 7.c.i
+  "call this.calendar.dateAdd",    // 7.e
+  "call this.calendar.dateAdd",    // 7.g
+  "get this.calendar.dateUntil",   // 7.o
+  "call this.calendar.dateUntil",  // 7.o
+  "call this.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);  // (7.s not called because other units can't add up to >1 year at this point)
 instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js
index 288ce158d4..036c9b0634 100644
--- a/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-dateadd-called-with-plaindate-instance.js
@@ -10,9 +10,9 @@ features: [Temporal]
 
 const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
 const instance = new Temporal.PlainYearMonth(1983, 3, calendar);
-TemporalHelpers.assertPlainYearMonth(instance.add({days: 31}), 1983, 4, 'M04', "Adding 31 days to march in is8601 calendar")
+TemporalHelpers.assertPlainYearMonth(instance.add({weeks: 5}), 1983, 4, 'M04', "Adding 5 weeks to March in is8601 calendar")
 assert.sameValue(calendar.dateAddCallCount, 1, "dateAdd called once with positive add");
 
 calendar.dateAddCallCount = 0;
-TemporalHelpers.assertPlainYearMonth(instance.add({days: -31}), 1983, 2, 'M02', "Adding -31 days to march in is8601 calendar")
+TemporalHelpers.assertPlainYearMonth(instance.add({weeks: -5}), 1983, 2, 'M02', "Adding -5 weeks to March in is8601 calendar")
 assert.sameValue(calendar.dateAddCallCount, 2, "dateAdd called 2 times with negative add");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js b/test/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
index cdb9871958..09310b094c 100644
--- a/test/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/add/order-of-operations.js
@@ -98,3 +98,79 @@ const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constra
 
 instance.add(fields, options);
 assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+  // ToTemporalDuration
+  "get fields.days",
+  "get fields.days.valueOf",
+  "call fields.days.valueOf",
+  "get fields.hours",
+  "get fields.hours.valueOf",
+  "call fields.hours.valueOf",
+  "get fields.microseconds",
+  "get fields.microseconds.valueOf",
+  "call fields.microseconds.valueOf",
+  "get fields.milliseconds",
+  "get fields.milliseconds.valueOf",
+  "call fields.milliseconds.valueOf",
+  "get fields.minutes",
+  "get fields.minutes.valueOf",
+  "call fields.minutes.valueOf",
+  "get fields.months",
+  "get fields.nanoseconds",
+  "get fields.nanoseconds.valueOf",
+  "call fields.nanoseconds.valueOf",
+  "get fields.seconds",
+  "get fields.seconds.valueOf",
+  "call fields.seconds.valueOf",
+  "get fields.weeks",
+  "get fields.years",
+  // CalendarFields
+  "get this.calendar.fields",
+  "call this.calendar.fields",
+  // PrepareTemporalFields on receiver
+  "get this.calendar.monthCode",
+  "call this.calendar.monthCode",
+  "get this.calendar.year",
+  "call this.calendar.year",
+  // CalendarDateFromFields
+  "get this.calendar.dateFromFields",
+  "call this.calendar.dateFromFields",
+  "get this.calendar.dateAdd",
+  // SnapshotOwnProperties
+  "ownKeys options",
+  "getOwnPropertyDescriptor options.overflow",
+  "get options.overflow",
+  // AddDate
+  "get options.overflow",
+  "get options.overflow.toString",
+  "call options.overflow.toString",
+  // PrepareTemporalFields on added date
+  "get this.calendar.monthCode",
+  "call this.calendar.monthCode",
+  "get this.calendar.year",
+  "call this.calendar.year",
+  // CalendarYearMonthFromFields
+  "get this.calendar.yearMonthFromFields",
+  "call this.calendar.yearMonthFromFields",
+  // inside Calendar.p.yearMonthFromFields
+  "get options.overflow.toString",
+  "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+  days: 1,
+  hours: 1,
+  minutes: 1,
+  seconds: 1,
+  milliseconds: 1,
+  microseconds: 1,
+  nanoseconds: 1,
+}, "fields");
+
+instance.add(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar units");
+
+actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js b/test/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js
index f870702b96..838b5099fd 100644
--- a/test/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/since/order-of-operations.js
@@ -124,14 +124,13 @@ actual.splice(0); // clear
 
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expected.concat([
-  "get this.calendar.dateAdd",     // 7.c
-  "call this.calendar.dateAdd",    // 7.d
-  "call this.calendar.dateAdd",    // 7.f
-  "get this.calendar.dateUntil",   // 7.n
-  "call this.calendar.dateUntil",  // 7.n
-  "call this.calendar.dateAdd",    // 7.s
-  "call this.calendar.dateAdd",    // 7.x MoveRelativeDate
-]);
+  "get this.calendar.dateAdd",     // 7.c.i
+  "call this.calendar.dateAdd",    // 7.e
+  "call this.calendar.dateAdd",    // 7.g
+  "get this.calendar.dateUntil",   // 7.o
+  "call this.calendar.dateUntil",  // 7.o
+  "call this.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);  // (7.s not called because other units can't add up to >1 year at this point)
 instance.since(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "years" }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js
index 8d0fe87863..6ebde0028e 100644
--- a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js
@@ -10,9 +10,9 @@ features: [Temporal]
 
 const calendar = TemporalHelpers.calendarDateAddPlainDateInstance();
 const instance = new Temporal.PlainYearMonth(1983, 3, calendar);
-TemporalHelpers.assertPlainYearMonth(instance.subtract({days: 31}), 1983, 2, 'M02', "Removing 31 days to march in is8601 calendar")
+TemporalHelpers.assertPlainYearMonth(instance.subtract({weeks: 5}), 1983, 2, 'M02', "Removing 5 weeks from March in is8601 calendar")
 assert.sameValue(calendar.dateAddCallCount, 2, "dateAdd called 2 times with positive subtract");
 
 calendar.dateAddCallCount = 0;
-TemporalHelpers.assertPlainYearMonth(instance.subtract({days: -31}), 1983, 4, 'M04', "Removing -31 days to march in is8601 calendar")
+TemporalHelpers.assertPlainYearMonth(instance.subtract({weeks: -5}), 1983, 4, 'M04', "Removing -5 weeks from March in is8601 calendar")
 assert.sameValue(calendar.dateAddCallCount, 1, "dateAdd called once with negative subtract");
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
index 18f4650af2..b79f6c9129 100644
--- a/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/subtract/order-of-operations.js
@@ -103,3 +103,84 @@ const options = TemporalHelpers.propertyBagObserver(actual, { overflow: "constra
 
 instance.subtract(fields, options);
 assert.compareArray(actual, expected, "order of operations");
+
+actual.splice(0); // clear
+
+const noCalendarExpected = [
+  // ToTemporalDuration
+  "get fields.days",
+  "get fields.days.valueOf",
+  "call fields.days.valueOf",
+  "get fields.hours",
+  "get fields.hours.valueOf",
+  "call fields.hours.valueOf",
+  "get fields.microseconds",
+  "get fields.microseconds.valueOf",
+  "call fields.microseconds.valueOf",
+  "get fields.milliseconds",
+  "get fields.milliseconds.valueOf",
+  "call fields.milliseconds.valueOf",
+  "get fields.minutes",
+  "get fields.minutes.valueOf",
+  "call fields.minutes.valueOf",
+  "get fields.months",
+  "get fields.nanoseconds",
+  "get fields.nanoseconds.valueOf",
+  "call fields.nanoseconds.valueOf",
+  "get fields.seconds",
+  "get fields.seconds.valueOf",
+  "call fields.seconds.valueOf",
+  "get fields.weeks",
+  "get fields.years",
+  // CalendarFields
+  "get this.calendar.fields",
+  "call this.calendar.fields",
+  // PrepareTemporalFields on receiver
+  "get this.calendar.monthCode",
+  "call this.calendar.monthCode",
+  "get this.calendar.year",
+  "call this.calendar.year",
+  // CalendarDateFromFields
+  "get this.calendar.dateFromFields",
+  "call this.calendar.dateFromFields",
+  "get this.calendar.dateAdd",
+  "call this.calendar.dateAdd",
+  "get this.calendar.day",
+  "call this.calendar.day",
+  "get this.calendar.dateFromFields",
+  "call this.calendar.dateFromFields",
+  // SnapshotOwnProperties
+  "ownKeys options",
+  "getOwnPropertyDescriptor options.overflow",
+  "get options.overflow",
+  // AddDate
+  "get options.overflow",
+  "get options.overflow.toString",
+  "call options.overflow.toString",
+  // PrepareTemporalFields on added date
+  "get this.calendar.monthCode",
+  "call this.calendar.monthCode",
+  "get this.calendar.year",
+  "call this.calendar.year",
+  // CalendarYearMonthFromFields
+  "get this.calendar.yearMonthFromFields",
+  "call this.calendar.yearMonthFromFields",
+  // inside Calendar.p.yearMonthFromFields
+  "get options.overflow.toString",
+  "call options.overflow.toString",
+];
+
+const noCalendarFields = TemporalHelpers.propertyBagObserver(actual, {
+  days: 1,
+  hours: 1,
+  minutes: 1,
+  seconds: 1,
+  milliseconds: 1,
+  microseconds: 1,
+  nanoseconds: 1,
+}, "fields");
+
+instance.subtract(noCalendarFields, options);
+assert.compareArray(actual, noCalendarExpected, "order of operations with no calendar units");
+
+actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js b/test/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js
index fb9046c254..ec45a31e3d 100644
--- a/test/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js
+++ b/test/built-ins/Temporal/PlainYearMonth/prototype/until/order-of-operations.js
@@ -124,14 +124,13 @@ actual.splice(0); // clear
 
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expected.concat([
-  "get this.calendar.dateAdd",     // 7.c
-  "call this.calendar.dateAdd",    // 7.d
-  "call this.calendar.dateAdd",    // 7.f
-  "get this.calendar.dateUntil",   // 7.n
-  "call this.calendar.dateUntil",  // 7.n
-  "call this.calendar.dateAdd",    // 7.s
-  "call this.calendar.dateAdd",    // 7.x MoveRelativeDate
-]);
+  "get this.calendar.dateAdd",     // 7.c.i
+  "call this.calendar.dateAdd",    // 7.e
+  "call this.calendar.dateAdd",    // 7.g
+  "get this.calendar.dateUntil",   // 7.o
+  "call this.calendar.dateUntil",  // 7.o
+  "call this.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);  // (7.s not called because other units can't add up to >1 year at this point)
 instance.until(otherYearMonthPropertyBag, createOptionsObserver({ smallestUnit: "years" }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js
index 666e26b787..fad648fc76 100644
--- a/test/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/since/order-of-operations.js
@@ -109,10 +109,10 @@ const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
 const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, ownTimeZone, ownCalendar);
 
 const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
-  year: 2001,
+  year: 2004,
   month: 5,
   monthCode: "M05",
-  day: 2,
+  day: 12,
   hour: 1,
   minute: 46,
   second: 40,
@@ -208,14 +208,13 @@ actual.splice(0); // clear
 
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
-  "get this.calendar.dateAdd",     // 7.c
-  "call this.calendar.dateAdd",    // 7.d
-  "call this.calendar.dateAdd",    // 7.f
-  "get this.calendar.dateUntil",   // 7.n
-  "call this.calendar.dateUntil",  // 7.n
-  "call this.calendar.dateAdd",    // 7.s
-  "call this.calendar.dateAdd",    // 7.x MoveRelativeDate
-]);
+  "get this.calendar.dateAdd",     // 7.c.i
+  "call this.calendar.dateAdd",    // 7.e
+  "call this.calendar.dateAdd",    // 7.g
+  "get this.calendar.dateUntil",   // 7.o
+  "call this.calendar.dateUntil",  // 7.o
+  "call this.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);  // (7.s not called because other units can't add up to >1 year at this point)
 instance.since(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear
diff --git a/test/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js b/test/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js
index 71dec65c3c..bafa5329e5 100644
--- a/test/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js
+++ b/test/built-ins/Temporal/ZonedDateTime/prototype/until/order-of-operations.js
@@ -109,10 +109,10 @@ const ownCalendar = TemporalHelpers.calendarObserver(actual, "this.calendar");
 const instance = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, ownTimeZone, ownCalendar);
 
 const otherDateTimePropertyBag = TemporalHelpers.propertyBagObserver(actual, {
-  year: 2001,
+  year: 2004,
   month: 5,
   monthCode: "M05",
-  day: 2,
+  day: 12,
   hour: 1,
   minute: 46,
   second: 40,
@@ -208,14 +208,13 @@ actual.splice(0); // clear
 
 // code path through RoundDuration that rounds to the nearest year:
 const expectedOpsForYearRounding = expected.concat(expectedOpsForCalendarDifference, expectedOpsForCalendarRounding, [
-  "get this.calendar.dateAdd",     // 7.c
-  "call this.calendar.dateAdd",    // 7.d
-  "call this.calendar.dateAdd",    // 7.f
-  "get this.calendar.dateUntil",   // 7.n
-  "call this.calendar.dateUntil",  // 7.n
-  "call this.calendar.dateAdd",    // 7.s
-  "call this.calendar.dateAdd",    // 7.x MoveRelativeDate
-]);
+  "get this.calendar.dateAdd",     // 7.c.i
+  "call this.calendar.dateAdd",    // 7.e
+  "call this.calendar.dateAdd",    // 7.g
+  "get this.calendar.dateUntil",   // 7.o
+  "call this.calendar.dateUntil",  // 7.o
+  "call this.calendar.dateAdd",    // 7.y MoveRelativeDate
+]);  // (7.s not called because other units can't add up to >1 year at this point)
 instance.until(otherDateTimePropertyBag, createOptionsObserver({ smallestUnit: "years" }));
 assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years");
 actual.splice(0); // clear