Skip to main content

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