boa_engine/builtins/temporal/plain_month_day/mod.rs
1//! Boa's implementation of the ECMAScript `Temporal.PlainMonthDay` built-in object.
2use std::str::FromStr;
3
4use crate::{
5 Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,
6 JsValue,
7 builtins::{
8 BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
9 options::{get_option, get_options_object},
10 temporal::{calendar::get_temporal_calendar_slot_value_with_default, to_calendar_fields},
11 },
12 context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
13 js_string,
14 object::internal_methods::get_prototype_from_constructor,
15 property::Attribute,
16 realm::Realm,
17 string::StaticJsStrings,
18};
19use boa_gc::{Finalize, Trace};
20
21use temporal_rs::{
22 Calendar, MonthCode, PlainMonthDay as InnerMonthDay,
23 fields::CalendarFields,
24 options::{DisplayCalendar, Overflow},
25 parsed_intermediates::ParsedDate,
26 partial::PartialDate,
27};
28
29use super::{DateTimeValues, create_temporal_date, is_partial_temporal_object};
30
31/// The `Temporal.PlainMonthDay` built-in implementation
32///
33/// More information:
34///
35/// - [ECMAScript Temporal proposal][spec]
36/// - [MDN reference][mdn]
37/// - [`temporal_rs` documentation][temporal_rs-docs]
38///
39/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plainmonthday-objects
40/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay
41/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html
42#[derive(Debug, Clone, Trace, Finalize, JsData)]
43#[boa_gc(unsafe_empty_trace)] // Safety: PlainMonthDay contains no traceable inner fields.
44pub struct PlainMonthDay {
45 pub(crate) inner: InnerMonthDay,
46}
47
48impl PlainMonthDay {
49 fn new(inner: InnerMonthDay) -> Self {
50 Self { inner }
51 }
52}
53
54impl BuiltInObject for PlainMonthDay {
55 const NAME: JsString = StaticJsStrings::PLAIN_MD_NAME;
56}
57
58impl IntrinsicObject for PlainMonthDay {
59 fn init(realm: &Realm) {
60 let get_day = BuiltInBuilder::callable(realm, Self::get_day)
61 .name(js_string!("get day"))
62 .build();
63
64 let get_month_code = BuiltInBuilder::callable(realm, Self::get_month_code)
65 .name(js_string!("get monthCode"))
66 .build();
67
68 let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id)
69 .name(js_string!("get calendarId"))
70 .build();
71
72 BuiltInBuilder::from_standard_constructor::<Self>(realm)
73 .property(
74 JsSymbol::to_string_tag(),
75 StaticJsStrings::PLAIN_MD_TAG,
76 Attribute::CONFIGURABLE,
77 )
78 .accessor(
79 js_string!("day"),
80 Some(get_day),
81 None,
82 Attribute::CONFIGURABLE,
83 )
84 .accessor(
85 js_string!("monthCode"),
86 Some(get_month_code),
87 None,
88 Attribute::CONFIGURABLE,
89 )
90 .accessor(
91 js_string!("calendarId"),
92 Some(get_calendar_id),
93 None,
94 Attribute::CONFIGURABLE,
95 )
96 .static_method(Self::from, js_string!("from"), 1)
97 .method(Self::with, js_string!("with"), 1)
98 .method(Self::equals, js_string!("equals"), 1)
99 .method(Self::to_string, js_string!("toString"), 0)
100 .method(Self::to_locale_string, js_string!("toLocaleString"), 0)
101 .method(Self::to_json, js_string!("toJSON"), 0)
102 .method(Self::value_of, js_string!("valueOf"), 0)
103 .method(Self::to_plain_date, js_string!("toPlainDate"), 1)
104 .build();
105 }
106
107 fn get(intrinsics: &Intrinsics) -> JsObject {
108 Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
109 }
110}
111
112impl BuiltInConstructor for PlainMonthDay {
113 const CONSTRUCTOR_ARGUMENTS: usize = 2;
114 const PROTOTYPE_STORAGE_SLOTS: usize = 14;
115 const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;
116
117 const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
118 StandardConstructors::plain_month_day;
119
120 fn constructor(
121 new_target: &JsValue,
122 args: &[JsValue],
123 context: &mut Context,
124 ) -> JsResult<JsValue> {
125 // 1. If NewTarget is undefined, then
126 if new_target.is_undefined() {
127 // a. Throw a TypeError exception.
128 return Err(JsNativeError::typ()
129 .with_message("NewTarget cannot be undefined when constructing a PlainYearMonth.")
130 .into());
131 }
132
133 // We can ignore 2 as the underlying temporal library handles the reference year
134 let m = args
135 .get_or_undefined(0)
136 .to_finitef64(context)?
137 .as_integer_with_truncation::<u8>();
138
139 let d = args
140 .get_or_undefined(1)
141 .to_finitef64(context)?
142 .as_integer_with_truncation::<u8>();
143
144 let calendar = args
145 .get_or_undefined(2)
146 .map(|s| {
147 s.as_string()
148 .as_ref()
149 .map(JsString::to_std_string_lossy)
150 .ok_or_else(|| JsNativeError::typ().with_message("calendar must be a string."))
151 })
152 .transpose()?
153 .map(|s| Calendar::try_from_utf8(s.as_bytes()))
154 .transpose()?
155 .unwrap_or_default();
156
157 let ref_year = args
158 .get_or_undefined(3)
159 .map(|v| {
160 let finite = v.to_finitef64(context)?;
161 Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())
162 })
163 .transpose()?;
164
165 let inner = InnerMonthDay::new_with_overflow(m, d, calendar, Overflow::Reject, ref_year)?;
166 create_temporal_month_day(inner, Some(new_target), context)
167 }
168}
169
170// ==== `Temporal.PlainMonthDay` static methods implementation ====
171
172impl PlainMonthDay {
173 /// 10.2.2 `Temporal.PlainMonthDay.from ( item [ , options ] )`
174 ///
175 /// More information:
176 ///
177 /// - [ECMAScript Temporal proposal][spec]
178 /// - [MDN reference][mdn]
179 ///
180 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.from
181 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/from
182 fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
183 let item = args.get_or_undefined(0);
184 let options = args.get_or_undefined(1);
185 let inner = to_temporal_month_day(item, options, context)?;
186 create_temporal_month_day(inner, None, context)
187 }
188}
189
190// ==== `PlainMonthDay` Accessor Implementations ====
191
192impl PlainMonthDay {
193 // Helper for retrieving internal fields
194 fn get_internal_field(this: &JsValue, field: &DateTimeValues) -> JsResult<JsValue> {
195 let object = this.as_object();
196 let month_day = object
197 .as_ref()
198 .and_then(JsObject::downcast_ref::<Self>)
199 .ok_or_else(|| {
200 JsNativeError::typ().with_message("this value must be a PlainMonthDay object.")
201 })?;
202 let inner = &month_day.inner;
203 match field {
204 DateTimeValues::Day => Ok(inner.day().into()),
205 DateTimeValues::MonthCode => Ok(js_string!(inner.month_code().as_str()).into()),
206 _ => unreachable!(),
207 }
208 }
209
210 /// 10.3.3 get `Temporal.PlainMonthDay.prototype.calendarId`
211 ///
212 /// More information:
213 ///
214 /// - [ECMAScript Temporal proposal][spec]
215 /// - [MDN reference][mdn]
216 /// - [`temporal_rs` documentation][temporal_rs-docs]
217 ///
218 /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.calendarid
219 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/calendarId
220 /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.calendar
221 fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
222 let object = this.as_object();
223 let month_day = object
224 .as_ref()
225 .and_then(JsObject::downcast_ref::<Self>)
226 .ok_or_else(|| {
227 JsNativeError::typ().with_message("this value must be a PlainMonthDay object.")
228 })?;
229 Ok(js_string!(month_day.inner.calendar().identifier()).into())
230 }
231
232 /// 10.3.4 get `Temporal.PlainMonthDay.prototype.day`
233 ///
234 /// More information:
235 ///
236 /// - [ECMAScript Temporal proposal][spec]
237 /// - [MDN reference][mdn]
238 /// - [`temporal_rs` documentation][temporal_rs-docs]
239 ///
240 /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.day
241 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/day
242 /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.day
243 fn get_day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
244 Self::get_internal_field(this, &DateTimeValues::Day)
245 }
246
247 /// 10.3.5 get `Temporal.PlainMonthDay.prototype.monthCode`
248 ///
249 /// More information:
250 ///
251 /// - [ECMAScript Temporal proposal][spec]
252 /// - [MDN reference][mdn]
253 /// - [`temporal_rs` documentation][temporal_rs-docs]
254 ///
255 /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.monthcode
256 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/monthCode
257 /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.month_code
258 fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
259 Self::get_internal_field(this, &DateTimeValues::MonthCode)
260 }
261}
262
263// ==== `Temporal.PlainMonthDay` Methods ====
264
265impl PlainMonthDay {
266 /// 10.3.6 `Temporal.PlainMonthDay.prototype.with ( temporalMonthDayLike [ , options ] )`
267 ///
268 /// More information:
269 ///
270 /// - [ECMAScript Temporal proposal][spec]
271 /// - [MDN reference][mdn]
272 /// - [`temporal_rs` documentation][temporal_rs-docs]
273 ///
274 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.with
275 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/with
276 /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.with
277 fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
278 // 1. Let monthDay be the this value.
279 // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
280 let object = this.as_object();
281 let month_day = object
282 .as_ref()
283 .and_then(JsObject::downcast_ref::<Self>)
284 .ok_or_else(|| {
285 JsNativeError::typ().with_message("this value must be a PlainMonthDay object.")
286 })?;
287
288 // 3. If ? IsPartialTemporalObject(temporalMonthDayLike) is false, throw a TypeError exception.
289 let Some(object) = is_partial_temporal_object(args.get_or_undefined(0), context)? else {
290 return Err(JsNativeError::typ()
291 .with_message("temporalMonthDayLike was not a partial object")
292 .into());
293 };
294 // 4. Let calendar be monthDay.[[Calendar]].
295 // 5. Let fields be ISODateToFields(calendar, monthDay.[[ISODate]], month-day).
296 // 6. Let partialMonthDay be ? PrepareCalendarFields(calendar, temporalMonthDayLike, « year, month, month-code, day », « », partial).
297 let fields = to_calendar_fields(&object, month_day.inner.calendar(), context)?;
298 // 7. Set fields to CalendarMergeFields(calendar, fields, partialMonthDay).
299 // 8. Let resolvedOptions be ? GetOptionsObject(options).
300 let resolved_options = get_options_object(args.get_or_undefined(1))?;
301 // 9. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
302 let overflow = get_option::<Overflow>(&resolved_options, js_string!("overflow"), context)?;
303 // 10. Let isoDate be ? CalendarMonthDayFromFields(calendar, fields, overflow).
304 // 11. Return ! CreateTemporalMonthDay(isoDate, calendar).
305 create_temporal_month_day(month_day.inner.with(fields, overflow)?, None, context)
306 }
307
308 /// 10.3.7 `Temporal.PlainMonthDay.prototype.equals ( other )`
309 ///
310 /// More information:
311 ///
312 /// - [ECMAScript Temporal proposal][spec]
313 /// - [MDN reference][mdn]
314 /// - [`temporal_rs` documentation][temporal_rs-docs]
315 ///
316 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.equals
317 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/equals
318 /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#impl-PartialEq-for-PlainMonthDay
319 fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
320 let object = this.as_object();
321 let month_day = object
322 .as_ref()
323 .and_then(JsObject::downcast_ref::<Self>)
324 .ok_or_else(|| {
325 JsNativeError::typ().with_message("this value must be a PlainMonthDay object.")
326 })?;
327
328 let other =
329 to_temporal_month_day(args.get_or_undefined(0), &JsValue::undefined(), context)?;
330
331 Ok((month_day.inner == other).into())
332 }
333
334 /// 10.3.8 `Temporal.PlainMonthDay.prototype.toString ( [ options ] )`
335 ///
336 /// More information:
337 ///
338 /// - [ECMAScript Temporal proposal][spec]
339 /// - [MDN reference][mdn]
340 /// - [`temporal_rs` documentation][temporal_rs-docs]
341 ///
342 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.tostring
343 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/toString
344 /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.to_ixdtf_string
345 fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
346 // 1. Let monthDay be the this value.
347 // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
348 let object = this.as_object();
349 let month_day = object
350 .as_ref()
351 .and_then(JsObject::downcast_ref::<Self>)
352 .ok_or_else(|| {
353 JsNativeError::typ().with_message("this value must be a PlainMonthDay object.")
354 })?;
355
356 // 3. Set options to ? NormalizeOptionsObject(options).
357 let options = get_options_object(args.get_or_undefined(0))?;
358 // 4. Let showCalendar be ? ToShowCalendarOption(options).
359 // Get calendarName from the options object
360 let show_calendar =
361 get_option::<DisplayCalendar>(&options, js_string!("calendarName"), context)?
362 .unwrap_or(DisplayCalendar::Auto);
363
364 let ixdtf = month_day.inner.to_ixdtf_string(show_calendar);
365 Ok(JsString::from(ixdtf).into())
366 }
367
368 /// 10.3.9 `Temporal.PlainMonthDay.prototype.toLocaleString ( [ locales [ , options ] ] )`
369 ///
370 /// More information:
371 ///
372 /// - [ECMAScript Temporal proposal][spec]
373 /// - [MDN reference][mdn]
374 ///
375 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.tolocalestring
376 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/toLocaleString
377 pub(crate) fn to_locale_string(
378 this: &JsValue,
379 _: &[JsValue],
380 _: &mut Context,
381 ) -> JsResult<JsValue> {
382 // TODO: Update for ECMA-402 compliance
383 let object = this.as_object();
384 let month_day = object
385 .as_ref()
386 .and_then(JsObject::downcast_ref::<Self>)
387 .ok_or_else(|| {
388 JsNativeError::typ().with_message("this value must be a PlainMonthDay object.")
389 })?;
390
391 Ok(JsString::from(month_day.inner.to_string()).into())
392 }
393
394 /// 10.3.10 `Temporal.PlainMonthDay.prototype.toJSON ( )`
395 ///
396 /// More information:
397 ///
398 /// - [ECMAScript Temporal proposal][spec]
399 /// - [MDN reference][mdn]
400 ///
401 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.tojson
402 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/toJSON
403 pub(crate) fn to_json(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
404 let object = this.as_object();
405 let month_day = object
406 .as_ref()
407 .and_then(JsObject::downcast_ref::<Self>)
408 .ok_or_else(|| {
409 JsNativeError::typ().with_message("this value must be a PlainMonthDay object.")
410 })?;
411
412 Ok(JsString::from(month_day.inner.to_string()).into())
413 }
414
415 /// 9.3.11 `Temporal.PlainMonthDay.prototype.valueOf ( )`
416 ///
417 /// More information:
418 ///
419 /// - [ECMAScript Temporal proposal][spec]
420 /// - [MDN reference][mdn]
421 ///
422 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.valueof
423 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/valueOf
424 pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
425 Err(JsNativeError::typ()
426 .with_message("`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`")
427 .into())
428 }
429
430 /// 10.3.12 `Temporal.PlainMonthDay.prototype.toPlainDate ( item )`
431 ///
432 /// More information:
433 ///
434 /// - [ECMAScript Temporal proposal][spec]
435 /// - [MDN reference][mdn]
436 /// - [`temporal_rs` documentation][temporal_rs-docs]
437 ///
438 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.toplaindate
439 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/toPlainDate
440 /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.to_plain_date
441 fn to_plain_date(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
442 // 1. Let monthDay be the this value.
443 // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
444 let object = this.as_object();
445 let month_day = object
446 .as_ref()
447 .and_then(JsObject::downcast_ref::<Self>)
448 .ok_or_else(|| {
449 JsNativeError::typ().with_message("this value must be a PlainMonthDay object.")
450 })?;
451
452 // 3. If item is not an Object, then
453 let Some(item) = args.get_or_undefined(0).as_object() else {
454 // a. Throw a TypeError exception.
455 return Err(JsNativeError::typ()
456 .with_message("toPlainDate item must be an object")
457 .into());
458 };
459
460 // TODO: Handle and implement the below
461 // 4. Let calendar be monthDay.[[Calendar]].
462 // 5. Let fields be ISODateToFields(calendar, monthDay.[[ISODate]], month-day).
463 // 6. Let inputFields be ? PrepareCalendarFields(calendar, item, « year », « », « »).
464 let year = item
465 .get(js_string!("year"), context)?
466 .map(|v| {
467 let finite = v.to_finitef64(context)?;
468 Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())
469 })
470 .transpose()?;
471
472 let fields = CalendarFields::new().with_optional_year(year);
473
474 // 7. Let mergedFields be CalendarMergeFields(calendar, fields, inputFields).
475 // 8. Let isoDate be ? CalendarDateFromFields(calendar, mergedFields, constrain).
476 // 9. Return ! CreateTemporalDate(isoDate, calendar).
477 let result = month_day.inner.to_plain_date(Some(fields))?;
478 create_temporal_date(result, None, context).map(Into::into)
479 }
480}
481
482// ==== `PlainMonthDay` Abstract Operations ====
483
484pub(crate) fn create_temporal_month_day(
485 inner: InnerMonthDay,
486 new_target: Option<&JsValue>,
487 context: &mut Context,
488) -> JsResult<JsValue> {
489 // 1. If IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw a RangeError exception.
490 // 2. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
491
492 // 3. If newTarget is not present, set newTarget to %Temporal.PlainMonthDay%.
493 let new_target = if let Some(target) = new_target {
494 target.clone()
495 } else {
496 context
497 .realm()
498 .intrinsics()
499 .constructors()
500 .plain_month_day()
501 .constructor()
502 .into()
503 };
504
505 // 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainMonthDay.prototype%", « [[InitializedTemporalMonthDay]], [[ISOMonth]], [[ISODay]], [[ISOYear]], [[Calendar]] »).
506 let proto = get_prototype_from_constructor(
507 &new_target,
508 StandardConstructors::plain_month_day,
509 context,
510 )?;
511
512 // 5. Set object.[[ISOMonth]] to isoMonth.
513 // 6. Set object.[[ISODay]] to isoDay.
514 // 7. Set object.[[Calendar]] to calendar.
515 // 8. Set object.[[ISOYear]] to referenceISOYear.
516 let obj = JsObject::from_proto_and_data(proto, PlainMonthDay::new(inner));
517
518 // 9. Return object.
519 Ok(obj.into())
520}
521
522fn to_temporal_month_day(
523 item: &JsValue,
524 options: &JsValue,
525 context: &mut Context,
526) -> JsResult<InnerMonthDay> {
527 // NOTE: One should be guaranteed by caller
528 // 1. If options is not present, set options to undefined.
529 // 2. If item is a Object, then
530 if let Some(obj) = item.as_object() {
531 // a. If item has an [[InitializedTemporalMonthDay]] internal slot, then
532 if let Some(md) = obj.downcast_ref::<PlainMonthDay>() {
533 // i. Let resolvedOptions be ? GetOptionsObject(options).
534 let options = get_options_object(options)?;
535 // ii. Perform ? GetTemporalOverflowOption(resolvedOptions).
536 let _ = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
537 // iii. Return ! CreateTemporalMonthDay(item.[[ISODate]], item.[[Calendar]]).
538 return Ok(md.inner.clone());
539 }
540
541 // b. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).
542 let calendar = get_temporal_calendar_slot_value_with_default(&obj, context)?;
543 // NOTE: inlined
544 // c. Let fields be ? PrepareCalendarFields(calendar, item, « year, month, month-code, day », «», «»).
545 let day = obj
546 .get(js_string!("day"), context)?
547 .map(|v| {
548 let finite = v.to_finitef64(context)?;
549 finite
550 .as_positive_integer_with_truncation::<u8>()
551 .map_err(JsError::from)
552 })
553 .transpose()?;
554
555 let month = obj
556 .get(js_string!("month"), context)?
557 .map(|v| {
558 let finite = v.to_finitef64(context)?;
559 finite
560 .as_positive_integer_with_truncation::<u8>()
561 .map_err(JsError::from)
562 })
563 .transpose()?;
564
565 let month_code = obj
566 .get(js_string!("monthCode"), context)?
567 .map(|v| {
568 let primitive = v.to_primitive(context, crate::value::PreferredType::String)?;
569 let Some(month_code) = primitive.as_string() else {
570 return Err(JsNativeError::typ()
571 .with_message("The monthCode field value must be a string.")
572 .into());
573 };
574 MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(JsError::from)
575 })
576 .transpose()?;
577
578 let year = obj
579 .get(js_string!("year"), context)?
580 .map(|v| {
581 let finite = v.to_finitef64(context)?;
582 Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())
583 })
584 .transpose()?;
585
586 let partial_date = PartialDate::new()
587 .with_month(month)
588 .with_day(day)
589 .with_year(year)
590 .with_month_code(month_code)
591 .with_calendar(calendar);
592
593 // d. Let resolvedOptions be ? GetOptionsObject(options).
594 let options = get_options_object(options)?;
595 // e. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
596 let overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
597 // f. Let isoDate be ? CalendarMonthDayFromFields(calendar, fields, overflow).
598 // g. Return ! CreateTemporalMonthDay(isoDate, calendar).
599 return Ok(InnerMonthDay::from_partial(partial_date, overflow)?);
600 }
601
602 // 3. If item is not a String, throw a TypeError exception.
603 let Some(md_string) = item.as_string() else {
604 return Err(JsNativeError::typ()
605 .with_message("item must be an object or a string")
606 .into());
607 };
608 // 4. Let result be ? ParseISODateTime(item, « TemporalMonthDayString »).
609 // 5. Let calendar be result.[[Calendar]].
610 // 6. If calendar is empty, set calendar to "iso8601".
611 // 7. Set calendar to ? CanonicalizeCalendar(calendar).
612 let parse_record =
613 ParsedDate::month_day_from_utf8(md_string.to_std_string_escaped().as_bytes())?;
614 // 8. Let resolvedOptions be ? GetOptionsObject(options).
615 let options = get_options_object(options)?;
616 // 9. Perform ? GetTemporalOverflowOption(resolvedOptions).
617 let _ = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
618 // 10. If calendar is "iso8601", then
619 // a. Let referenceISOYear be 1972 (the first ISO 8601 leap year after the epoch).
620 // b. Let isoDate be CreateISODateRecord(referenceISOYear, result.[[Month]], result.[[Day]]).
621 // c. Return ! CreateTemporalMonthDay(isoDate, calendar).
622 // 11. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).
623 // 12. If ISODateWithinLimits(isoDate) is false, throw a RangeError exception.
624 // 13. Set result to ISODateToFields(calendar, isoDate, month-day).
625 // 14. NOTE: The following operation is called with constrain regardless of the value of overflow, in order for the calendar to store a canonical value in the [[Year]] field of the [[ISODate]] internal slot of the result.
626 // 15. Set isoDate to ? CalendarMonthDayFromFields(calendar, result, constrain).
627 // 16. Return ! CreateTemporalMonthDay(isoDate, calendar).
628 Ok(InnerMonthDay::from_parsed(parse_record)?)
629}