forked from tc39/test262
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tests for computing PlainYearMonth addition and subtraction in correc…
…t calendar space tc39/proposal-temporal#2003 is a normative change that reached consensus at the March 2022 TC39 plenary meeting. This adds tests that verify the new spec text is implemented correctly, performing arithmetic on a PlainYearMonth instance that would previously have thrown an error if it was implemented as written.
- Loading branch information
Showing
2 changed files
with
294 additions
and
0 deletions.
There are no files selected for viewing
147 changes: 147 additions & 0 deletions
147
test/built-ins/Temporal/PlainYearMonth/prototype/add/calendar-datefromfields-called.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// Copyright (C) 2022 Igalia, S.L. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
|
||
/*--- | ||
esid: sec-temporal.plainyearmonth.prototype.add | ||
description: > | ||
Calls calendar's dateFromFields method to obtain a start date for the | ||
operation, based on the sign of the duration | ||
info: | | ||
8. Let _fields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»). | ||
9. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0). | ||
10. If _sign_ < 0, then | ||
a. Let _dayFromCalendar_ be ? CalendarDaysInMonth(_calendar_, _yearMonth_). | ||
b. Let _day_ be ? ToPositiveInteger(_dayFromCalendar_). | ||
11. Else, | ||
a. Let _day_ be 1. | ||
12. Perform ! CreateDataPropertyOrThrow(_fields_, *"day"*, _day_). | ||
13. Let _date_ be ? DateFromFields(_calendar_, _fields_, *undefined*). | ||
includes: [deepEqual.js, temporalHelpers.js] | ||
features: [Temporal] | ||
---*/ | ||
|
||
class CustomCalendar extends Temporal.Calendar { | ||
constructor() { | ||
super("iso8601"); | ||
this.dateFromFieldsCalls = []; | ||
} | ||
year(date) { | ||
// years in this calendar start and end on the same day as ISO 8601 years | ||
return date.getISOFields().isoYear; | ||
} | ||
month(date) { | ||
// this calendar has 10 months of 36 days each, plus an 11th month of 5 or 6 | ||
const { isoYear, isoMonth, isoDay } = date.getISOFields(); | ||
const isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay); | ||
return Math.floor((isoDate.dayOfYear - 1) / 36) + 1; | ||
} | ||
monthCode(date) { | ||
return "M" + this.month(date).toString().padStart(2, "0"); | ||
} | ||
day(date) { | ||
return (date.dayOfYear - 1) % 36 + 1; | ||
} | ||
daysInMonth(date) { | ||
if (this.month(date) < 11) return 36; | ||
return this.daysInYear(date) - 360; | ||
} | ||
_dateFromFieldsImpl({ year, month, monthCode, day }) { | ||
if (year === undefined) throw new TypeError("year required"); | ||
if (month === undefined && monthCode === undefined) throw new TypeError("one of month or monthCode required"); | ||
if (month !== undefined && month < 1) throw new RangeError("month < 1"); | ||
if (day === undefined) throw new TypeError("day required"); | ||
|
||
if (monthCode !== undefined) { | ||
const numberPart = +(monthCode.slice(1)); | ||
if ("M" + `${numberPart}`.padStart(2, "0") !== monthCode) throw new RangeError("invalid monthCode"); | ||
if (month === undefined) { | ||
month = numberPart; | ||
} else if (month !== numberPart) { | ||
throw new RangeError("month and monthCode must match"); | ||
} | ||
} | ||
|
||
const isoDayOfYear = (month - 1) * 36 + day; | ||
return new Temporal.PlainDate(year, 1, 1).add({ days: isoDayOfYear - 1 }).withCalendar(this); | ||
} | ||
dateFromFields(...args) { | ||
this.dateFromFieldsCalls.push(args); | ||
return this._dateFromFieldsImpl(...args); | ||
} | ||
yearMonthFromFields(fields, options) { | ||
const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, day: 1 }, options).getISOFields(); | ||
return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); | ||
} | ||
monthDayFromFields(fields, options) { | ||
const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, year: 2000 }, options).getISOFields(); | ||
return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); | ||
} | ||
dateAdd(date, duration, options) { | ||
if (duration.months) throw new Error("adding months not implemented in this test"); | ||
return super.dateAdd(date, duration, options); | ||
} | ||
toString() { | ||
return "thirty-six"; | ||
} | ||
} | ||
|
||
const calendar = new CustomCalendar(); | ||
const month2 = Temporal.PlainYearMonth.from({ year: 2022, month: 2, calendar }); | ||
const lessThanOneMonth = new Temporal.Duration(0, 0, 0, 35); | ||
const oneMonth = new Temporal.Duration(0, 0, 0, 36); | ||
|
||
calendar.dateFromFieldsCalls = []; | ||
TemporalHelpers.assertPlainYearMonth( | ||
month2.add(lessThanOneMonth), | ||
2022, 2, "M02", | ||
"adding positive less than one month's worth of days yields the same month" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); | ||
assert.deepEqual( | ||
calendar.dateFromFieldsCalls[0][0], | ||
{ year: 2022, month: 2, monthCode: "M02", day: 1 }, | ||
"first day of month 2 passed to dateFromFields when adding positive duration" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); | ||
|
||
calendar.dateFromFieldsCalls = []; | ||
TemporalHelpers.assertPlainYearMonth( | ||
month2.add(oneMonth), | ||
2022, 3, "M03", | ||
"adding positive one month's worth of days yields the following month" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); | ||
assert.deepEqual( | ||
calendar.dateFromFieldsCalls[0][0], | ||
{ year: 2022, month: 2, monthCode: "M02", day: 1 }, | ||
"first day of month 2 passed to dateFromFields when adding positive duration" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); | ||
|
||
calendar.dateFromFieldsCalls = []; | ||
TemporalHelpers.assertPlainYearMonth( | ||
month2.add(lessThanOneMonth.negated()), | ||
2022, 2, "M02", | ||
"adding negative less than one month's worth of days yields the same month" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); | ||
assert.deepEqual( | ||
calendar.dateFromFieldsCalls[0][0], | ||
{ year: 2022, month: 2, monthCode: "M02", day: 36 }, | ||
"last day of month 2 passed to dateFromFields when adding negative duration" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); | ||
|
||
calendar.dateFromFieldsCalls = []; | ||
TemporalHelpers.assertPlainYearMonth( | ||
month2.add(oneMonth.negated()), | ||
2022, 1, "M01", | ||
"adding negative one month's worth of days yields the previous month" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); | ||
assert.deepEqual( | ||
calendar.dateFromFieldsCalls[0][0], | ||
{ year: 2022, month: 2, monthCode: "M02", day: 36 }, | ||
"last day of month 2 passed to dateFromFields when adding negative duration" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); |
147 changes: 147 additions & 0 deletions
147
test/built-ins/Temporal/PlainYearMonth/prototype/subtract/calendar-datefromfields-called.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// Copyright (C) 2022 Igalia, S.L. All rights reserved. | ||
// This code is governed by the BSD license found in the LICENSE file. | ||
|
||
/*--- | ||
esid: sec-temporal.plainyearmonth.prototype.subtract | ||
description: > | ||
Calls calendar's dateFromFields method to obtain a start date for the | ||
operation, based on the sign of the duration | ||
info: | | ||
9. Let _fields_ be ? PrepareTemporalFields(_yearMonth_, _fieldNames_, «»). | ||
10. Let _sign_ be ! DurationSign(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _balanceResult_.[[Days]], 0, 0, 0, 0, 0, 0). | ||
11. If _sign_ < 0, then | ||
a. Let _dayFromCalendar_ be ? CalendarDaysInMonth(_calendar_, _yearMonth_). | ||
b. Let _day_ be ? ToPositiveInteger(_dayFromCalendar_). | ||
12. Else, | ||
a. Let _day_ be 1. | ||
13. Perform ! CreateDataPropertyOrThrow(_fields_, *"day"*, _day_). | ||
14. Let _date_ be ? DateFromFields(_calendar_, _fields_, *undefined*). | ||
includes: [deepEqual.js, temporalHelpers.js] | ||
features: [Temporal] | ||
---*/ | ||
|
||
class CustomCalendar extends Temporal.Calendar { | ||
constructor() { | ||
super("iso8601"); | ||
this.dateFromFieldsCalls = []; | ||
} | ||
year(date) { | ||
// years in this calendar start and end on the same day as ISO 8601 years | ||
return date.getISOFields().isoYear; | ||
} | ||
month(date) { | ||
// this calendar has 10 months of 36 days each, plus an 11th month of 5 or 6 | ||
const { isoYear, isoMonth, isoDay } = date.getISOFields(); | ||
const isoDate = new Temporal.PlainDate(isoYear, isoMonth, isoDay); | ||
return Math.floor((isoDate.dayOfYear - 1) / 36) + 1; | ||
} | ||
monthCode(date) { | ||
return "M" + this.month(date).toString().padStart(2, "0"); | ||
} | ||
day(date) { | ||
return (date.dayOfYear - 1) % 36 + 1; | ||
} | ||
daysInMonth(date) { | ||
if (this.month(date) < 11) return 36; | ||
return this.daysInYear(date) - 360; | ||
} | ||
_dateFromFieldsImpl({ year, month, monthCode, day }) { | ||
if (year === undefined) throw new TypeError("year required"); | ||
if (month === undefined && monthCode === undefined) throw new TypeError("one of month or monthCode required"); | ||
if (month !== undefined && month < 1) throw new RangeError("month < 1"); | ||
if (day === undefined) throw new TypeError("day required"); | ||
|
||
if (monthCode !== undefined) { | ||
const numberPart = +(monthCode.slice(1)); | ||
if ("M" + `${numberPart}`.padStart(2, "0") !== monthCode) throw new RangeError("invalid monthCode"); | ||
if (month === undefined) { | ||
month = numberPart; | ||
} else if (month !== numberPart) { | ||
throw new RangeError("month and monthCode must match"); | ||
} | ||
} | ||
|
||
const isoDayOfYear = (month - 1) * 36 + day; | ||
return new Temporal.PlainDate(year, 1, 1).add({ days: isoDayOfYear - 1 }).withCalendar(this); | ||
} | ||
dateFromFields(...args) { | ||
this.dateFromFieldsCalls.push(args); | ||
return this._dateFromFieldsImpl(...args); | ||
} | ||
yearMonthFromFields(fields, options) { | ||
const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, day: 1 }, options).getISOFields(); | ||
return new Temporal.PlainYearMonth(isoYear, isoMonth, this, isoDay); | ||
} | ||
monthDayFromFields(fields, options) { | ||
const { isoYear, isoMonth, isoDay } = this._dateFromFieldsImpl({ ...fields, year: 2000 }, options).getISOFields(); | ||
return new Temporal.PlainMonthDay(isoMonth, isoDay, this, isoYear); | ||
} | ||
dateAdd(date, duration, options) { | ||
if (duration.months) throw new Error("adding months not implemented in this test"); | ||
return super.dateAdd(date, duration, options); | ||
} | ||
toString() { | ||
return "thirty-six"; | ||
} | ||
} | ||
|
||
const calendar = new CustomCalendar(); | ||
const month2 = Temporal.PlainYearMonth.from({ year: 2022, month: 2, calendar }); | ||
const lessThanOneMonth = new Temporal.Duration(0, 0, 0, 35); | ||
const oneMonth = new Temporal.Duration(0, 0, 0, 36); | ||
|
||
calendar.dateFromFieldsCalls = []; | ||
TemporalHelpers.assertPlainYearMonth( | ||
month2.subtract(lessThanOneMonth), | ||
2022, 2, "M02", | ||
"subtracting positive less than one month's worth of days yields the same month" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); | ||
assert.deepEqual( | ||
calendar.dateFromFieldsCalls[0][0], | ||
{ year: 2022, month: 2, monthCode: "M02", day: 36 }, | ||
"last day of month 2 passed to dateFromFields when subtracting positive duration" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); | ||
|
||
calendar.dateFromFieldsCalls = []; | ||
TemporalHelpers.assertPlainYearMonth( | ||
month2.subtract(oneMonth), | ||
2022, 1, "M01", | ||
"subtracting positive one month's worth of days yields the previous month" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); | ||
assert.deepEqual( | ||
calendar.dateFromFieldsCalls[0][0], | ||
{ year: 2022, month: 2, monthCode: "M02", day: 36 }, | ||
"last day of month 2 passed to dateFromFields when subtracting positive duration" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); | ||
|
||
calendar.dateFromFieldsCalls = []; | ||
TemporalHelpers.assertPlainYearMonth( | ||
month2.subtract(lessThanOneMonth.negated()), | ||
2022, 2, "M02", | ||
"subtracting negative less than one month's worth of days yields the same month" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); | ||
assert.deepEqual( | ||
calendar.dateFromFieldsCalls[0][0], | ||
{ year: 2022, month: 2, monthCode: "M02", day: 1 }, | ||
"first day of month 2 passed to dateFromFields when subtracting negative duration" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); | ||
|
||
calendar.dateFromFieldsCalls = []; | ||
TemporalHelpers.assertPlainYearMonth( | ||
month2.subtract(oneMonth.negated()), | ||
2022, 3, "M03", | ||
"subtracting negative one month's worth of days yields the following month" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls.length, 1, "dateFromFields was called"); | ||
assert.deepEqual( | ||
calendar.dateFromFieldsCalls[0][0], | ||
{ year: 2022, month: 2, monthCode: "M02", day: 1 }, | ||
"first day of month 2 passed to dateFromFields when subtracting negative duration" | ||
); | ||
assert.sameValue(calendar.dateFromFieldsCalls[0][1], undefined, "undefined options passed"); |