boa_engine/builtins/temporal/plain_date_time/
mod.rs

1//! Boa's implementation of the ECMAScript `Temporal.PlainDateTime` built-in object.
2
3use std::str::FromStr;
4
5use crate::{
6    Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,
7    JsValue,
8    builtins::{
9        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
10        options::{get_option, get_options_object},
11        temporal::calendar::to_temporal_calendar_identifier,
12    },
13    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
14    js_string,
15    object::internal_methods::get_prototype_from_constructor,
16    property::Attribute,
17    realm::Realm,
18    string::StaticJsStrings,
19    value::IntoOrUndefined,
20};
21use boa_gc::{Finalize, Trace};
22
23#[cfg(test)]
24mod tests;
25
26use icu_calendar::AnyCalendarKind;
27use temporal_rs::{
28    Calendar, MonthCode, PlainDateTime as InnerDateTime, TinyAsciiStr,
29    fields::{CalendarFields, DateTimeFields},
30    options::{
31        Disambiguation, DisplayCalendar, Overflow, RoundingIncrement, RoundingMode,
32        RoundingOptions, ToStringRoundingOptions, Unit,
33    },
34    partial::{PartialDateTime, PartialTime},
35};
36
37use super::{
38    PlainDate, ZonedDateTime,
39    calendar::get_temporal_calendar_slot_value_with_default,
40    create_temporal_date, create_temporal_duration, create_temporal_time,
41    create_temporal_zoneddatetime,
42    options::{TemporalUnitGroup, get_difference_settings, get_digits_option, get_temporal_unit},
43    to_temporal_duration_record, to_temporal_time, to_temporal_timezone_identifier,
44};
45use crate::value::JsVariant;
46
47/// The `Temporal.PlainDateTime` built-in implementation.
48///
49/// More information:
50///
51/// - [ECMAScript Temporal proposal][spec]
52/// - [MDN reference][mdn]
53/// - [`temporal_rs` documentation][temporal_rs-docs]
54///
55/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plaindatetime-objects
56/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime
57/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html
58#[derive(Debug, Clone, Trace, Finalize, JsData)]
59#[boa_gc(unsafe_empty_trace)] // Safety: InnerDateTime does not contain any traceable types.
60pub struct PlainDateTime {
61    pub(crate) inner: InnerDateTime,
62}
63
64impl PlainDateTime {
65    fn new(inner: InnerDateTime) -> Self {
66        Self { inner }
67    }
68
69    pub(crate) fn inner(&self) -> &InnerDateTime {
70        &self.inner
71    }
72}
73
74impl BuiltInObject for PlainDateTime {
75    const NAME: JsString = StaticJsStrings::PLAIN_DATETIME_NAME;
76}
77
78impl IntrinsicObject for PlainDateTime {
79    fn init(realm: &Realm) {
80        let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id)
81            .name(js_string!("get calendarId"))
82            .build();
83
84        let get_era = BuiltInBuilder::callable(realm, Self::get_era)
85            .name(js_string!("get era"))
86            .build();
87
88        let get_era_year = BuiltInBuilder::callable(realm, Self::get_era_year)
89            .name(js_string!("get eraYear"))
90            .build();
91
92        let get_year = BuiltInBuilder::callable(realm, Self::get_year)
93            .name(js_string!("get year"))
94            .build();
95
96        let get_month = BuiltInBuilder::callable(realm, Self::get_month)
97            .name(js_string!("get month"))
98            .build();
99
100        let get_month_code = BuiltInBuilder::callable(realm, Self::get_month_code)
101            .name(js_string!("get monthCode"))
102            .build();
103
104        let get_day = BuiltInBuilder::callable(realm, Self::get_day)
105            .name(js_string!("get day"))
106            .build();
107
108        let get_hour = BuiltInBuilder::callable(realm, Self::get_hour)
109            .name(js_string!("get hour"))
110            .build();
111
112        let get_minute = BuiltInBuilder::callable(realm, Self::get_minute)
113            .name(js_string!("get minute"))
114            .build();
115
116        let get_second = BuiltInBuilder::callable(realm, Self::get_second)
117            .name(js_string!("get second"))
118            .build();
119
120        let get_millisecond = BuiltInBuilder::callable(realm, Self::get_millisecond)
121            .name(js_string!("get millisecond"))
122            .build();
123
124        let get_microsecond = BuiltInBuilder::callable(realm, Self::get_microsecond)
125            .name(js_string!("get microsecond"))
126            .build();
127
128        let get_nanosecond = BuiltInBuilder::callable(realm, Self::get_nanosecond)
129            .name(js_string!("get nanosecond"))
130            .build();
131
132        let get_day_of_week = BuiltInBuilder::callable(realm, Self::get_day_of_week)
133            .name(js_string!("get dayOfWeek"))
134            .build();
135
136        let get_day_of_year = BuiltInBuilder::callable(realm, Self::get_day_of_year)
137            .name(js_string!("get dayOfYear"))
138            .build();
139
140        let get_week_of_year = BuiltInBuilder::callable(realm, Self::get_week_of_year)
141            .name(js_string!("get weekOfYear"))
142            .build();
143
144        let get_year_of_week = BuiltInBuilder::callable(realm, Self::get_year_of_week)
145            .name(js_string!("get yearOfWeek"))
146            .build();
147
148        let get_days_in_week = BuiltInBuilder::callable(realm, Self::get_days_in_week)
149            .name(js_string!("get daysInWeek"))
150            .build();
151
152        let get_days_in_month = BuiltInBuilder::callable(realm, Self::get_days_in_month)
153            .name(js_string!("get daysInMonth"))
154            .build();
155
156        let get_days_in_year = BuiltInBuilder::callable(realm, Self::get_days_in_year)
157            .name(js_string!("get daysInYear"))
158            .build();
159
160        let get_months_in_year = BuiltInBuilder::callable(realm, Self::get_months_in_year)
161            .name(js_string!("get monthsInYear"))
162            .build();
163
164        let get_in_leap_year = BuiltInBuilder::callable(realm, Self::get_in_leap_year)
165            .name(js_string!("get inLeapYear"))
166            .build();
167
168        BuiltInBuilder::from_standard_constructor::<Self>(realm)
169            .property(
170                JsSymbol::to_string_tag(),
171                StaticJsStrings::PLAIN_DATETIME_TAG,
172                Attribute::CONFIGURABLE,
173            )
174            .accessor(
175                js_string!("calendarId"),
176                Some(get_calendar_id),
177                None,
178                Attribute::CONFIGURABLE,
179            )
180            .accessor(
181                js_string!("era"),
182                Some(get_era),
183                None,
184                Attribute::CONFIGURABLE,
185            )
186            .accessor(
187                js_string!("eraYear"),
188                Some(get_era_year),
189                None,
190                Attribute::CONFIGURABLE,
191            )
192            .accessor(
193                js_string!("year"),
194                Some(get_year),
195                None,
196                Attribute::CONFIGURABLE,
197            )
198            .accessor(
199                js_string!("month"),
200                Some(get_month),
201                None,
202                Attribute::CONFIGURABLE,
203            )
204            .accessor(
205                js_string!("monthCode"),
206                Some(get_month_code),
207                None,
208                Attribute::CONFIGURABLE,
209            )
210            .accessor(
211                js_string!("day"),
212                Some(get_day),
213                None,
214                Attribute::CONFIGURABLE,
215            )
216            .accessor(
217                js_string!("hour"),
218                Some(get_hour),
219                None,
220                Attribute::CONFIGURABLE,
221            )
222            .accessor(
223                js_string!("minute"),
224                Some(get_minute),
225                None,
226                Attribute::CONFIGURABLE,
227            )
228            .accessor(
229                js_string!("second"),
230                Some(get_second),
231                None,
232                Attribute::CONFIGURABLE,
233            )
234            .accessor(
235                js_string!("millisecond"),
236                Some(get_millisecond),
237                None,
238                Attribute::CONFIGURABLE,
239            )
240            .accessor(
241                js_string!("microsecond"),
242                Some(get_microsecond),
243                None,
244                Attribute::CONFIGURABLE,
245            )
246            .accessor(
247                js_string!("nanosecond"),
248                Some(get_nanosecond),
249                None,
250                Attribute::CONFIGURABLE,
251            )
252            .accessor(
253                js_string!("dayOfWeek"),
254                Some(get_day_of_week),
255                None,
256                Attribute::CONFIGURABLE,
257            )
258            .accessor(
259                js_string!("dayOfYear"),
260                Some(get_day_of_year),
261                None,
262                Attribute::CONFIGURABLE,
263            )
264            .accessor(
265                js_string!("weekOfYear"),
266                Some(get_week_of_year),
267                None,
268                Attribute::CONFIGURABLE,
269            )
270            .accessor(
271                js_string!("yearOfWeek"),
272                Some(get_year_of_week),
273                None,
274                Attribute::CONFIGURABLE,
275            )
276            .accessor(
277                js_string!("daysInWeek"),
278                Some(get_days_in_week),
279                None,
280                Attribute::CONFIGURABLE,
281            )
282            .accessor(
283                js_string!("daysInMonth"),
284                Some(get_days_in_month),
285                None,
286                Attribute::CONFIGURABLE,
287            )
288            .accessor(
289                js_string!("daysInYear"),
290                Some(get_days_in_year),
291                None,
292                Attribute::CONFIGURABLE,
293            )
294            .accessor(
295                js_string!("monthsInYear"),
296                Some(get_months_in_year),
297                None,
298                Attribute::CONFIGURABLE,
299            )
300            .accessor(
301                js_string!("inLeapYear"),
302                Some(get_in_leap_year),
303                None,
304                Attribute::CONFIGURABLE,
305            )
306            .static_method(Self::from, js_string!("from"), 1)
307            .static_method(Self::compare, js_string!("compare"), 2)
308            .method(Self::with, js_string!("with"), 1)
309            .method(Self::with_plain_time, js_string!("withPlainTime"), 0)
310            .method(Self::with_calendar, js_string!("withCalendar"), 1)
311            .method(Self::add, js_string!("add"), 1)
312            .method(Self::subtract, js_string!("subtract"), 1)
313            .method(Self::until, js_string!("until"), 1)
314            .method(Self::since, js_string!("since"), 1)
315            .method(Self::round, js_string!("round"), 1)
316            .method(Self::equals, js_string!("equals"), 1)
317            .method(Self::to_string, js_string!("toString"), 0)
318            .method(Self::to_locale_string, js_string!("toLocaleString"), 0)
319            .method(Self::to_json, js_string!("toJSON"), 0)
320            .method(Self::value_of, js_string!("valueOf"), 0)
321            .method(Self::to_zoned_date_time, js_string!("toZonedDateTime"), 1)
322            .method(Self::to_plain_date, js_string!("toPlainDate"), 0)
323            .method(Self::to_plain_time, js_string!("toPlainTime"), 0)
324            .build();
325    }
326
327    fn get(intrinsics: &Intrinsics) -> JsObject {
328        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
329    }
330}
331
332impl BuiltInConstructor for PlainDateTime {
333    const CONSTRUCTOR_ARGUMENTS: usize = 3;
334    const PROTOTYPE_STORAGE_SLOTS: usize = 61;
335    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;
336
337    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
338        StandardConstructors::plain_date_time;
339
340    fn constructor(
341        new_target: &JsValue,
342        args: &[JsValue],
343        context: &mut Context,
344    ) -> JsResult<JsValue> {
345        // 1. If NewTarget is undefined, then
346        if new_target.is_undefined() {
347            // a. Throw a TypeError exception.
348            return Err(JsNativeError::typ()
349                .with_message("NewTarget cannot be undefined when contructing PlainDatedt.")
350                .into());
351        }
352
353        // 2. Set isoYear to ? ToIntegerWithTruncation(isoYear).
354        let iso_year = args
355            .get_or_undefined(0)
356            .to_finitef64(context)?
357            .as_integer_with_truncation::<i32>();
358        // 3. Set isoMonth to ? ToIntegerWithTruncation(isoMonth).
359        let iso_month = args
360            .get_or_undefined(1)
361            .to_finitef64(context)?
362            .as_integer_with_truncation::<u8>();
363        // 4. Set isoDay to ? ToIntegerWithTruncation(isoDay).
364        let iso_day = args
365            .get_or_undefined(2)
366            .to_finitef64(context)?
367            .as_integer_with_truncation::<u8>();
368        // 5. If hour is undefined, set hour to 0; else set hour to ? ToIntegerWithTruncation(hour).
369        let hour = args.get_or_undefined(3).map_or(Ok::<u8, JsError>(0), |v| {
370            let finite = v.to_finitef64(context)?;
371            let int = finite.as_integer_with_truncation::<i8>();
372            if int < 0 {
373                return Err(JsNativeError::range()
374                    .with_message("invalid time field")
375                    .into());
376            }
377            Ok(int as u8)
378        })?;
379        // 6. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute).
380        let minute = args.get_or_undefined(4).map_or(Ok::<u8, JsError>(0), |v| {
381            let finite = v.to_finitef64(context)?;
382            let int = finite.as_integer_with_truncation::<i8>();
383            if int < 0 {
384                return Err(JsNativeError::range()
385                    .with_message("invalid time field")
386                    .into());
387            }
388            Ok(int as u8)
389        })?;
390
391        // 7. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second).
392        let second = args.get_or_undefined(5).map_or(Ok::<u8, JsError>(0), |v| {
393            let finite = v.to_finitef64(context)?;
394            let int = finite.as_integer_with_truncation::<i8>();
395            if int < 0 {
396                return Err(JsNativeError::range()
397                    .with_message("invalid time field")
398                    .into());
399            }
400            Ok(int as u8)
401        })?;
402
403        // 8. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond).
404        let millisecond = args
405            .get_or_undefined(6)
406            .map_or(Ok::<u16, JsError>(0), |v| {
407                let finite = v.to_finitef64(context)?;
408                let int = finite.as_integer_with_truncation::<i16>();
409                if int < 0 {
410                    return Err(JsNativeError::range()
411                        .with_message("invalid time field")
412                        .into());
413                }
414                Ok(int as u16)
415            })?;
416
417        // 9. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond).
418        let microsecond = args
419            .get_or_undefined(7)
420            .map_or(Ok::<u16, JsError>(0), |v| {
421                let finite = v.to_finitef64(context)?;
422                let int = finite.as_integer_with_truncation::<i16>();
423                if int < 0 {
424                    return Err(JsNativeError::range()
425                        .with_message("invalid time field")
426                        .into());
427                }
428                Ok(int as u16)
429            })?;
430
431        // 10. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond).
432        let nanosecond = args
433            .get_or_undefined(8)
434            .map_or(Ok::<u16, JsError>(0), |v| {
435                let finite = v.to_finitef64(context)?;
436                let int = finite.as_integer_with_truncation::<i16>();
437                if int < 0 {
438                    return Err(JsNativeError::range()
439                        .with_message("invalid time field")
440                        .into());
441                }
442                Ok(int as u16)
443            })?;
444
445        let calendar_slot = args
446            .get_or_undefined(9)
447            .map(|s| {
448                s.as_string()
449                    .as_ref()
450                    .map(JsString::to_std_string_lossy)
451                    .ok_or_else(|| JsNativeError::typ().with_message("calendar must be a string."))
452            })
453            .transpose()?
454            .map(|s| Calendar::try_from_utf8(s.as_bytes()))
455            .transpose()?
456            .unwrap_or_default();
457
458        let dt = InnerDateTime::try_new(
459            iso_year,
460            iso_month,
461            iso_day,
462            hour,
463            minute,
464            second,
465            millisecond,
466            microsecond,
467            nanosecond,
468            calendar_slot,
469        )?;
470
471        // 12. Return ? CreateTemporalDateTime(isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond, calendar, NewTarget).
472        create_temporal_datetime(dt, Some(new_target), context).map(Into::into)
473    }
474}
475
476// ==== `PlainDateTimeTime` accessor methods implmentation ====
477
478impl PlainDateTime {
479    /// 5.3.3 get `Temporal.PlainDateTime.prototype.calendarId`
480    ///
481    /// More information:
482    ///
483    /// - [ECMAScript Temporal proposal][spec]
484    /// - [MDN reference][mdn]
485    /// - [`temporal_rs` documentation][temporal_rs-docs]
486    ///
487    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.calendarid
488    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/calendarId
489    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.calendar
490    fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
491        let object = this.as_object();
492        let dt = object
493            .as_ref()
494            .and_then(JsObject::downcast_ref::<Self>)
495            .ok_or_else(|| {
496                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
497            })?;
498
499        Ok(JsString::from(dt.inner.calendar().identifier()).into())
500    }
501
502    /// 5.3.4 get `Temporal.PlainDateTime.prototype.era`
503    ///
504    /// More information:
505    ///
506    /// - [ECMAScript Temporal proposal][spec]
507    /// - [MDN reference][mdn]
508    /// - [`temporal_rs` documentation][temporal_rs-docs]
509    ///
510    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.era
511    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/era
512    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.era
513    fn get_era(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
514        let object = this.as_object();
515        let dt = object
516            .as_ref()
517            .and_then(JsObject::downcast_ref::<Self>)
518            .ok_or_else(|| {
519                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
520            })?;
521
522        Ok(dt
523            .inner
524            .era()
525            .map(|s| JsString::from(s.as_str()))
526            .into_or_undefined())
527    }
528
529    /// 5.3.5 get `Temporal.PlainDateTime.prototype.eraYear`
530    ///
531    /// More information:
532    ///
533    /// - [ECMAScript Temporal proposal][spec]
534    /// - [MDN reference][mdn]
535    /// - [`temporal_rs` documentation][temporal_rs-docs]
536    ///
537    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.erayear
538    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/eraYear
539    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.era_year
540    fn get_era_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
541        let object = this.as_object();
542        let dt = object
543            .as_ref()
544            .and_then(JsObject::downcast_ref::<Self>)
545            .ok_or_else(|| {
546                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
547            })?;
548
549        Ok(dt.inner.era_year().into_or_undefined())
550    }
551
552    /// 5.3.6 get `Temporal.PlainDateTime.prototype.year`
553    ///
554    /// More information:
555    ///
556    /// - [ECMAScript Temporal proposal][spec]
557    /// - [MDN reference][mdn]
558    /// - [`temporal_rs` documentation][temporal_rs-docs]
559    ///
560    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.year
561    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/year
562    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.year
563    fn get_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
564        let object = this.as_object();
565        let dt = object
566            .as_ref()
567            .and_then(JsObject::downcast_ref::<Self>)
568            .ok_or_else(|| {
569                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
570            })?;
571
572        Ok(dt.inner.year().into())
573    }
574
575    /// 5.3.7 get `Temporal.PlainDateTime.prototype.month`
576    ///
577    /// More information:
578    ///
579    /// - [ECMAScript Temporal proposal][spec]
580    /// - [MDN reference][mdn]
581    /// - [`temporal_rs` documentation][temporal_rs-docs]
582    ///
583    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.month
584    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/month
585    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html
586    fn get_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
587        let object = this.as_object();
588        let dt = object
589            .as_ref()
590            .and_then(JsObject::downcast_ref::<Self>)
591            .ok_or_else(|| {
592                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
593            })?;
594
595        Ok(dt.inner.month().into())
596    }
597
598    /// 5.3.8 get `Temporal.PlainDateTime.prototype.monthCode`
599    ///
600    /// More information:
601    ///
602    /// - [ECMAScript Temporal proposal][spec]
603    /// - [MDN reference][mdn]
604    /// - [`temporal_rs` documentation][temporal_rs-docs]
605    ///
606    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.monthcode
607    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/monthCode
608    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.month_code
609    fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
610        let object = this.as_object();
611        let dt = object
612            .as_ref()
613            .and_then(JsObject::downcast_ref::<Self>)
614            .ok_or_else(|| {
615                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
616            })?;
617
618        Ok(JsString::from(dt.inner.month_code().as_str()).into())
619    }
620
621    /// 5.3.9 get `Temporal.PlainDateTime.prototype.day`
622    ///
623    /// More information:
624    ///
625    /// - [ECMAScript Temporal proposal][spec]
626    /// - [MDN reference][mdn]
627    /// - [`temporal_rs` documentation][temporal_rs-docs]
628    ///
629    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.day
630    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/day
631    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.day
632    fn get_day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
633        let object = this.as_object();
634        let dt = object
635            .as_ref()
636            .and_then(JsObject::downcast_ref::<Self>)
637            .ok_or_else(|| {
638                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
639            })?;
640
641        Ok(dt.inner.day().into())
642    }
643
644    /// 5.3.10 get `Temporal.PlainDateTime.prototype.hour`
645    ///
646    /// More information:
647    ///
648    /// - [ECMAScript Temporal proposal][spec]
649    /// - [MDN reference][mdn]
650    /// - [`temporal_rs` documentation][temporal_rs-docs]
651    ///
652    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.hour
653    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/hour
654    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.hour
655    fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
656        // 1. Let dateTime be the this value.
657        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
658        let object = this.as_object();
659        let dt = object
660            .as_ref()
661            .and_then(JsObject::downcast_ref::<Self>)
662            .ok_or_else(|| {
663                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
664            })?;
665
666        // 3. Return 𝔽(datedt.[[ISOHour]]).
667        Ok(dt.inner.hour().into())
668    }
669
670    /// 5.3.11 get `Temporal.PlainDateTime.prototype.minute`
671    ///
672    /// More information:
673    ///
674    /// - [ECMAScript Temporal proposal][spec]
675    /// - [MDN reference][mdn]
676    /// - [`temporal_rs` documentation][temporal_rs-docs]
677    ///
678    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.minute
679    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/minute
680    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.minute
681    fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
682        // 1. Let dateTime be the this value.
683        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
684        let object = this.as_object();
685        let dt = object
686            .as_ref()
687            .and_then(JsObject::downcast_ref::<Self>)
688            .ok_or_else(|| {
689                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
690            })?;
691
692        // 3. Return 𝔽(datedt.[[ISOMinute]]).
693        Ok(dt.inner.minute().into())
694    }
695
696    /// 5.3.12 get `Temporal.PlainDateTime.prototype.second`
697    ///
698    /// More information:
699    ///
700    /// - [ECMAScript Temporal proposal][spec]
701    /// - [MDN reference][mdn]
702    /// - [`temporal_rs` documentation][temporal_rs-docs]
703    ///
704    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.second
705    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/second
706    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.second
707    fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
708        // 1. Let dateTime be the this value.
709        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
710        let object = this.as_object();
711        let dt = object
712            .as_ref()
713            .and_then(JsObject::downcast_ref::<Self>)
714            .ok_or_else(|| {
715                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
716            })?;
717
718        // 3. Return 𝔽(datedt.[[ISOSecond]]).
719        Ok(dt.inner.second().into())
720    }
721
722    /// 5.3.13 get `Temporal.PlainDateTime.prototype.millisecond`
723    ///
724    /// More information:
725    ///
726    /// - [ECMAScript Temporal proposal][spec]
727    /// - [MDN reference][mdn]
728    /// - [`temporal_rs` documentation][temporal_rs-docs]
729    ///
730    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.millisecond
731    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/millisecond
732    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.millisecond
733    fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
734        // 1. Let dateTime be the this value.
735        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
736        let object = this.as_object();
737        let dt = object
738            .as_ref()
739            .and_then(JsObject::downcast_ref::<Self>)
740            .ok_or_else(|| {
741                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
742            })?;
743
744        // 3. Return 𝔽(datedt.[[ISOMillisecond]]).
745        Ok(dt.inner.millisecond().into())
746    }
747
748    /// 5.3.14 get `Temporal.PlainDateTime.prototype.microsecond`
749    ///
750    /// More information:
751    ///
752    /// - [ECMAScript Temporal proposal][spec]
753    /// - [MDN reference][mdn]
754    /// - [`temporal_rs` documentation][temporal_rs-docs]
755    ///
756    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.microsecond
757    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/microsecond
758    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.microsecond
759    fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
760        // 1. Let dateTime be the this value.
761        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
762        let object = this.as_object();
763        let dt = object
764            .as_ref()
765            .and_then(JsObject::downcast_ref::<Self>)
766            .ok_or_else(|| {
767                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
768            })?;
769
770        // 3. Return 𝔽(datedt.[[ISOMicrosecond]]).
771        Ok(dt.inner.microsecond().into())
772    }
773
774    /// 5.3.15 get `Temporal.PlainDateTime.prototype.nanosecond`
775    ///
776    /// More information:
777    ///
778    /// - [ECMAScript Temporal proposal][spec]
779    /// - [MDN reference][mdn]
780    /// - [`temporal_rs` documentation][temporal_rs-docs]
781    ///
782    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.nanosecond
783    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/nanosecond
784    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.nanosecond
785    fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
786        // 1. Let dateTime be the this value.
787        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
788        let object = this.as_object();
789        let dt = object
790            .as_ref()
791            .and_then(JsObject::downcast_ref::<Self>)
792            .ok_or_else(|| {
793                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
794            })?;
795
796        // 3. Return 𝔽(datedt.[[ISONanosecond]]).
797        Ok(dt.inner.nanosecond().into())
798    }
799
800    /// 5.3.16 get `Temporal.PlainDateTime.prototype.dayOfWeek`
801    ///
802    /// More information:
803    ///
804    /// - [ECMAScript Temporal proposal][spec]
805    /// - [MDN reference][mdn]
806    /// - [`temporal_rs` documentation][temporal_rs-docs]
807    ///
808    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.dayofweek
809    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/dayOfWeek
810    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.day_of_week
811    fn get_day_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
812        let object = this.as_object();
813        let dt = object
814            .as_ref()
815            .and_then(JsObject::downcast_ref::<Self>)
816            .ok_or_else(|| {
817                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
818            })?;
819
820        Ok(dt.inner.day_of_week().into())
821    }
822
823    /// 5.3.17 get `Temporal.PlainDateTime.prototype.dayOfYear`
824    ///
825    /// More information:
826    ///
827    /// - [ECMAScript Temporal proposal][spec]
828    /// - [MDN reference][mdn]
829    /// - [`temporal_rs` documentation][temporal_rs-docs]
830    ///
831    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.dayofyear
832    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/dayOfYear
833    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.day_of_year
834    fn get_day_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
835        let object = this.as_object();
836        let dt = object
837            .as_ref()
838            .and_then(JsObject::downcast_ref::<Self>)
839            .ok_or_else(|| {
840                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
841            })?;
842
843        Ok(dt.inner.day_of_year().into())
844    }
845
846    /// 5.3.18 get `Temporal.PlainDateTime.prototype.weekOfYear`
847    ///
848    /// More information:
849    ///
850    /// - [ECMAScript Temporal proposal][spec]
851    /// - [MDN reference][mdn]
852    /// - [`temporal_rs` documentation][temporal_rs-docs]
853    ///
854    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.weekofyear
855    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/weekOfYear
856    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.week_of_year
857    fn get_week_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
858        let object = this.as_object();
859        let dt = object
860            .as_ref()
861            .and_then(JsObject::downcast_ref::<Self>)
862            .ok_or_else(|| {
863                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
864            })?;
865
866        Ok(dt.inner.week_of_year().into_or_undefined())
867    }
868
869    /// 5.3.19 get `Temporal.PlainDateTime.prototype.yearOfWeek`
870    ///
871    /// More information:
872    ///
873    /// - [ECMAScript Temporal proposal][spec]
874    /// - [MDN reference][mdn]
875    /// - [`temporal_rs` documentation][temporal_rs-docs]
876    ///
877    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.yearofweek
878    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/yearOfWeek
879    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.year_of_week
880    fn get_year_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
881        let object = this.as_object();
882        let dt = object
883            .as_ref()
884            .and_then(JsObject::downcast_ref::<Self>)
885            .ok_or_else(|| {
886                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
887            })?;
888
889        Ok(dt.inner.year_of_week().into_or_undefined())
890    }
891
892    /// 5.3.20 get `Temporal.PlainDateTime.prototype.daysInWeek`
893    ///
894    /// More information:
895    ///
896    /// - [ECMAScript Temporal proposal][spec]
897    /// - [MDN reference][mdn]
898    /// - [`temporal_rs` documentation][temporal_rs-docs]
899    ///
900    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinweek
901    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/daysInWeek
902    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.days_in_week
903    fn get_days_in_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
904        let object = this.as_object();
905        let dt = object
906            .as_ref()
907            .and_then(JsObject::downcast_ref::<Self>)
908            .ok_or_else(|| {
909                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
910            })?;
911
912        Ok(dt.inner.days_in_week().into())
913    }
914
915    /// 5.3.21 get `Temporal.PlainDateTime.prototype.daysInMonth`
916    ///
917    /// More information:
918    ///
919    /// - [ECMAScript Temporal proposal][spec]
920    /// - [MDN reference][mdn]
921    /// - [`temporal_rs` documentation][temporal_rs-docs]
922    ///
923    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinmonth
924    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/daysInMonth
925    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.days_in_month
926    fn get_days_in_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
927        let object = this.as_object();
928        let dt = object
929            .as_ref()
930            .and_then(JsObject::downcast_ref::<Self>)
931            .ok_or_else(|| {
932                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
933            })?;
934
935        Ok(dt.inner.days_in_month().into())
936    }
937
938    /// 5.3.22 get `Temporal.PlainDateTime.prototype.daysInYear`
939    ///
940    /// More information:
941    ///
942    /// - [ECMAScript Temporal proposal][spec]
943    /// - [MDN reference][mdn]
944    /// - [`temporal_rs` documentation][temporal_rs-docs]
945    ///
946    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinyear
947    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/daysInYear
948    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.days_in_year
949    fn get_days_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
950        let object = this.as_object();
951        let dt = object
952            .as_ref()
953            .and_then(JsObject::downcast_ref::<Self>)
954            .ok_or_else(|| {
955                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
956            })?;
957
958        Ok(dt.inner.days_in_year().into())
959    }
960
961    /// 5.3.23 get `Temporal.PlainDateTime.prototype.monthsInYear`
962    ///
963    /// More information:
964    ///
965    /// - [ECMAScript Temporal proposal][spec]
966    /// - [MDN reference][mdn]
967    /// - [`temporal_rs` documentation][temporal_rs-docs]
968    ///
969    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.monthsinyear
970    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/monthsInYear
971    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.months_in_year
972    fn get_months_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
973        let object = this.as_object();
974        let dt = object
975            .as_ref()
976            .and_then(JsObject::downcast_ref::<Self>)
977            .ok_or_else(|| {
978                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
979            })?;
980
981        Ok(dt.inner.months_in_year().into())
982    }
983
984    /// 5.3.24 get `Temporal.PlainDateTime.prototype.inLeapYear`
985    ///
986    /// More information:
987    ///
988    /// - [ECMAScript Temporal proposal][spec]
989    /// - [MDN reference][mdn]
990    /// - [`temporal_rs` documentation][temporal_rs-docs]
991    ///
992    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.inleapyear
993    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/inLeapYear
994    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.in_leap_year
995    fn get_in_leap_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
996        let object = this.as_object();
997        let dt = object
998            .as_ref()
999            .and_then(JsObject::downcast_ref::<Self>)
1000            .ok_or_else(|| {
1001                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1002            })?;
1003
1004        Ok(dt.inner.in_leap_year().into())
1005    }
1006}
1007
1008// ==== PlainDateTime static methods implementation ====
1009
1010impl PlainDateTime {
1011    /// 5.2.2 `Temporal.PlainDateTime.from ( item [ , options ] )`
1012    ///
1013    /// More information:
1014    ///
1015    /// - [ECMAScript Temporal proposal][spec]
1016    /// - [MDN reference][mdn]
1017    ///
1018    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.from
1019    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/from
1020    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1021        let item = args.get_or_undefined(0);
1022        // 1. Set options to ? GetOptionsObject(options).
1023        let options = args.get(1);
1024        // 2. If item is an Object and item has an [[InitializedTemporalDateTime]] internal slot, then
1025        let object = item.as_object();
1026        let dt = if let Some(pdt) = object.as_ref().and_then(JsObject::downcast_ref::<Self>) {
1027            // a. Perform ? GetTemporalOverflowOption(options).
1028            let options = get_options_object(args.get_or_undefined(1))?;
1029            let _ = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1030            // b. Return ! CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]],
1031            // item.[[ISODay]], item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]],
1032            // item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]],
1033            // item.[[Calendar]]).
1034            pdt.inner.clone()
1035        } else {
1036            to_temporal_datetime(item, options.cloned(), context)?
1037        };
1038
1039        // 3. Return ? ToTemporalDateTime(item, options).
1040        create_temporal_datetime(dt, None, context).map(Into::into)
1041    }
1042
1043    /// 5.2.3 `Temporal.PlainDateTime.compare ( one, two )`
1044    ///
1045    /// More information:
1046    ///
1047    /// - [ECMAScript Temporal proposal][spec]
1048    /// - [MDN reference][mdn]
1049    /// - [`temporal_rs` documentation][temporal_rs-docs]
1050    ///
1051    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.compare
1052    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/compare
1053    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.compare_iso
1054    fn compare(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1055        // 1. Set one to ? ToTemporalDateTime(one).
1056        let one = to_temporal_datetime(args.get_or_undefined(0), None, context)?;
1057        // 2. Set two to ? ToTemporalDateTime(two).
1058        let two = to_temporal_datetime(args.get_or_undefined(1), None, context)?;
1059
1060        // 3. Return 𝔽(CompareISODateTime(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]],
1061        // one.[[ISOHour]], one.[[ISOMinute]], one.[[ISOSecond]], one.[[ISOMillisecond]],
1062        // one.[[ISOMicrosecond]], one.[[ISONanosecond]], two.[[ISOYear]], two.[[ISOMonth]],
1063        // two.[[ISODay]], two.[[ISOHour]], two.[[ISOMinute]], two.[[ISOSecond]],
1064        // two.[[ISOMillisecond]], two.[[ISOMicrosecond]], two.[[ISONanosecond]])).
1065        Ok((one.compare_iso(&two) as i8).into())
1066    }
1067}
1068
1069// ==== PlainDateTime methods implementation ====
1070
1071impl PlainDateTime {
1072    ///  5.3.25 `Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )`
1073    ///
1074    /// More information:
1075    ///
1076    /// - [ECMAScript Temporal proposal][spec]
1077    /// - [MDN reference][mdn]
1078    /// - [`temporal_rs` documentation][temporal_rs-docs]
1079    ///
1080    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with
1081    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with
1082    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.with
1083    fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1084        // 1. Let plainDateTime be the this value.
1085        // 2. Perform ? RequireInternalSlot(plainDateTime, [[InitializedTemporalDateTime]]).
1086        let object = this.as_object();
1087        let dt = object
1088            .as_ref()
1089            .and_then(JsObject::downcast_ref::<Self>)
1090            .ok_or_else(|| {
1091                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1092            })?;
1093
1094        // 3. If ? IsPartialTemporalObject(temporalDateTimeLike) is false, throw a TypeError exception.
1095        let Some(partial_object) =
1096            super::is_partial_temporal_object(args.get_or_undefined(0), context)?
1097        else {
1098            return Err(JsNativeError::typ()
1099                .with_message("with object was not a PartialTemporalObject.")
1100                .into());
1101        };
1102        // 4. Let calendar be plainDateTime.[[Calendar]].
1103        // 5. Let fields be ISODateToFields(calendar, plainDateTime.[[ISODateTime]].[[ISODate]], date).
1104        // 6. Set fields.[[Hour]] to plainDateTime.[[ISODateTime]].[[Time]].[[Hour]].
1105        // 7. Set fields.[[Minute]] to plainDateTime.[[ISODateTime]].[[Time]].[[Minute]].
1106        // 8. Set fields.[[Second]] to plainDateTime.[[ISODateTime]].[[Time]].[[Second]].
1107        // 9. Set fields.[[Millisecond]] to plainDateTime.[[ISODateTime]].[[Time]].[[Millisecond]].
1108        // 10. Set fields.[[Microsecond]] to plainDateTime.[[ISODateTime]].[[Time]].[[Microsecond]].
1109        // 11. Set fields.[[Nanosecond]] to plainDateTime.[[ISODateTime]].[[Time]].[[Nanosecond]].
1110        // 12. Let partialDateTime be ? PrepareCalendarFields(calendar, temporalDateTimeLike, « year, month, month-code, day », « hour, minute, second, millisecond, microsecond, nanosecond », partial).
1111        // 13. Set fields to CalendarMergeFields(calendar, fields, partialDateTime).
1112        let fields = to_date_time_fields(&partial_object, dt.inner.calendar(), context)?;
1113        // 14. Let resolvedOptions be ? GetOptionsObject(options).
1114        let options = get_options_object(args.get_or_undefined(1))?;
1115        // 15. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
1116        let overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1117
1118        // 16. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).
1119        // 17. Return ? CreateTemporalDateTime(result, calendar).
1120        create_temporal_datetime(dt.inner.with(fields, overflow)?, None, context).map(Into::into)
1121    }
1122
1123    /// 5.3.26 Temporal.PlainDateTime.prototype.withPlainTime ( `[ plainTimeLike ]` )
1124    ///
1125    /// More information:
1126    ///
1127    /// - [ECMAScript Temporal proposal][spec]
1128    /// - [MDN reference][mdn]
1129    /// - [`temporal_rs` documentation][temporal_rs-docs]
1130    ///
1131    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withplaintime
1132    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/withPlainTime
1133    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.with_plain_time
1134    fn with_plain_time(
1135        this: &JsValue,
1136        args: &[JsValue],
1137        context: &mut Context,
1138    ) -> JsResult<JsValue> {
1139        let object = this.as_object();
1140        let dt = object
1141            .as_ref()
1142            .and_then(JsObject::downcast_ref::<Self>)
1143            .ok_or_else(|| {
1144                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1145            })?;
1146
1147        let time = args
1148            .get_or_undefined(0)
1149            .map(|v| to_temporal_time(v, None, context))
1150            .transpose()?;
1151
1152        create_temporal_datetime(dt.inner.with_time(time)?, None, context).map(Into::into)
1153    }
1154
1155    /// 5.3.27 `Temporal.PlainDateTime.prototype.withCalendar ( calendarLike )`
1156    ///
1157    /// More information:
1158    ///
1159    /// - [ECMAScript Temporal proposal][spec]
1160    /// - [MDN reference][mdn]
1161    /// - [`temporal_rs` documentation][temporal_rs-docs]
1162    ///
1163    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withCalendar
1164    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/withCalendar
1165    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.with_calendar
1166    fn with_calendar(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1167        let object = this.as_object();
1168        let dt = object
1169            .as_ref()
1170            .and_then(JsObject::downcast_ref::<Self>)
1171            .ok_or_else(|| {
1172                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1173            })?;
1174
1175        let calendar = to_temporal_calendar_identifier(args.get_or_undefined(0))?;
1176
1177        create_temporal_datetime(dt.inner.with_calendar(calendar), None, context).map(Into::into)
1178    }
1179
1180    /// 5.3.28 `Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )`
1181    ///
1182    /// More information:
1183    ///
1184    /// - [ECMAScript Temporal proposal][spec]
1185    /// - [MDN reference][mdn]
1186    /// - [`temporal_rs` documentation][temporal_rs-docs]
1187    ///
1188    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.add
1189    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/add
1190    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.add
1191    fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1192        // 1. Let temporalDate be the this value.
1193        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
1194        let object = this.as_object();
1195        let dt = object
1196            .as_ref()
1197            .and_then(JsObject::downcast_ref::<Self>)
1198            .ok_or_else(|| {
1199                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1200            })?;
1201
1202        // 3. Let duration be ? ToTemporalDuration(temporalDurationLike).
1203        let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;
1204
1205        // 4. Set options to ? GetOptionsObject(options).
1206        let options = get_options_object(args.get_or_undefined(1))?;
1207        let overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1208
1209        // 5. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).
1210        // 6. Return ? AddDate(calendarRec, temporalDate, duration, options).
1211        create_temporal_datetime(dt.inner.add(&duration, overflow)?, None, context).map(Into::into)
1212    }
1213
1214    /// 5.3.29 `Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options ] )`
1215    ///
1216    /// More information:
1217    ///
1218    /// - [ECMAScript Temporal proposal][spec]
1219    /// - [MDN reference][mdn]
1220    /// - [`temporal_rs` documentation][temporal_rs-docs]
1221    ///
1222    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.subtract
1223    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/subtract
1224    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.subtract
1225    fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1226        // 1. Let temporalDate be the this value.
1227        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
1228        let object = this.as_object();
1229        let dt = object
1230            .as_ref()
1231            .and_then(JsObject::downcast_ref::<Self>)
1232            .ok_or_else(|| {
1233                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1234            })?;
1235
1236        // 3. Let duration be ? ToTemporalDuration(temporalDurationLike).
1237        let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;
1238
1239        // 4. Set options to ? GetOptionsObject(options).
1240        let options = get_options_object(args.get_or_undefined(1))?;
1241        let overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1242
1243        // 5. Let negatedDuration be CreateNegatedTemporalDuration(duration).
1244        // 6. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).
1245        // 7. Return ? AddDate(calendarRec, temporalDate, negatedDuration, options).
1246        create_temporal_datetime(dt.inner.subtract(&duration, overflow)?, None, context)
1247            .map(Into::into)
1248    }
1249
1250    /// 5.3.30 `Temporal.PlainDateTime.prototype.until ( other [ , options ] )`
1251    ///
1252    /// More information:
1253    ///
1254    /// - [ECMAScript Temporal proposal][spec]
1255    /// - [MDN reference][mdn]
1256    /// - [`temporal_rs` documentation][temporal_rs-docs]
1257    ///
1258    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.until
1259    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/until
1260    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.until
1261    fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1262        let object = this.as_object();
1263        let dt = object
1264            .as_ref()
1265            .and_then(JsObject::downcast_ref::<Self>)
1266            .ok_or_else(|| {
1267                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1268            })?;
1269
1270        let other = to_temporal_datetime(args.get_or_undefined(0), None, context)?;
1271
1272        let options = get_options_object(args.get_or_undefined(1))?;
1273        let settings = get_difference_settings(&options, context)?;
1274
1275        create_temporal_duration(dt.inner.until(&other, settings)?, None, context).map(Into::into)
1276    }
1277
1278    /// 5.3.31 `Temporal.PlainDateTime.prototype.since ( other [ , options ] )`
1279    ///
1280    /// More information:
1281    ///
1282    /// - [ECMAScript Temporal proposal][spec]
1283    /// - [MDN reference][mdn]
1284    /// - [`temporal_rs` documentation][temporal_rs-docs]
1285    ///
1286    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.since
1287    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/since
1288    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.since
1289    fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1290        let object = this.as_object();
1291        let dt = object
1292            .as_ref()
1293            .and_then(JsObject::downcast_ref::<Self>)
1294            .ok_or_else(|| {
1295                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1296            })?;
1297
1298        let other = to_temporal_datetime(args.get_or_undefined(0), None, context)?;
1299
1300        let options = get_options_object(args.get_or_undefined(1))?;
1301        let settings = get_difference_settings(&options, context)?;
1302
1303        create_temporal_duration(dt.inner.since(&other, settings)?, None, context).map(Into::into)
1304    }
1305
1306    /// 5.3.32 Temporal.PlainDateTime.prototype.round ( roundTo )
1307    ///
1308    /// More information:
1309    ///
1310    /// - [ECMAScript Temporal proposal][spec]
1311    /// - [MDN reference][mdn]
1312    /// - [`temporal_rs` documentation][temporal_rs-docs]
1313    ///
1314    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round
1315    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/round
1316    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.round
1317    fn round(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1318        let object = this.as_object();
1319        let dt = object
1320            .as_ref()
1321            .and_then(JsObject::downcast_ref::<Self>)
1322            .ok_or_else(|| {
1323                JsNativeError::typ().with_message("the this object must be a PlainTime object.")
1324            })?;
1325
1326        let round_to = match args.first().map(JsValue::variant) {
1327            // 3. If roundTo is undefined, then
1328            None | Some(JsVariant::Undefined) => {
1329                return Err(JsNativeError::typ()
1330                    .with_message("roundTo cannot be undefined.")
1331                    .into());
1332            }
1333            // 4. If Type(roundTo) is String, then
1334            Some(JsVariant::String(rt)) => {
1335                // a. Let paramString be roundTo.
1336                let param_string = rt.clone();
1337                // b. Set roundTo to OrdinaryObjectCreate(null).
1338                let new_round_to = JsObject::with_null_proto();
1339                // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
1340                new_round_to.create_data_property_or_throw(
1341                    js_string!("smallestUnit"),
1342                    param_string,
1343                    context,
1344                )?;
1345                new_round_to
1346            }
1347            // 5. Else,
1348            Some(round_to) => {
1349                // a. Set roundTo to ? GetOptionsObject(roundTo).
1350                get_options_object(&JsValue::from(round_to))?
1351            }
1352        };
1353
1354        let mut options = RoundingOptions::default();
1355
1356        options.increment =
1357            get_option::<RoundingIncrement>(&round_to, js_string!("roundingIncrement"), context)?;
1358
1359        // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
1360        options.rounding_mode =
1361            get_option::<RoundingMode>(&round_to, js_string!("roundingMode"), context)?;
1362
1363        // 9. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", TIME, REQUIRED, undefined).
1364        options.smallest_unit = get_temporal_unit(
1365            &round_to,
1366            js_string!("smallestUnit"),
1367            TemporalUnitGroup::Time,
1368            Some(vec![Unit::Day]),
1369            context,
1370        )?;
1371
1372        create_temporal_datetime(dt.inner().round(options)?, None, context).map(Into::into)
1373    }
1374
1375    /// 5.3.33 Temporal.PlainDateTime.prototype.equals ( other )
1376    ///
1377    /// More information:
1378    ///
1379    /// - [ECMAScript Temporal proposal][spec]
1380    /// - [MDN reference][mdn]
1381    /// - [`temporal_rs` documentation][temporal_rs-docs]
1382    ///
1383    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.equals
1384    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/equals
1385    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#impl-Eq-for-PlainDateTime
1386    fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1387        // 1. Let dateTime be the this value.
1388        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
1389        let object = this.as_object();
1390        let dt = object
1391            .as_ref()
1392            .and_then(JsObject::downcast_ref::<Self>)
1393            .ok_or_else(|| {
1394                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1395            })?;
1396
1397        // 3. Set other to ? ToTemporalDateTime(other).
1398        let other = to_temporal_datetime(args.get_or_undefined(0), None, context)?;
1399
1400        // 4. Let result be CompareISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]],
1401        // dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]],
1402        // dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]],
1403        // dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]],
1404        // other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]],
1405        // other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
1406        // 5. If result is not 0, return false.
1407        // 6. Return ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]).
1408        Ok((dt.inner == other).into())
1409    }
1410
1411    /// 5.3.34 `Temporal.PlainDateTime.prototype.toString ( [ options ] )`
1412    ///
1413    /// More information:
1414    ///
1415    /// - [ECMAScript Temporal proposal][spec]
1416    /// - [MDN reference][mdn]
1417    /// - [`temporal_rs` documentation][temporal_rs-docs]
1418    ///
1419    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with
1420    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with
1421    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.to_ixdtf_string
1422    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1423        let object = this.as_object();
1424        let dt = object
1425            .as_ref()
1426            .and_then(JsObject::downcast_ref::<Self>)
1427            .ok_or_else(|| {
1428                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1429            })?;
1430
1431        let options = get_options_object(args.get_or_undefined(0))?;
1432
1433        let show_calendar =
1434            get_option::<DisplayCalendar>(&options, js_string!("calendarName"), context)?
1435                .unwrap_or(DisplayCalendar::Auto);
1436        let precision = get_digits_option(&options, context)?;
1437        let rounding_mode =
1438            get_option::<RoundingMode>(&options, js_string!("roundingMode"), context)?;
1439        let smallest_unit = get_option::<Unit>(&options, js_string!("smallestUnit"), context)?;
1440
1441        let ixdtf = dt.inner.to_ixdtf_string(
1442            ToStringRoundingOptions {
1443                precision,
1444                smallest_unit,
1445                rounding_mode,
1446            },
1447            show_calendar,
1448        )?;
1449        Ok(JsString::from(ixdtf).into())
1450    }
1451
1452    /// 5.3.35 `Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )`
1453    ///
1454    /// More information:
1455    ///
1456    /// - [ECMAScript Temporal proposal][spec]
1457    /// - [MDN reference][mdn]
1458    ///
1459    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with
1460    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with
1461    fn to_locale_string(this: &JsValue, _args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1462        // TODO: Update for ECMA-402 compliance
1463        let object = this.as_object();
1464        let dt = object
1465            .as_ref()
1466            .and_then(JsObject::downcast_ref::<Self>)
1467            .ok_or_else(|| {
1468                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1469            })?;
1470
1471        let ixdtf = dt
1472            .inner
1473            .to_ixdtf_string(ToStringRoundingOptions::default(), DisplayCalendar::Auto)?;
1474        Ok(JsString::from(ixdtf).into())
1475    }
1476
1477    /// 5.3.36 `Temporal.PlainDateTime.prototype.toJSON ( )`
1478    ///
1479    /// More information:
1480    ///
1481    /// - [ECMAScript Temporal proposal][spec]
1482    /// - [MDN reference][mdn]
1483    ///
1484    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with
1485    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with
1486    fn to_json(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1487        let object = this.as_object();
1488        let dt = object
1489            .as_ref()
1490            .and_then(JsObject::downcast_ref::<Self>)
1491            .ok_or_else(|| {
1492                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1493            })?;
1494
1495        let ixdtf = dt
1496            .inner
1497            .to_ixdtf_string(ToStringRoundingOptions::default(), DisplayCalendar::Auto)?;
1498        Ok(JsString::from(ixdtf).into())
1499    }
1500
1501    /// 5.3.37 `Temporal.PlainDateTime.prototype.valueOf ( )`
1502    ///
1503    /// More information:
1504    ///
1505    /// - [ECMAScript Temporal proposal][spec]
1506    /// - [MDN reference][mdn]
1507    ///
1508    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with
1509    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with
1510    pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1511        Err(JsNativeError::typ()
1512            .with_message("`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`")
1513            .into())
1514    }
1515
1516    /// 5.3.38 `Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ , options ] )`
1517    ///
1518    /// More information:
1519    ///
1520    /// - [ECMAScript Temporal proposal][spec]
1521    /// - [MDN reference][mdn]
1522    /// - [`temporal_rs` documentation][temporal_rs-docs]
1523    ///
1524    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tozoneddatetime
1525    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/toZonedDateTime
1526    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.to_zoned_date_time
1527    fn to_zoned_date_time(
1528        this: &JsValue,
1529        args: &[JsValue],
1530        context: &mut Context,
1531    ) -> JsResult<JsValue> {
1532        // 1. Let dateTime be the this value.
1533        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
1534        let object = this.as_object();
1535        let dt = object
1536            .as_ref()
1537            .and_then(JsObject::downcast_ref::<Self>)
1538            .ok_or_else(|| {
1539                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1540            })?;
1541        // 3. Let timeZone be ? ToTemporalTimeZoneIdentifier(temporalTimeZoneLike).
1542        let timezone = to_temporal_timezone_identifier(args.get_or_undefined(0), context)?;
1543        // 4. Let resolvedOptions be ? GetOptionsObject(options).
1544        let options = get_options_object(args.get_or_undefined(1))?;
1545        // 5. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).
1546        let disambiguation =
1547            get_option::<Disambiguation>(&options, js_string!("disambiguation"), context)?
1548                .unwrap_or_default();
1549
1550        // 6. Let epochNs be ? GetEpochNanosecondsFor(timeZone, dateTime.[[ISODateTime]], disambiguation).
1551        // 7. Return ! CreateTemporalZonedDateTime(epochNs, timeZone, dateTime.[[Calendar]]).
1552
1553        let result = dt.inner.to_zoned_date_time_with_provider(
1554            timezone,
1555            disambiguation,
1556            context.tz_provider(),
1557        )?;
1558        create_temporal_zoneddatetime(result, None, context).map(Into::into)
1559    }
1560
1561    /// 5.3.39 `Temporal.PlainDateTime.prototype.toPlainDate ( )`
1562    ///
1563    /// More information:
1564    ///
1565    /// - [ECMAScript Temporal proposal][spec]
1566    /// - [MDN reference][mdn]
1567    /// - [`temporal_rs` documentation][temporal_rs-docs]
1568    ///
1569    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplaindate
1570    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/toPlainDate
1571    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.to_plain_date
1572    fn to_plain_date(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1573        let object = this.as_object();
1574        let dt = object
1575            .as_ref()
1576            .and_then(JsObject::downcast_ref::<Self>)
1577            .ok_or_else(|| {
1578                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1579            })?;
1580
1581        let result = dt.inner.to_plain_date();
1582        create_temporal_date(result, None, context).map(Into::into)
1583    }
1584
1585    /// 5.3.40 `Temporal.PlainDateTime.prototype.toPlainTime ( )`
1586    ///
1587    /// More information:
1588    ///
1589    /// - [ECMAScript Temporal proposal][spec]
1590    /// - [MDN reference][mdn]
1591    /// - [`temporal_rs` documentation][temporal_rs-docs]
1592    ///
1593    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplaintime
1594    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/toPlainTime
1595    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.to_plain_time
1596    fn to_plain_time(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1597        let object = this.as_object();
1598        let dt = object
1599            .as_ref()
1600            .and_then(JsObject::downcast_ref::<Self>)
1601            .ok_or_else(|| {
1602                JsNativeError::typ().with_message("the this object must be a PlainDateTime object.")
1603            })?;
1604
1605        let result = dt.inner.to_plain_time();
1606        create_temporal_time(result, None, context).map(Into::into)
1607    }
1608}
1609
1610// ==== PlainDateTime Abstract Operations ====
1611
1612pub(crate) fn create_temporal_datetime(
1613    inner: InnerDateTime,
1614    new_target: Option<&JsValue>,
1615    context: &mut Context,
1616) -> JsResult<JsObject> {
1617    // NOTE(nekevss): The below validations should be upheld with the creation of `InnerDateTime`.
1618    // 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
1619    // 2. If IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
1620    // 3. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond) is false, then
1621    // a. Throw a RangeError exception.
1622
1623    // 4. If newTarget is not present, set newTarget to %Temporal.PlainDateTime%.
1624    let new_target = if let Some(new_target) = new_target {
1625        new_target.clone()
1626    } else {
1627        context
1628            .realm()
1629            .intrinsics()
1630            .constructors()
1631            .plain_date_time()
1632            .constructor()
1633            .into()
1634    };
1635
1636    // 5. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDateTime.prototype%", « [[InitializedTemporalDateTime]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »).
1637    let prototype = get_prototype_from_constructor(
1638        &new_target,
1639        StandardConstructors::plain_date_time,
1640        context,
1641    )?;
1642
1643    // 6. Set object.[[ISOYear]] to isoYear.
1644    // 7. Set object.[[ISOMonth]] to isoMonth.
1645    // 8. Set object.[[ISODay]] to isoDay.
1646    // 9. Set object.[[ISOHour]] to hour.
1647    // 10. Set object.[[ISOMinute]] to minute.
1648    // 11. Set object.[[ISOSecond]] to second.
1649    // 12. Set object.[[ISOMillisecond]] to millisecond.
1650    // 13. Set object.[[ISOMicrosecond]] to microsecond.
1651    // 14. Set object.[[ISONanosecond]] to nanosecond.
1652    // 15. Set object.[[Calendar]] to calendar.
1653    let obj = JsObject::from_proto_and_data(prototype, PlainDateTime::new(inner));
1654
1655    // 16. Return object.
1656    Ok(obj)
1657}
1658
1659pub(crate) fn to_temporal_datetime(
1660    value: &JsValue,
1661    options: Option<JsValue>,
1662    context: &mut Context,
1663) -> JsResult<InnerDateTime> {
1664    // 1. If options is not present, set options to undefined.
1665    // 2. Let resolvedOptions be ? SnapshotOwnProperties(! GetOptionsObject(options), null).
1666    // 3. If item is an Object, then
1667    if let Some(object) = value.as_object() {
1668        // a. If item has an [[InitializedTemporalDateTime]] internal slot, then
1669        if let Some(dt) = object.downcast_ref::<PlainDateTime>() {
1670            // i. Return item.
1671            return Ok(dt.inner.clone());
1672        // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
1673        } else if let Some(zdt) = object.downcast_ref::<ZonedDateTime>() {
1674            // i. Perform ? GetTemporalOverflowOption(resolvedOptions).
1675            let options = get_options_object(&options.unwrap_or_default())?;
1676            let _ = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1677            // ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
1678            // iii. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(item.[[TimeZone]], « get-offset-nanoseconds-for »).
1679            // iv. Return ? GetPlainDateTimeFor(timeZoneRec, instant, item.[[Calendar]]).
1680            return Ok(zdt.inner.to_plain_date_time());
1681        // c. If item has an [[InitializedTemporalDate]] internal slot, then
1682        } else if let Some(date) = object.downcast_ref::<PlainDate>() {
1683            // i. Perform ? GetTemporalOverflowOption(resolvedOptions).
1684            let options = get_options_object(&options.unwrap_or_default())?;
1685            let _ = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1686            // ii. Return ? CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], 0, 0, 0, 0, 0, 0, item.[[Calendar]]).
1687            return Ok(date.inner.to_plain_date_time(None)?);
1688        }
1689
1690        // d. Let calendar be ? GetTemporalCalendarSlotValueWithISODefault(item).
1691        // e. Let calendarRec be ? CreateCalendarMethodsRecord(calendar, « date-from-fields, fields »).
1692        // f. Let fields be ? PrepareCalendarFields(calendarRec, item, « "day", "month",
1693        // "monthCode", "year" », « "hour", "microsecond", "millisecond", "minute",
1694        // "nanosecond", "second" », «»)
1695        // TODO: Move validation to `temporal_rs`.
1696        let partial_dt = to_partial_datetime(&object, context)?;
1697        let resolved_options = get_options_object(&options.unwrap_or_default())?;
1698        // g. Let result be ? InterpretTemporalDateTimeFields(calendarRec, fields, resolvedOptions).
1699        let overflow = get_option::<Overflow>(&resolved_options, js_string!("overflow"), context)?;
1700        return InnerDateTime::from_partial(partial_dt, overflow).map_err(Into::into);
1701    }
1702    // 4. Else,
1703    //     a. If item is not a String, throw a TypeError exception.
1704    let Some(string) = value.as_string() else {
1705        return Err(JsNativeError::typ()
1706            .with_message("Cannot convert unrecognized value to PlainDateTime.")
1707            .into());
1708    };
1709    // b. Let result be ? ParseTemporalDateTimeString(item).
1710    // c. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
1711    // d. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]],
1712    // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
1713    // e. Let calendar be result.[[Calendar]].
1714    // f. If calendar is empty, set calendar to "iso8601".
1715    // g. If IsBuiltinCalendar(calendar) is false, throw a RangeError exception.
1716    // h. Set calendar to CanonicalizeUValue("ca", calendar).
1717    let date = string.to_std_string_escaped().parse::<InnerDateTime>()?;
1718    // i. Perform ? GetTemporalOverflowOption(resolvedOptions).
1719    let resolved_options = get_options_object(&options.unwrap_or_default())?;
1720    let _ = get_option::<Overflow>(&resolved_options, js_string!("overflow"), context)?;
1721    // 5. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]],
1722    // result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]],
1723    // result.[[Microsecond]], result.[[Nanosecond]], calendar).
1724    Ok(date)
1725}
1726
1727fn to_partial_datetime(
1728    partial_object: &JsObject,
1729    context: &mut Context,
1730) -> JsResult<PartialDateTime> {
1731    let calendar = get_temporal_calendar_slot_value_with_default(partial_object, context)?;
1732    let fields = to_date_time_fields(partial_object, &calendar, context)?;
1733    Ok(PartialDateTime { fields, calendar })
1734}
1735
1736fn to_date_time_fields(
1737    partial_object: &JsObject,
1738    calendar: &Calendar,
1739    context: &mut Context,
1740) -> JsResult<DateTimeFields> {
1741    let day = partial_object
1742        .get(js_string!("day"), context)?
1743        .map(|v| {
1744            let finite = v.to_finitef64(context)?;
1745            finite
1746                .as_positive_integer_with_truncation()
1747                .map_err(JsError::from)
1748        })
1749        .transpose()?;
1750    let hour = partial_object
1751        .get(js_string!("hour"), context)?
1752        .map(|v| {
1753            let finite = v.to_finitef64(context)?;
1754            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())
1755        })
1756        .transpose()?;
1757    // TODO: `temporal_rs` needs a `has_era` method
1758    let has_no_era = calendar.kind() == AnyCalendarKind::Iso
1759        || calendar.kind() == AnyCalendarKind::Chinese
1760        || calendar.kind() == AnyCalendarKind::Dangi;
1761    let (era, era_year) = if has_no_era {
1762        (None, None)
1763    } else {
1764        let era = partial_object
1765            .get(js_string!("era"), context)?
1766            .map(|v| {
1767                let v = v.to_primitive(context, crate::value::PreferredType::String)?;
1768                let Some(era) = v.as_string() else {
1769                    return Err(JsError::from(
1770                        JsNativeError::typ()
1771                            .with_message("The monthCode field value must be a string."),
1772                    ));
1773                };
1774                // TODO: double check if an invalid monthCode is a range or type error.
1775                TinyAsciiStr::<19>::try_from_str(&era.to_std_string_escaped())
1776                    .map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string())))
1777            })
1778            .transpose()?;
1779        let era_year = partial_object
1780            .get(js_string!("eraYear"), context)?
1781            .map(|v| {
1782                let finite = v.to_finitef64(context)?;
1783                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())
1784            })
1785            .transpose()?;
1786        (era, era_year)
1787    };
1788    let microsecond = partial_object
1789        .get(js_string!("microsecond"), context)?
1790        .map(|v| {
1791            let finite = v.to_finitef64(context)?;
1792            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())
1793        })
1794        .transpose()?;
1795
1796    let millisecond = partial_object
1797        .get(js_string!("millisecond"), context)?
1798        .map(|v| {
1799            let finite = v.to_finitef64(context)?;
1800            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())
1801        })
1802        .transpose()?;
1803
1804    let minute = partial_object
1805        .get(js_string!("minute"), context)?
1806        .map(|v| {
1807            let finite = v.to_finitef64(context)?;
1808            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())
1809        })
1810        .transpose()?;
1811
1812    let month = partial_object
1813        .get(js_string!("month"), context)?
1814        .map(|v| {
1815            let finite = v.to_finitef64(context)?;
1816            finite
1817                .as_positive_integer_with_truncation()
1818                .map_err(JsError::from)
1819        })
1820        .transpose()?;
1821
1822    let month_code = partial_object
1823        .get(js_string!("monthCode"), context)?
1824        .map(|v| {
1825            let v = v.to_primitive(context, crate::value::PreferredType::String)?;
1826            let Some(month_code) = v.as_string() else {
1827                return Err(JsNativeError::typ()
1828                    .with_message("The monthCode field value must be a string.")
1829                    .into());
1830            };
1831            MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(JsError::from)
1832        })
1833        .transpose()?;
1834
1835    let nanosecond = partial_object
1836        .get(js_string!("nanosecond"), context)?
1837        .map(|v| {
1838            let finite = v.to_finitef64(context)?;
1839            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())
1840        })
1841        .transpose()?;
1842
1843    let second = partial_object
1844        .get(js_string!("second"), context)?
1845        .map(|v| {
1846            let finite = v.to_finitef64(context)?;
1847            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())
1848        })
1849        .transpose()?;
1850
1851    let year = partial_object
1852        .get(js_string!("year"), context)?
1853        .map(|v| {
1854            let finite = v.to_finitef64(context)?;
1855            Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())
1856        })
1857        .transpose()?;
1858
1859    let calendar_fields = CalendarFields {
1860        year,
1861        month,
1862        month_code,
1863        day,
1864        era,
1865        era_year,
1866    };
1867    let time = PartialTime {
1868        hour,
1869        minute,
1870        second,
1871        millisecond,
1872        microsecond,
1873        nanosecond,
1874    };
1875
1876    Ok(DateTimeFields {
1877        calendar_fields,
1878        time,
1879    })
1880}