Skip to main content

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}