Skip to content

Commit 6ae79e5

Browse files
authored
Restrict the units that can be added to PlainYearMonth (#671)
This implements the spec text from tc39/proposal-temporal#3253 That change hasn't landed yet, but there are test262 tests relying on this (tc39/test262#4883)
1 parent 306d914 commit 6ae79e5

File tree

1 file changed

+44
-52
lines changed

1 file changed

+44
-52
lines changed

src/builtins/core/plain_year_month.rs

Lines changed: 44 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@ use crate::{
1616
parsed_intermediates::ParsedDate,
1717
parsers::{FormattableCalendar, FormattableDate, FormattableYearMonth},
1818
provider::{NeverProvider, TimeZoneProvider},
19-
temporal_assert,
2019
unix_time::EpochNanoseconds,
21-
Calendar, MonthCode, TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
20+
Calendar, MonthCode, TemporalError, TemporalResult, TimeZone,
2221
};
2322

24-
use super::{
25-
duration::normalized::InternalDurationRecord, DateDuration, Duration, PlainDate, PlainDateTime,
26-
};
23+
use super::{duration::normalized::InternalDurationRecord, Duration, PlainDate, PlainDateTime};
2724
use writeable::Writeable;
2825

2926
/// A partial PlainYearMonth record
@@ -191,70 +188,51 @@ impl PlainYearMonth {
191188
// NOTE: The following operation has been moved to the caller.
192189
// MOVE: 2. If operation is subtract, set duration to CreateNegatedTemporalDuration(duration).
193190

191+
// 3. Let internalDuration be ToInternalDurationRecord(duration).
192+
let internal_duration = duration.to_internal_duration_record();
193+
194+
// 4. Let durationToAdd be internalDuration.[[Date]].
195+
let duration_to_add = internal_duration.date();
196+
194197
// NOTE: The following are engine specific:
195-
// SKIP: 3. Let resolvedOptions be ? GetOptionsObject(options).
196-
// SKIP: 4. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
198+
// SKIP: 5. Let resolvedOptions be ? GetOptionsObject(options).
199+
// SKIP: 6. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
197200

198-
// 5. Let sign be DurationSign(duration).
199-
let sign = duration.sign();
201+
// 7. If durationToAdd.[[Weeks]] ≠ 0, or durationToAdd.[[Days]] ≠ 0, or internalDuration.[[Time]] ≠ 0,
202+
// throw a RangeError exception.
203+
204+
if duration_to_add.weeks != 0
205+
|| duration_to_add.days != 0
206+
|| internal_duration.normalized_time_duration().0 != 0
207+
{
208+
return Err(TemporalError::range()
209+
.with_message("Can only add years or months to PlainYearMonth."));
210+
}
200211

201-
// 6. Let calendar be yearMonth.[[Calendar]].
212+
// 8. Let calendar be yearMonth.[[Calendar]].
202213
let calendar = self.calendar();
203214

204-
// 7. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], year-month).
215+
// 9. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], year-month).
205216
let fields = CalendarFields::from(YearMonthCalendarFields::try_from_year_month(self)?);
206217

207-
// 8. Set fields.[[Day]] to 1.
218+
// 10. Set fields.[[Day]] to 1.
208219
let fields = fields.with_day(1);
209220

210-
// 9. Let intermediateDate be ? CalendarDateFromFields(calendar, fields, constrain).
211-
let intermediate_date = calendar.date_from_fields(fields, overflow)?;
212-
213-
// 10. If sign < 0, then
214-
let date = if sign.as_sign_multiplier() < 0 {
215-
// a. Let oneMonthDuration be ! CreateDateDurationRecord(0, 1, 0, 0).
216-
let one_month_duration = DateDuration::new_unchecked(0, 1, 0, 0);
217-
218-
// b. Let nextMonth be ? CalendarDateAdd(calendar, intermediateDate, oneMonthDuration, constrain).
219-
let next_month = calendar.date_add(
220-
&intermediate_date.iso,
221-
&one_month_duration,
222-
Overflow::Constrain,
223-
)?;
224-
let next_month = next_month.iso;
225-
226-
// c. Let date be BalanceISODate(nextMonth.[[Year]], nextMonth.[[Month]], nextMonth.[[Day]] - 1).
227-
let date = IsoDate::balance(
228-
next_month.year,
229-
i32::from(next_month.month),
230-
i32::from(next_month.day).checked_sub(1).temporal_unwrap()?,
231-
);
232-
233-
// d. Assert: ISODateWithinLimits(date) is true.
234-
temporal_assert!(date.is_valid());
235-
236-
date
237-
} else {
238-
// 11. Else,
239-
// a. Let date be intermediateDate.
240-
intermediate_date.iso
241-
};
242-
243-
// 12. Let durationToAdd be ToDateDurationRecordWithoutTime(duration).
244-
let duration_to_add = duration.to_date_duration_record_without_time()?;
221+
// 11. Let date be ? CalendarDateFromFields(calendar, fields, constrain).
222+
let date = calendar.date_from_fields(fields, Overflow::Constrain)?;
245223

246-
// 13. Let addedDate be ? CalendarDateAdd(calendar, date, durationToAdd, overflow).
247-
let added_date = calendar.date_add(&date, &duration_to_add, overflow)?;
224+
// 12. Let addedDate be ? CalendarDateAdd(calendar, date, durationToAdd, overflow).
225+
let added_date = calendar.date_add(&date.iso, &duration_to_add, overflow)?;
248226

249-
// 14. Let addedDateFields be ISODateToFields(calendar, addedDate, year-month).
227+
// 13. Let addedDateFields be ISODateToFields(calendar, addedDate, year-month).
250228
let added_date_fields = YearMonthCalendarFields::new()
251229
.with_month_code(added_date.month_code())
252230
.with_year(added_date.year());
253231

254-
// 15. Let isoDate be ? CalendarYearMonthFromFields(calendar, addedDateFields, overflow).
232+
// 14. Let isoDate be ? CalendarYearMonthFromFields(calendar, addedDateFields, overflow).
255233
let iso_date = calendar.year_month_from_fields(added_date_fields, overflow)?;
256234

257-
// 16. Return ! CreateTemporalYearMonth(isoDate, calendar).
235+
// 15. Return ! CreateTemporalYearMonth(isoDate, calendar).
258236
Ok(iso_date)
259237
}
260238

@@ -1172,4 +1150,18 @@ mod tests {
11721150
"2001-12-15[u-ca=chinese]"
11731151
);
11741152
}
1153+
1154+
#[test]
1155+
/// Should be able to subtract years from a leap month
1156+
///
1157+
/// Regression test for bugs fixed by <https://github.com/tc39/proposal-temporal/pull/3253/>
1158+
fn test_subtract_years_from_leap_month() {
1159+
let dangi_m03l_2012 = PlainYearMonth::from_str("2012-04-21[u-ca=dangi]").unwrap();
1160+
let minus_19y = Duration::from_str("-P19Y").unwrap();
1161+
let prev = dangi_m03l_2012.add(&minus_19y, Overflow::Reject).unwrap();
1162+
assert_eq!(
1163+
prev.to_ixdtf_string(Default::default()),
1164+
"1993-04-22[u-ca=dangi]"
1165+
);
1166+
}
11751167
}

0 commit comments

Comments
 (0)