Skip to content

Commit 8fb96ef

Browse files
committed
Restrict the units that can be added to PlainYearMonth
1 parent 306d914 commit 8fb96ef

File tree

1 file changed

+47
-53
lines changed

1 file changed

+47
-53
lines changed

src/builtins/core/plain_year_month.rs

Lines changed: 47 additions & 53 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

194-
// NOTE: The following are engine specific:
195-
// SKIP: 3. Let resolvedOptions be ? GetOptionsObject(options).
196-
// SKIP: 4. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
191+
// 3. Let internalDuration be ToInternalDurationRecord(duration).
192+
let internal_duration = duration.to_internal_duration_record();
197193

198-
// 5. Let sign be DurationSign(duration).
199-
let sign = duration.sign();
194+
// 4. Let durationToAdd be internalDuration.[[Date]].
195+
let duration_to_add = internal_duration.date();
200196

201-
// 6. Let calendar be yearMonth.[[Calendar]].
202-
let calendar = self.calendar();
197+
// 5. If durationToAdd.[[Weeks]] ≠ 0, or durationToAdd.[[Days]] ≠ 0, or internalDuration.[[Time]] ≠ 0,
198+
// throw a RangeError exception.
203199

204-
// 7. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], year-month).
205-
let fields = CalendarFields::from(YearMonthCalendarFields::try_from_year_month(self)?);
200+
if duration_to_add.weeks != 0
201+
|| duration_to_add.days != 0
202+
|| internal_duration.normalized_time_duration().0 != 0
203+
{
204+
return Err(TemporalError::range()
205+
.with_message("Can only add years or months to PlainYearMonth."));
206+
}
206207

207-
// 8. Set fields.[[Day]] to 1.
208-
let fields = fields.with_day(1);
208+
// NOTE: The following are engine specific:
209+
// SKIP: 6. Let resolvedOptions be ? GetOptionsObject(options).
210+
// SKIP: 7. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
209211

210-
// 9. Let intermediateDate be ? CalendarDateFromFields(calendar, fields, constrain).
211-
let intermediate_date = calendar.date_from_fields(fields, overflow)?;
212+
// 8. Let calendar be yearMonth.[[Calendar]].
213+
let calendar = self.calendar();
212214

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);
215+
// 9. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], year-month).
216+
let fields = CalendarFields::from(YearMonthCalendarFields::try_from_year_month(self)?);
217217

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-
};
218+
// 10. Set fields.[[Day]] to 1.
219+
let fields = fields.with_day(1);
242220

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,20 @@ 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
1162+
.add(&minus_19y, Overflow::Reject)
1163+
.unwrap();
1164+
assert_eq!(
1165+
prev.to_ixdtf_string(Default::default()),
1166+
"1993-04-22[u-ca=dangi]"
1167+
);
1168+
}
11751169
}

0 commit comments

Comments
 (0)