Skip to main content

boa_engine/builtins/temporal/zoneddatetime/
mod.rs

1//! Boa's implementation of the ECMAScript `Temporal.ZonedDateTime` built-in object
2
3use std::str::FromStr;
4
5use crate::{
6    Context, JsArgs, JsBigInt, JsData, JsError, JsNativeError, JsObject, JsResult, JsString,
7    JsSymbol, JsValue, JsVariant,
8    builtins::{
9        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
10        options::{get_option, get_options_object},
11        temporal::{calendar::to_temporal_calendar_identifier, options::get_digits_option},
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, PreferredType},
20};
21use boa_gc::{Finalize, Trace};
22use cow_utils::CowUtils;
23use icu_calendar::AnyCalendarKind;
24use temporal_rs::{
25    Calendar, MonthCode, TimeZone, TinyAsciiStr, UtcOffset, ZonedDateTime as ZonedDateTimeInner,
26    fields::{CalendarFields, ZonedDateTimeFields},
27    options::{
28        Disambiguation, DisplayCalendar, DisplayOffset, DisplayTimeZone, OffsetDisambiguation,
29        Overflow, RoundingIncrement, RoundingMode, RoundingOptions, ToStringRoundingOptions, Unit,
30    },
31    parsed_intermediates::ParsedZonedDateTime,
32    partial::{PartialTime, PartialZonedDateTime},
33    provider::TransitionDirection,
34};
35
36use super::{
37    calendar::get_temporal_calendar_slot_value_with_default,
38    create_temporal_date, create_temporal_datetime, create_temporal_duration,
39    create_temporal_instant, create_temporal_time, is_partial_temporal_object,
40    options::{TemporalUnitGroup, get_difference_settings, get_temporal_unit},
41    to_temporal_duration, to_temporal_time,
42};
43
44/// The `Temporal.ZonedDateTime` built-in implementation
45///
46/// More information:
47///
48/// - [ECMAScript Temporal proposal][spec]
49/// - [MDN reference][mdn]
50/// - [`temporal_rs` documentation][temporal_rs-docs]
51///
52/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-objects
53/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime
54/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html
55#[derive(Debug, Clone, Trace, Finalize, JsData)]
56#[boa_gc(unsafe_empty_trace)] // Safety: Does not contain any traceable fields.
57pub struct ZonedDateTime {
58    pub(crate) inner: Box<ZonedDateTimeInner>,
59}
60
61impl ZonedDateTime {
62    pub(crate) fn new(inner: ZonedDateTimeInner) -> Self {
63        Self {
64            inner: Box::new(inner),
65        }
66    }
67}
68
69impl BuiltInObject for ZonedDateTime {
70    const NAME: JsString = StaticJsStrings::ZONED_DT_NAME;
71}
72
73impl IntrinsicObject for ZonedDateTime {
74    fn init(realm: &Realm) {
75        let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id)
76            .name(js_string!("get calendarId"))
77            .build();
78
79        let get_timezone_id = BuiltInBuilder::callable(realm, Self::get_timezone_id)
80            .name(js_string!("get timeZoneId"))
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_epoch_milliseconds = BuiltInBuilder::callable(realm, Self::get_epoch_milliseconds)
132            .name(js_string!("get epochMilliseconds"))
133            .build();
134
135        let get_epoch_nanoseconds = BuiltInBuilder::callable(realm, Self::get_epoch_nanoseconds)
136            .name(js_string!("get epochNanoseconds"))
137            .build();
138
139        let get_day_of_week = BuiltInBuilder::callable(realm, Self::get_day_of_week)
140            .name(js_string!("get dayOfWeek"))
141            .build();
142
143        let get_day_of_year = BuiltInBuilder::callable(realm, Self::get_day_of_year)
144            .name(js_string!("get dayOfYear"))
145            .build();
146
147        let get_week_of_year = BuiltInBuilder::callable(realm, Self::get_week_of_year)
148            .name(js_string!("get weekOfYear"))
149            .build();
150
151        let get_hours_in_day = BuiltInBuilder::callable(realm, Self::get_hours_in_day)
152            .name(js_string!("get daysInWeek"))
153            .build();
154
155        let get_year_of_week = BuiltInBuilder::callable(realm, Self::get_year_of_week)
156            .name(js_string!("get yearOfWeek"))
157            .build();
158
159        let get_days_in_week = BuiltInBuilder::callable(realm, Self::get_days_in_week)
160            .name(js_string!("get daysInWeek"))
161            .build();
162
163        let get_days_in_month = BuiltInBuilder::callable(realm, Self::get_days_in_month)
164            .name(js_string!("get daysInMonth"))
165            .build();
166
167        let get_days_in_year = BuiltInBuilder::callable(realm, Self::get_days_in_year)
168            .name(js_string!("get daysInYear"))
169            .build();
170
171        let get_months_in_year = BuiltInBuilder::callable(realm, Self::get_months_in_year)
172            .name(js_string!("get monthsInYear"))
173            .build();
174
175        let get_in_leap_year = BuiltInBuilder::callable(realm, Self::get_in_leap_year)
176            .name(js_string!("get inLeapYear"))
177            .build();
178
179        let get_offset_nanos = BuiltInBuilder::callable(realm, Self::get_offset_nanoseconds)
180            .name(js_string!("get offsetNanoseconds"))
181            .build();
182
183        let get_offset = BuiltInBuilder::callable(realm, Self::get_offset)
184            .name(js_string!("get offset"))
185            .build();
186
187        BuiltInBuilder::from_standard_constructor::<Self>(realm)
188            .property(
189                JsSymbol::to_string_tag(),
190                StaticJsStrings::ZONED_DT_TAG,
191                Attribute::CONFIGURABLE,
192            )
193            .accessor(
194                js_string!("calendarId"),
195                Some(get_calendar_id),
196                None,
197                Attribute::CONFIGURABLE,
198            )
199            .accessor(
200                js_string!("timeZoneId"),
201                Some(get_timezone_id),
202                None,
203                Attribute::CONFIGURABLE,
204            )
205            .accessor(
206                js_string!("era"),
207                Some(get_era),
208                None,
209                Attribute::CONFIGURABLE,
210            )
211            .accessor(
212                js_string!("eraYear"),
213                Some(get_era_year),
214                None,
215                Attribute::CONFIGURABLE,
216            )
217            .accessor(
218                js_string!("year"),
219                Some(get_year),
220                None,
221                Attribute::CONFIGURABLE,
222            )
223            .accessor(
224                js_string!("month"),
225                Some(get_month),
226                None,
227                Attribute::CONFIGURABLE,
228            )
229            .accessor(
230                js_string!("monthCode"),
231                Some(get_month_code),
232                None,
233                Attribute::CONFIGURABLE,
234            )
235            .accessor(
236                js_string!("day"),
237                Some(get_day),
238                None,
239                Attribute::CONFIGURABLE,
240            )
241            .accessor(
242                js_string!("hour"),
243                Some(get_hour),
244                None,
245                Attribute::CONFIGURABLE,
246            )
247            .accessor(
248                js_string!("minute"),
249                Some(get_minute),
250                None,
251                Attribute::CONFIGURABLE,
252            )
253            .accessor(
254                js_string!("second"),
255                Some(get_second),
256                None,
257                Attribute::CONFIGURABLE,
258            )
259            .accessor(
260                js_string!("millisecond"),
261                Some(get_millisecond),
262                None,
263                Attribute::CONFIGURABLE,
264            )
265            .accessor(
266                js_string!("microsecond"),
267                Some(get_microsecond),
268                None,
269                Attribute::CONFIGURABLE,
270            )
271            .accessor(
272                js_string!("nanosecond"),
273                Some(get_nanosecond),
274                None,
275                Attribute::CONFIGURABLE,
276            )
277            .accessor(
278                js_string!("epochMilliseconds"),
279                Some(get_epoch_milliseconds),
280                None,
281                Attribute::CONFIGURABLE,
282            )
283            .accessor(
284                js_string!("epochNanoseconds"),
285                Some(get_epoch_nanoseconds),
286                None,
287                Attribute::CONFIGURABLE,
288            )
289            .accessor(
290                js_string!("dayOfWeek"),
291                Some(get_day_of_week),
292                None,
293                Attribute::CONFIGURABLE,
294            )
295            .accessor(
296                js_string!("dayOfYear"),
297                Some(get_day_of_year),
298                None,
299                Attribute::CONFIGURABLE,
300            )
301            .accessor(
302                js_string!("weekOfYear"),
303                Some(get_week_of_year),
304                None,
305                Attribute::CONFIGURABLE,
306            )
307            .accessor(
308                js_string!("yearOfWeek"),
309                Some(get_year_of_week),
310                None,
311                Attribute::CONFIGURABLE,
312            )
313            .accessor(
314                js_string!("hoursInDay"),
315                Some(get_hours_in_day),
316                None,
317                Attribute::CONFIGURABLE,
318            )
319            .accessor(
320                js_string!("daysInWeek"),
321                Some(get_days_in_week),
322                None,
323                Attribute::CONFIGURABLE,
324            )
325            .accessor(
326                js_string!("daysInMonth"),
327                Some(get_days_in_month),
328                None,
329                Attribute::CONFIGURABLE,
330            )
331            .accessor(
332                js_string!("daysInYear"),
333                Some(get_days_in_year),
334                None,
335                Attribute::CONFIGURABLE,
336            )
337            .accessor(
338                js_string!("monthsInYear"),
339                Some(get_months_in_year),
340                None,
341                Attribute::CONFIGURABLE,
342            )
343            .accessor(
344                js_string!("inLeapYear"),
345                Some(get_in_leap_year),
346                None,
347                Attribute::CONFIGURABLE,
348            )
349            .accessor(
350                js_string!("offsetNanoseconds"),
351                Some(get_offset_nanos),
352                None,
353                Attribute::CONFIGURABLE,
354            )
355            .accessor(
356                js_string!("offset"),
357                Some(get_offset),
358                None,
359                Attribute::CONFIGURABLE,
360            )
361            .static_method(Self::from, js_string!("from"), 1)
362            .static_method(Self::compare, js_string!("compare"), 2)
363            .method(Self::with, js_string!("with"), 1)
364            .method(Self::with_plain_time, js_string!("withPlainTime"), 0)
365            .method(Self::with_timezone, js_string!("withTimeZone"), 1)
366            .method(Self::with_calendar, js_string!("withCalendar"), 1)
367            .method(Self::add, js_string!("add"), 1)
368            .method(Self::subtract, js_string!("subtract"), 1)
369            .method(Self::until, js_string!("until"), 1)
370            .method(Self::since, js_string!("since"), 1)
371            .method(Self::round, js_string!("round"), 1)
372            .method(Self::equals, js_string!("equals"), 1)
373            .method(Self::to_string, js_string!("toString"), 0)
374            .method(Self::to_locale_string, js_string!("toLocaleString"), 0)
375            .method(Self::to_json, js_string!("toJSON"), 0)
376            .method(Self::value_of, js_string!("valueOf"), 0)
377            .method(Self::start_of_day, js_string!("startOfDay"), 0)
378            .method(
379                Self::get_time_zone_transition,
380                js_string!("getTimeZoneTransition"),
381                1,
382            )
383            .method(Self::to_instant, js_string!("toInstant"), 0)
384            .method(Self::to_plain_date, js_string!("toPlainDate"), 0)
385            .method(Self::to_plain_time, js_string!("toPlainTime"), 0)
386            .method(Self::to_plain_date_time, js_string!("toPlainDateTime"), 0)
387            .build();
388    }
389
390    fn get(intrinsics: &Intrinsics) -> JsObject {
391        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
392    }
393}
394
395impl BuiltInConstructor for ZonedDateTime {
396    const CONSTRUCTOR_ARGUMENTS: usize = 2;
397    const PROTOTYPE_STORAGE_SLOTS: usize = 77;
398    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;
399
400    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
401        StandardConstructors::zoned_date_time;
402
403    fn constructor(
404        new_target: &JsValue,
405        args: &[JsValue],
406        context: &mut Context,
407    ) -> JsResult<JsValue> {
408        // 1. If NewTarget is undefined, then
409        if new_target.is_undefined() {
410            // a. Throw a TypeError exception.
411            return Err(JsNativeError::typ()
412                .with_message("NewTarget cannot be undefined.")
413                .into());
414        }
415        //  2. Set epochNanoseconds to ? ToBigInt(epochNanoseconds).
416        //  3. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
417        let epoch_nanos = args.get_or_undefined(0).to_bigint(context)?;
418
419        //  4. If timeZone is not a String, throw a TypeError exception.
420        let Some(timezone_str) = args.get_or_undefined(1).as_string() else {
421            return Err(JsNativeError::typ()
422                .with_message("timeZone must be a string.")
423                .into());
424        };
425
426        //  5. Let timeZoneParse be ? ParseTimeZoneIdentifier(timeZone).
427        //  6. If timeZoneParse.[[OffsetMinutes]] is empty, then
428        // a. Let identifierRecord be GetAvailableNamezdtimeZoneIdentifier(timeZoneParse.[[Name]]).
429        // b. If identifierRecord is empty, throw a RangeError exception.
430        // c. Set timeZone to identifierRecord.[[Identifier]].
431        //  7. Else,
432        // a. Set timeZone to FormatOffsetTimeZoneIdentifier(timeZoneParse.[[OffsetMinutes]]).
433        let timezone = TimeZone::try_from_identifier_str_with_provider(
434            &timezone_str.to_std_string_escaped(),
435            context.timezone_provider(),
436        )?;
437
438        //  8. If calendar is undefined, set calendar to "iso8601".
439        //  9. If calendar is not a String, throw a TypeError exception.
440        //  10. Set calendar to ? CanonicalizeCalendar(calendar).
441        let calendar = args
442            .get_or_undefined(2)
443            .map(|s| {
444                s.as_string()
445                    .as_ref()
446                    .map(JsString::to_std_string_lossy)
447                    .ok_or_else(|| JsNativeError::typ().with_message("calendar must be a string."))
448            })
449            .transpose()?
450            .map(|s| Calendar::try_from_utf8(s.as_bytes()))
451            .transpose()?
452            .unwrap_or_default();
453
454        let inner = ZonedDateTimeInner::try_new_with_provider(
455            epoch_nanos.to_i128(),
456            timezone,
457            calendar,
458            context.timezone_provider(),
459        )?;
460
461        //  11. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar, NewTarget).
462        create_temporal_zoneddatetime(inner, Some(new_target), context).map(Into::into)
463    }
464}
465
466// ==== `ZonedDateTime` accessor property methods ====
467
468impl ZonedDateTime {
469    /// 6.3.3 get `Temporal.ZonedDateTime.prototype.calendarId`
470    ///
471    /// More information:
472    ///
473    /// - [ECMAScript Temporal proposal][spec]
474    /// - [MDN reference][mdn]
475    /// - [`temporal_rs` documentation][temporal_rs-docs]
476    ///
477    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.calendarid
478    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/calendarId
479    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.calendar
480    fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
481        let object = this.as_object();
482        let zdt = object
483            .as_ref()
484            .and_then(JsObject::downcast_ref::<Self>)
485            .ok_or_else(|| {
486                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
487            })?;
488
489        Ok(JsString::from(zdt.inner.calendar().identifier()).into())
490    }
491
492    /// 6.3.4 get `Temporal.ZonedDateTime.prototype.timeZoneId`
493    ///
494    /// More information:
495    ///
496    /// - [ECMAScript Temporal proposal][spec]
497    /// - [MDN reference][mdn]
498    /// - [`temporal_rs` documentation][temporal_rs-docs]
499    ///
500    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.timezoneid
501    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/timeZoneId
502    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.timezone
503    fn get_timezone_id(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
504        let object = this.as_object();
505        let zdt = object
506            .as_ref()
507            .and_then(JsObject::downcast_ref::<Self>)
508            .ok_or_else(|| {
509                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
510            })?;
511
512        Ok(JsString::from(
513            zdt.inner
514                .time_zone()
515                .identifier_with_provider(context.timezone_provider())?,
516        )
517        .into())
518    }
519
520    /// 6.3.5 get `Temporal.ZonedDateTime.prototype.era`
521    ///
522    /// More information:
523    ///
524    /// - [ECMAScript Temporal proposal][spec]
525    /// - [MDN reference][mdn]
526    /// - [`temporal_rs` documentation][temporal_rs-docs]
527    ///
528    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.era
529    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/era
530    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.era
531    fn get_era(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
532        let object = this.as_object();
533        let zdt = object
534            .as_ref()
535            .and_then(JsObject::downcast_ref::<Self>)
536            .ok_or_else(|| {
537                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
538            })?;
539
540        let era = zdt.inner.era();
541        Ok(era
542            .map(|tinystr| JsString::from(tinystr.cow_to_lowercase()))
543            .into_or_undefined())
544    }
545
546    /// 6.3.6 get `Temporal.ZonedDateTime.prototype.eraYear`
547    ///
548    /// More information:
549    ///
550    /// - [ECMAScript Temporal proposal][spec]
551    /// - [MDN reference][mdn]
552    /// - [`temporal_rs` documentation][temporal_rs-docs]
553    ///
554    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.erayear
555    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/eraYear
556    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.era_year
557    fn get_era_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
558        let object = this.as_object();
559        let zdt = object
560            .as_ref()
561            .and_then(JsObject::downcast_ref::<Self>)
562            .ok_or_else(|| {
563                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
564            })?;
565
566        Ok(zdt.inner.era_year().into_or_undefined())
567    }
568
569    /// 6.3.7 get `Temporal.ZonedDateTime.prototype.year`
570    ///
571    /// More information:
572    ///
573    /// - [ECMAScript Temporal proposal][spec]
574    /// - [MDN reference][mdn]
575    /// - [`temporal_rs` documentation][temporal_rs-docs]
576    ///
577    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.year
578    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/year
579    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.year
580    fn get_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
581        let object = this.as_object();
582        let zdt = object
583            .as_ref()
584            .and_then(JsObject::downcast_ref::<Self>)
585            .ok_or_else(|| {
586                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
587            })?;
588
589        Ok(zdt.inner.year().into())
590    }
591
592    /// 6.3.8 get `Temporal.ZonedDateTime.prototype.month`
593    ///
594    /// More information:
595    ///
596    /// - [ECMAScript Temporal proposal][spec]
597    /// - [MDN reference][mdn]
598    /// - [`temporal_rs` documentation][temporal_rs-docs]
599    ///
600    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.month
601    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/month
602    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.month
603    fn get_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
604        let object = this.as_object();
605        let zdt = object
606            .as_ref()
607            .and_then(JsObject::downcast_ref::<Self>)
608            .ok_or_else(|| {
609                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
610            })?;
611
612        Ok(zdt.inner.month().into())
613    }
614
615    /// 6.3.9 get `Temporal.ZonedDateTime.prototype.monthCode`
616    ///
617    /// More information:
618    ///
619    /// - [ECMAScript Temporal proposal][spec]
620    /// - [MDN reference][mdn]
621    /// - [`temporal_rs` documentation][temporal_rs-docs]
622    ///
623    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.monthcode
624    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/monthCode
625    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.month_code
626    fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
627        let object = this.as_object();
628        let zdt = object
629            .as_ref()
630            .and_then(JsObject::downcast_ref::<Self>)
631            .ok_or_else(|| {
632                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
633            })?;
634
635        Ok(JsString::from(zdt.inner.month_code().as_str()).into())
636    }
637
638    /// 6.3.10 get `Temporal.ZonedDateTime.prototype.day`
639    ///
640    /// More information:
641    ///
642    /// - [ECMAScript Temporal proposal][spec]
643    /// - [MDN reference][mdn]
644    /// - [`temporal_rs` documentation][temporal_rs-docs]
645    ///
646    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.day
647    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/day
648    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.day
649    fn get_day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
650        let object = this.as_object();
651        let zdt = object
652            .as_ref()
653            .and_then(JsObject::downcast_ref::<Self>)
654            .ok_or_else(|| {
655                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
656            })?;
657
658        Ok(zdt.inner.day().into())
659    }
660
661    /// 6.3.11 get `Temporal.ZonedDateTime.prototype.hour`
662    ///
663    /// More information:
664    ///
665    /// - [ECMAScript Temporal proposal][spec]
666    /// - [MDN reference][mdn]
667    /// - [`temporal_rs` documentation][temporal_rs-docs]
668    ///
669    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.hour
670    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/hour
671    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.hour
672    fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
673        let object = this.as_object();
674        let zdt = object
675            .as_ref()
676            .and_then(JsObject::downcast_ref::<Self>)
677            .ok_or_else(|| {
678                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
679            })?;
680
681        Ok(zdt.inner.hour().into())
682    }
683
684    /// 6.3.12 get `Temporal.ZonedDateTime.prototype.minute`
685    ///
686    /// More information:
687    ///
688    /// - [ECMAScript Temporal proposal][spec]
689    /// - [MDN reference][mdn]
690    /// - [`temporal_rs` documentation][temporal_rs-docs]
691    ///
692    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.minute
693    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/minute
694    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.minute
695    fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
696        let object = this.as_object();
697        let zdt = object
698            .as_ref()
699            .and_then(JsObject::downcast_ref::<Self>)
700            .ok_or_else(|| {
701                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
702            })?;
703
704        Ok(zdt.inner.minute().into())
705    }
706
707    /// 6.3.13 get `Temporal.ZonedDateTime.prototype.second`
708    ///
709    /// More information:
710    ///
711    /// - [ECMAScript Temporal proposal][spec]
712    /// - [MDN reference][mdn]
713    /// - [`temporal_rs` documentation][temporal_rs-docs]
714    ///
715    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.second
716    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/second
717    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.second
718    fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
719        let object = this.as_object();
720        let zdt = object
721            .as_ref()
722            .and_then(JsObject::downcast_ref::<Self>)
723            .ok_or_else(|| {
724                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
725            })?;
726
727        Ok(zdt.inner.second().into())
728    }
729
730    /// 6.3.14 get `Temporal.ZonedDateTime.prototype.millisecond`
731    ///
732    /// More information:
733    ///
734    /// - [ECMAScript Temporal proposal][spec]
735    /// - [MDN reference][mdn]
736    /// - [`temporal_rs` documentation][temporal_rs-docs]
737    ///
738    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.millisecond
739    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/millisecond
740    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.millisecond
741    fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
742        let object = this.as_object();
743        let zdt = object
744            .as_ref()
745            .and_then(JsObject::downcast_ref::<Self>)
746            .ok_or_else(|| {
747                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
748            })?;
749
750        Ok(zdt.inner.millisecond().into())
751    }
752
753    /// 6.3.15 get `Temporal.ZonedDateTime.prototype.microsecond`
754    ///
755    /// More information:
756    ///
757    /// - [ECMAScript Temporal proposal][spec]
758    /// - [MDN reference][mdn]
759    /// - [`temporal_rs` documentation][temporal_rs-docs]
760    ///
761    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.microsecond
762    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/microsecond
763    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.microsecond
764    fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
765        let object = this.as_object();
766        let zdt = object
767            .as_ref()
768            .and_then(JsObject::downcast_ref::<Self>)
769            .ok_or_else(|| {
770                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
771            })?;
772
773        Ok(zdt.inner.microsecond().into())
774    }
775
776    /// 6.3.16 get `Temporal.ZonedDateTime.prototype.nanosecond`
777    ///
778    /// More information:
779    ///
780    /// - [ECMAScript Temporal proposal][spec]
781    /// - [MDN reference][mdn]
782    /// - [`temporal_rs` documentation][temporal_rs-docs]
783    ///
784    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.nanosecond
785    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/nanosecond
786    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.nanosecond
787    fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
788        let object = this.as_object();
789        let zdt = 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 ZonedDateTime object.")
794            })?;
795
796        Ok(zdt.inner.nanosecond().into())
797    }
798
799    /// 6.3.17 get `Temporal.ZonedDateTime.prototype.epochMilliseconds`
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.zoneddatetime.prototype.epochmilliseconds
808    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/epochMilliseconds
809    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.epoch_milliseconds
810    fn get_epoch_milliseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
811        let object = this.as_object();
812        let zdt = 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 ZonedDateTime object.")
817            })?;
818
819        Ok(zdt.inner.epoch_milliseconds().into())
820    }
821
822    /// 6.3.18 get `Temporal.ZonedDateTime.prototype.epochNanoseconds`
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.zoneddatetime.prototype.epochnanoseconds
831    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/epochNanoseconds
832    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.epoch_nanoseconds
833    fn get_epoch_nanoseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
834        let object = this.as_object();
835        let zdt = 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 ZonedDateTime object.")
840            })?;
841
842        Ok(JsBigInt::from(zdt.inner.epoch_nanoseconds().as_i128()).into())
843    }
844
845    /// 6.3.19 get `Temporal.ZonedDateTime.prototype.dayOfWeek`
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.zoneddatetime.prototype.dayofweek
854    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/dayOfWeek
855    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.day_of_week
856    fn get_day_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
857        let object = this.as_object();
858        let zdt = 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 ZonedDateTime object.")
863            })?;
864
865        Ok(zdt.inner.day_of_week().into())
866    }
867
868    /// 6.3.20 get `Temporal.ZonedDateTime.prototype.dayOfYear`
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.zoneddatetime.prototype.dayofyear
877    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/dayOfYear
878    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.day_of_year
879    fn get_day_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
880        let object = this.as_object();
881        let zdt = 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 ZonedDateTime object.")
886            })?;
887
888        Ok(zdt.inner.day_of_year().into())
889    }
890
891    /// 6.3.21 get `Temporal.ZonedDateTime.prototype.weekOfYear`
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.zoneddatetime.prototype.weekofyear
900    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/weekOfYear
901    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.week_of_year
902    fn get_week_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
903        let object = this.as_object();
904        let zdt = 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 ZonedDateTime object.")
909            })?;
910
911        Ok(zdt.inner.week_of_year().into_or_undefined())
912    }
913
914    /// 6.3.22 get `Temporal.ZonedDateTime.prototype.yearOfWeek`
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.zoneddatetime.prototype.yearofweek
923    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/yearOfWeek
924    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.year_of_week
925    fn get_year_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
926        let object = this.as_object();
927        let zdt = 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 ZonedDateTime object.")
932            })?;
933
934        Ok(zdt.inner.year_of_week().into_or_undefined())
935    }
936
937    /// 6.3.23 get `Temporal.ZonedDateTime.prototype.hoursInDay`
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.zoneddatetime.prototype.hoursinday
946    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/hoursInDay
947    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.hours_in_day
948    fn get_hours_in_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
949        let object = this.as_object();
950        let zdt = 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 ZonedDateTime object.")
955            })?;
956
957        Ok(zdt
958            .inner
959            .hours_in_day_with_provider(context.timezone_provider())?
960            .into())
961    }
962
963    /// 6.3.24 get `Temporal.ZonedDateTime.prototype.daysInWeek`
964    ///
965    /// More information:
966    ///
967    /// - [ECMAScript Temporal proposal][spec]
968    /// - [MDN reference][mdn]
969    /// - [`temporal_rs` documentation][temporal_rs-docs]
970    ///
971    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinweek
972    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/daysInWeek
973    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.days_in_week
974    fn get_days_in_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
975        let object = this.as_object();
976        let zdt = object
977            .as_ref()
978            .and_then(JsObject::downcast_ref::<Self>)
979            .ok_or_else(|| {
980                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
981            })?;
982
983        Ok(zdt.inner.days_in_week().into())
984    }
985
986    /// 6.3.25 get `Temporal.ZonedDateTime.prototype.daysInMonth`
987    ///
988    /// More information:
989    ///
990    /// - [ECMAScript Temporal proposal][spec]
991    /// - [MDN reference][mdn]
992    /// - [`temporal_rs` documentation][temporal_rs-docs]
993    ///
994    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinmonth
995    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/daysInMonth
996    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.days_in_month
997    fn get_days_in_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
998        let object = this.as_object();
999        let zdt = object
1000            .as_ref()
1001            .and_then(JsObject::downcast_ref::<Self>)
1002            .ok_or_else(|| {
1003                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1004            })?;
1005
1006        Ok(zdt.inner.days_in_month().into())
1007    }
1008
1009    /// 6.3.26 get `Temporal.ZonedDateTime.prototype.daysInYear`
1010    ///
1011    /// More information:
1012    ///
1013    /// - [ECMAScript Temporal proposal][spec]
1014    /// - [MDN reference][mdn]
1015    /// - [`temporal_rs` documentation][temporal_rs-docs]
1016    ///
1017    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinyear
1018    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/daysInYear
1019    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.days_in_year
1020    fn get_days_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1021        let object = this.as_object();
1022        let zdt = object
1023            .as_ref()
1024            .and_then(JsObject::downcast_ref::<Self>)
1025            .ok_or_else(|| {
1026                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1027            })?;
1028
1029        Ok(zdt.inner.days_in_year().into())
1030    }
1031
1032    /// 6.3.27 get `Temporal.ZonedDateTime.prototype.monthsInYear`
1033    ///
1034    /// More information:
1035    ///
1036    /// - [ECMAScript Temporal proposal][spec]
1037    /// - [MDN reference][mdn]
1038    /// - [`temporal_rs` documentation][temporal_rs-docs]
1039    ///
1040    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.monthsinyear
1041    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/monthsInYear
1042    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.months_in_year
1043    fn get_months_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1044        let object = this.as_object();
1045        let zdt = object
1046            .as_ref()
1047            .and_then(JsObject::downcast_ref::<Self>)
1048            .ok_or_else(|| {
1049                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1050            })?;
1051
1052        Ok(zdt.inner.months_in_year().into())
1053    }
1054
1055    /// 6.3.28 get `Temporal.ZonedDateTime.prototype.inLeapYear`
1056    ///
1057    /// More information:
1058    ///
1059    /// - [ECMAScript Temporal proposal][spec]
1060    /// - [MDN reference][mdn]
1061    /// - [`temporal_rs` documentation][temporal_rs-docs]
1062    ///
1063    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.inleapyear
1064    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/inLeapYear
1065    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.in_leap_year
1066    fn get_in_leap_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1067        let object = this.as_object();
1068        let zdt = object
1069            .as_ref()
1070            .and_then(JsObject::downcast_ref::<Self>)
1071            .ok_or_else(|| {
1072                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1073            })?;
1074
1075        Ok(zdt.inner.in_leap_year().into())
1076    }
1077
1078    /// 6.3.29 get Temporal.ZonedDateTime.prototype.offsetNanoseconds
1079    ///
1080    /// More information:
1081    ///
1082    /// - [ECMAScript Temporal proposal][spec]
1083    /// - [MDN reference][mdn]
1084    /// - [`temporal_rs` documentation][temporal_rs-docs]
1085    ///
1086    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds
1087    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/offsetNanoseconds
1088    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.offset_nanoseconds
1089    fn get_offset_nanoseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1090        let object = this.as_object();
1091        let zdt = object
1092            .as_ref()
1093            .and_then(JsObject::downcast_ref::<Self>)
1094            .ok_or_else(|| {
1095                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1096            })?;
1097
1098        Ok(zdt.inner.offset_nanoseconds().into())
1099    }
1100
1101    /// 6.3.30 get Temporal.ZonedDateTime.prototype.offset
1102    ///
1103    /// More information:
1104    ///
1105    /// - [ECMAScript Temporal proposal][spec]
1106    /// - [MDN reference][mdn]
1107    /// - [`temporal_rs` documentation][temporal_rs-docs]
1108    ///
1109    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offset
1110    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/offset
1111    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.offset
1112    fn get_offset(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1113        let object = this.as_object();
1114        let zdt = object
1115            .as_ref()
1116            .and_then(JsObject::downcast_ref::<Self>)
1117            .ok_or_else(|| {
1118                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1119            })?;
1120
1121        Ok(JsString::from(zdt.inner.offset()).into())
1122    }
1123}
1124
1125// ==== `ZonedDateTime` static methods implementation ====
1126
1127impl ZonedDateTime {
1128    /// 6.2.2 `Temporal.ZonedDateTime.from ( item [ , options ] )`
1129    ///
1130    /// More information:
1131    ///
1132    /// - [ECMAScript Temporal proposal][spec]
1133    /// - [MDN reference][mdn]
1134    ///
1135    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.from
1136    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/from
1137    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1138        // 1. Return ? ToTemporalZonedDateTime(item, options).
1139        let item = args.get_or_undefined(0);
1140        let options = args.get(1);
1141        let inner = to_temporal_zoneddatetime(item, options, context)?;
1142        create_temporal_zoneddatetime(inner, None, context).map(Into::into)
1143    }
1144
1145    /// 6.2.3 `Temporal.ZonedDateTime.compare ( one, two )`
1146    ///
1147    /// More information:
1148    ///
1149    /// - [ECMAScript Temporal proposal][spec]
1150    /// - [MDN reference][mdn]
1151    /// - [`temporal_rs` documentation][temporal_rs-docs]
1152    ///
1153    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.compare
1154    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/compare
1155    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.compare_instant
1156    fn compare(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1157        // 1. Return ? ToTemporalZonedDateTime(item, options).
1158        let one = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?;
1159        let two = to_temporal_zoneddatetime(args.get_or_undefined(1), None, context)?;
1160        Ok((one.compare_instant(&two) as i8).into())
1161    }
1162}
1163
1164// ==== `ZonedDateTime` methods implementation ====
1165
1166impl ZonedDateTime {
1167    /// 6.3.31 `Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] )`
1168    ///
1169    /// More information:
1170    ///
1171    /// - [ECMAScript Temporal proposal][spec]
1172    /// - [MDN reference][mdn]
1173    /// - [`temporal_rs` documentation][temporal_rs-docs]
1174    ///
1175    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.with
1176    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/with
1177    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.with
1178    fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1179        // 1. Let zonedDateTime be the this value.
1180        // 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
1181        let object = this.as_object();
1182        let zdt = object
1183            .as_ref()
1184            .and_then(JsObject::downcast_ref::<Self>)
1185            .ok_or_else(|| {
1186                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1187            })?;
1188        // 3. If ? IsPartialTemporalObject(temporalZonedDateTimeLike) is false, throw a TypeError exception.
1189        let Some(obj) = is_partial_temporal_object(args.get_or_undefined(0), context)? else {
1190            return Err(JsNativeError::typ()
1191                .with_message("temporalZonedDateTimeLike was not a partial object")
1192                .into());
1193        };
1194        // 4. Let epochNs be zonedDateTime.[[EpochNanoseconds]].
1195        // 5. Let timeZone be zonedDateTime.[[TimeZone]].
1196        // 6. Let calendar be zonedDateTime.[[Calendar]].
1197        // 7. Let offsetNanoseconds be GetOffsetNanosecondsFor(timeZone, epochNs).
1198        // 8. Let isoDateTime be GetISODateTimeFor(timeZone, epochNs).
1199        // 9. Let fields be ISODateToFields(calendar, isoDateTime.[[ISODate]], date).
1200        // 10. Set fields.[[Hour]] to isoDateTime.[[Time]].[[Hour]].
1201        // 11. Set fields.[[Minute]] to isoDateTime.[[Time]].[[Minute]].
1202        // 12. Set fields.[[Second]] to isoDateTime.[[Time]].[[Second]].
1203        // 13. Set fields.[[Millisecond]] to isoDateTime.[[Time]].[[Millisecond]].
1204        // 14. Set fields.[[Microsecond]] to isoDateTime.[[Time]].[[Microsecond]].
1205        // 15. Set fields.[[Nanosecond]] to isoDateTime.[[Time]].[[Nanosecond]].
1206        // 16. Set fields.[[OffsetString]] to FormatUTCOffsetNanoseconds(offsetNanoseconds).
1207        // 17. Let partialZonedDateTime be ? PrepareCalendarFields(calendar, temporalZonedDateTimeLike, « year, month, month-code, day », « hour, minute, second, millisecond, microsecond, nanosecond, offset », partial).
1208        // 18. Set fields to CalendarMergeFields(calendar, fields, partialZonedDateTime).
1209        let (fields, _) = to_zoned_date_time_fields(
1210            &obj,
1211            zdt.inner.calendar(),
1212            ZdtFieldsType::NoTimeZone,
1213            context,
1214        )?;
1215
1216        // 19. Let resolvedOptions be ? GetOptionsObject(options).
1217        let resolved_options = get_options_object(args.get_or_undefined(1))?;
1218        // 20. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).
1219        let disambiguation =
1220            get_option::<Disambiguation>(&resolved_options, js_string!("disambiguation"), context)?;
1221        // 21. Let offset be ? GetTemporalOffsetOption(resolvedOptions, prefer).
1222        let offset =
1223            get_option::<OffsetDisambiguation>(&resolved_options, js_string!("offset"), context)?;
1224        // 22. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
1225        let overflow = get_option::<Overflow>(&resolved_options, js_string!("overflow"), context)?;
1226
1227        let result = zdt.inner.with_with_provider(
1228            fields,
1229            disambiguation,
1230            offset,
1231            overflow,
1232            context.timezone_provider(),
1233        )?;
1234        create_temporal_zoneddatetime(result, None, context).map(Into::into)
1235    }
1236
1237    /// 6.3.32 `Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )`
1238    ///
1239    /// More information:
1240    ///
1241    /// - [ECMAScript Temporal proposal][spec]
1242    /// - [MDN reference][mdn]
1243    /// - [`temporal_rs` documentation][temporal_rs-docs]
1244    ///
1245    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withPlainTime
1246    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/withPlainTime
1247    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.with_plain_time
1248    fn with_plain_time(
1249        this: &JsValue,
1250        args: &[JsValue],
1251        context: &mut Context,
1252    ) -> JsResult<JsValue> {
1253        let object = this.as_object();
1254        let zdt = object
1255            .as_ref()
1256            .and_then(JsObject::downcast_ref::<Self>)
1257            .ok_or_else(|| {
1258                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1259            })?;
1260
1261        let time = args
1262            .get_or_undefined(0)
1263            .map(|v| to_temporal_time(v, None, context))
1264            .transpose()?;
1265
1266        let inner = zdt
1267            .inner
1268            .with_plain_time_and_provider(time, context.timezone_provider())?;
1269        create_temporal_zoneddatetime(inner, None, context).map(Into::into)
1270    }
1271
1272    /// 6.3.33 `Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )`
1273    ///
1274    /// More information:
1275    ///
1276    /// - [ECMAScript Temporal proposal][spec]
1277    /// - [MDN reference][mdn]
1278    /// - [`temporal_rs` documentation][temporal_rs-docs]
1279    ///
1280    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withtimezone
1281    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/withTimeZone
1282    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.with_timezone
1283    fn with_timezone(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1284        let object = this.as_object();
1285        let zdt = object
1286            .as_ref()
1287            .and_then(JsObject::downcast_ref::<Self>)
1288            .ok_or_else(|| {
1289                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1290            })?;
1291
1292        let timezone = to_temporal_timezone_identifier(args.get_or_undefined(0), context)?;
1293
1294        let inner = zdt
1295            .inner
1296            .with_time_zone_with_provider(timezone, context.timezone_provider())?;
1297        create_temporal_zoneddatetime(inner, None, context).map(Into::into)
1298    }
1299
1300    /// 6.3.34 `Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )`
1301    ///
1302    /// More information:
1303    ///
1304    /// - [ECMAScript Temporal proposal][spec]
1305    /// - [MDN reference][mdn]
1306    /// - [`temporal_rs` documentation][temporal_rs-docs]
1307    ///
1308    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withcalendar
1309    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/withCalendar
1310    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.with_calendar
1311    fn with_calendar(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1312        let object = this.as_object();
1313        let zdt = object
1314            .as_ref()
1315            .and_then(JsObject::downcast_ref::<Self>)
1316            .ok_or_else(|| {
1317                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1318            })?;
1319
1320        let calendar = to_temporal_calendar_identifier(args.get_or_undefined(0))?;
1321
1322        let inner = zdt.inner.with_calendar(calendar);
1323        create_temporal_zoneddatetime(inner, None, context).map(Into::into)
1324    }
1325
1326    /// 6.3.35 `Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )`
1327    ///
1328    /// More information:
1329    ///
1330    /// - [ECMAScript Temporal proposal][spec]
1331    /// - [MDN reference][mdn]
1332    /// - [`temporal_rs` documentation][temporal_rs-docs]
1333    ///
1334    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.add
1335    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/add
1336    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.add
1337    fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1338        let object = this.as_object();
1339        let zdt = object
1340            .as_ref()
1341            .and_then(JsObject::downcast_ref::<Self>)
1342            .ok_or_else(|| {
1343                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1344            })?;
1345
1346        let duration = to_temporal_duration(args.get_or_undefined(0), context)?;
1347
1348        let options = get_options_object(args.get_or_undefined(1))?;
1349        let overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1350
1351        let result =
1352            zdt.inner
1353                .add_with_provider(&duration, overflow, context.timezone_provider())?;
1354        create_temporal_zoneddatetime(result, None, context).map(Into::into)
1355    }
1356
1357    /// 6.3.36 `Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options ] )`
1358    ///
1359    /// More information:
1360    ///
1361    /// - [ECMAScript Temporal proposal][spec]
1362    /// - [MDN reference][mdn]
1363    /// - [`temporal_rs` documentation][temporal_rs-docs]
1364    ///
1365    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.subtract
1366    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/subtract
1367    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.subtract
1368    fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1369        let object = this.as_object();
1370        let zdt = object
1371            .as_ref()
1372            .and_then(JsObject::downcast_ref::<Self>)
1373            .ok_or_else(|| {
1374                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1375            })?;
1376
1377        let duration = to_temporal_duration(args.get_or_undefined(0), context)?;
1378
1379        let options = get_options_object(args.get_or_undefined(1))?;
1380        let overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1381
1382        let result =
1383            zdt.inner
1384                .subtract_with_provider(&duration, overflow, context.timezone_provider())?;
1385        create_temporal_zoneddatetime(result, None, context).map(Into::into)
1386    }
1387
1388    /// 6.3.37 `Temporal.ZonedDateTime.prototype.until ( other [ , options ] )`
1389    ///
1390    /// More information:
1391    ///
1392    /// - [ECMAScript Temporal proposal][spec]
1393    /// - [MDN reference][mdn]
1394    /// - [`temporal_rs` documentation][temporal_rs-docs]
1395    ///
1396    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.until
1397    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/until
1398    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.until
1399    fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1400        let object = this.as_object();
1401        let zdt = object
1402            .as_ref()
1403            .and_then(JsObject::downcast_ref::<Self>)
1404            .ok_or_else(|| {
1405                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1406            })?;
1407
1408        let other = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?;
1409
1410        let options = get_options_object(args.get_or_undefined(1))?;
1411        let settings = get_difference_settings(&options, context)?;
1412
1413        let result =
1414            zdt.inner
1415                .until_with_provider(&other, settings, context.timezone_provider())?;
1416        create_temporal_duration(result, None, context).map(Into::into)
1417    }
1418
1419    /// 6.3.38 `Temporal.ZonedDateTime.prototype.since ( other [ , options ] )`
1420    ///
1421    /// More information:
1422    ///
1423    /// - [ECMAScript Temporal proposal][spec]
1424    /// - [MDN reference][mdn]
1425    /// - [`temporal_rs` documentation][temporal_rs-docs]
1426    ///
1427    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.since
1428    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/since
1429    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.since
1430    fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1431        let object = this.as_object();
1432        let zdt = object
1433            .as_ref()
1434            .and_then(JsObject::downcast_ref::<Self>)
1435            .ok_or_else(|| {
1436                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1437            })?;
1438
1439        let other = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?;
1440
1441        let options = get_options_object(args.get_or_undefined(1))?;
1442        let settings = get_difference_settings(&options, context)?;
1443
1444        let result =
1445            zdt.inner
1446                .since_with_provider(&other, settings, context.timezone_provider())?;
1447        create_temporal_duration(result, None, context).map(Into::into)
1448    }
1449
1450    /// 6.3.39 `Temporal.ZonedDateTime.prototype.round ( roundTo )`
1451    ///
1452    /// More information:
1453    ///
1454    /// - [ECMAScript Temporal proposal][spec]
1455    /// - [MDN reference][mdn]
1456    /// - [`temporal_rs` documentation][temporal_rs-docs]
1457    ///
1458    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round
1459    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/round
1460    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.round
1461    fn round(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1462        // 1. Let zonedDateTime be the this value.
1463        // 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
1464        let object = this.as_object();
1465        let zdt = object
1466            .as_ref()
1467            .and_then(JsObject::downcast_ref::<Self>)
1468            .ok_or_else(|| {
1469                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1470            })?;
1471
1472        // 3. If roundTo is undefined, then
1473        let round_to_arg = args.get_or_undefined(0);
1474        if round_to_arg.is_undefined() {
1475            // a. Throw a TypeError exception.
1476            return Err(JsNativeError::typ()
1477                .with_message("roundTo cannot be undefined.")
1478                .into());
1479        }
1480        // 4. If Type(roundTo) is String, then
1481        let round_to = if let Some(param_string) = round_to_arg.as_string() {
1482            // a. Let paramString be roundTo.
1483            let param_string = param_string.clone();
1484            // b. Set roundTo to OrdinaryObjectCreate(null).
1485            let new_round_to = JsObject::with_null_proto();
1486            // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
1487            new_round_to.create_data_property_or_throw(
1488                js_string!("smallestUnit"),
1489                param_string,
1490                context,
1491            )?;
1492            new_round_to
1493        } else {
1494            // 5. Else,
1495            // a. Set roundTo to ? GetOptionsObject(roundTo).
1496            get_options_object(round_to_arg)?
1497        };
1498
1499        // 6. NOTE: The following steps read options and perform independent validation
1500        // in alphabetical order (GetRoundingIncrementOption reads "roundingIncrement"
1501        // and GetRoundingModeOption reads "roundingMode").
1502        let mut options = RoundingOptions::default();
1503
1504        // 7. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo).
1505        options.increment =
1506            get_option::<RoundingIncrement>(&round_to, js_string!("roundingIncrement"), context)?;
1507
1508        // 8. Let roundingMode be ? GetRoundingModeOption(roundTo, half-expand).
1509        options.rounding_mode =
1510            get_option::<RoundingMode>(&round_to, js_string!("roundingMode"), context)?;
1511
1512        // 9. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, "smallestUnit", time, required, « day »).
1513        options.smallest_unit = get_temporal_unit(
1514            &round_to,
1515            js_string!("smallestUnit"),
1516            TemporalUnitGroup::Time,
1517            Some(vec![Unit::Day]),
1518            context,
1519        )?;
1520
1521        let result = zdt
1522            .inner
1523            .round_with_provider(options, context.timezone_provider())?;
1524        create_temporal_zoneddatetime(result, None, context).map(Into::into)
1525    }
1526
1527    /// 6.3.40 `Temporal.ZonedDateTime.prototype.equals ( other )`
1528    ///
1529    /// More information:
1530    ///
1531    /// - [ECMAScript Temporal proposal][spec]
1532    /// - [MDN reference][mdn]
1533    /// - [`temporal_rs` documentation][temporal_rs-docs]
1534    ///
1535    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.equals
1536    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/equals
1537    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#impl-PartialEq-for-ZonedDateTime
1538    fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1539        let object = this.as_object();
1540        let zdt = object
1541            .as_ref()
1542            .and_then(JsObject::downcast_ref::<Self>)
1543            .ok_or_else(|| {
1544                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1545            })?;
1546
1547        let other = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?;
1548        Ok(zdt
1549            .inner
1550            .equals_with_provider(&other, context.timezone_provider())?
1551            .into())
1552    }
1553
1554    /// 6.3.41 `Temporal.ZonedDateTime.prototype.toString ( [ options ] )`
1555    ///
1556    /// More information:
1557    ///
1558    /// - [ECMAScript Temporal proposal][spec]
1559    /// - [MDN reference][mdn]
1560    /// - [`temporal_rs` documentation][temporal_rs-docs]
1561    ///
1562    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tostring
1563    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toString
1564    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_ixdtf_string
1565    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1566        let object = this.as_object();
1567        let zdt = object
1568            .as_ref()
1569            .and_then(JsObject::downcast_ref::<Self>)
1570            .ok_or_else(|| {
1571                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1572            })?;
1573
1574        let options = get_options_object(args.get_or_undefined(0))?;
1575
1576        let show_calendar =
1577            get_option::<DisplayCalendar>(&options, js_string!("calendarName"), context)?
1578                .unwrap_or(DisplayCalendar::Auto);
1579        let precision = get_digits_option(&options, context)?;
1580        let show_offset = get_option::<DisplayOffset>(&options, js_string!("offset"), context)?
1581            .unwrap_or(DisplayOffset::Auto);
1582        let rounding_mode =
1583            get_option::<RoundingMode>(&options, js_string!("roundingMode"), context)?;
1584        let smallest_unit = get_option::<Unit>(&options, js_string!("smallestUnit"), context)?;
1585        // NOTE: There may be an order-of-operations here due to a check on Unit groups and smallest_unit value.
1586        let display_timezone =
1587            get_option::<DisplayTimeZone>(&options, js_string!("timeZoneName"), context)?
1588                .unwrap_or(DisplayTimeZone::Auto);
1589
1590        let options = ToStringRoundingOptions {
1591            precision,
1592            smallest_unit,
1593            rounding_mode,
1594        };
1595        let ixdtf = zdt.inner.to_ixdtf_string_with_provider(
1596            show_offset,
1597            display_timezone,
1598            show_calendar,
1599            options,
1600            context.timezone_provider(),
1601        )?;
1602
1603        Ok(JsString::from(ixdtf).into())
1604    }
1605
1606    /// 6.3.42 `Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )`
1607    ///
1608    /// More information:
1609    ///
1610    /// - [ECMAScript Temporal proposal][spec]
1611    /// - [MDN reference][mdn]
1612    ///
1613    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tolocalestring
1614    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toLocaleString
1615    fn to_locale_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1616        // TODO: Update for ECMA-402 compliance
1617        let object = this.as_object();
1618        let zdt = object
1619            .as_ref()
1620            .and_then(JsObject::downcast_ref::<Self>)
1621            .ok_or_else(|| {
1622                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1623            })?;
1624
1625        let ixdtf = zdt.inner.to_ixdtf_string_with_provider(
1626            DisplayOffset::Auto,
1627            DisplayTimeZone::Auto,
1628            DisplayCalendar::Auto,
1629            ToStringRoundingOptions::default(),
1630            context.timezone_provider(),
1631        )?;
1632
1633        Ok(JsString::from(ixdtf).into())
1634    }
1635
1636    /// 6.3.43 `Temporal.ZonedDateTime.prototype.toJSON ( )`
1637    ///
1638    /// More information:
1639    ///
1640    /// - [ECMAScript Temporal proposal][spec]
1641    /// - [MDN reference][mdn]
1642    ///
1643    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tojson
1644    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toJSON
1645    fn to_json(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1646        let object = this.as_object();
1647        let zdt = object
1648            .as_ref()
1649            .and_then(JsObject::downcast_ref::<Self>)
1650            .ok_or_else(|| {
1651                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1652            })?;
1653
1654        let ixdtf = zdt.inner.to_ixdtf_string_with_provider(
1655            DisplayOffset::Auto,
1656            DisplayTimeZone::Auto,
1657            DisplayCalendar::Auto,
1658            ToStringRoundingOptions::default(),
1659            context.timezone_provider(),
1660        )?;
1661
1662        Ok(JsString::from(ixdtf).into())
1663    }
1664
1665    /// 6.3.44 `Temporal.ZonedDateTime.prototype.valueOf ( )`
1666    ///
1667    /// More information:
1668    ///
1669    /// - [ECMAScript Temporal proposal][spec]
1670    /// - [MDN reference][mdn]
1671    ///
1672    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.valueof
1673    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/valueOf
1674    pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1675        Err(JsNativeError::typ()
1676            .with_message("`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`")
1677            .into())
1678    }
1679
1680    /// 6.3.45 `Temporal.ZonedDateTime.prototype.startOfDay ( )`
1681    ///
1682    /// More information:
1683    ///
1684    /// - [ECMAScript Temporal proposal][spec]
1685    /// - [MDN reference][mdn]
1686    /// - [`temporal_rs` documentation][temporal_rs-docs]
1687    ///
1688    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.startofday
1689    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/startOfDay
1690    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.start_of_day
1691    fn start_of_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1692        let object = this.as_object();
1693        let zdt = object
1694            .as_ref()
1695            .and_then(JsObject::downcast_ref::<Self>)
1696            .ok_or_else(|| {
1697                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1698            })?;
1699
1700        let new = zdt
1701            .inner
1702            .start_of_day_with_provider(context.timezone_provider())?;
1703        create_temporal_zoneddatetime(new, None, context).map(Into::into)
1704    }
1705
1706    /// 6.3.46 `Temporal.ZonedDateTime.prototype.getTimeZoneTransition ( directionParam )`
1707    ///
1708    /// More information:
1709    ///
1710    /// - [ECMAScript Temporal proposal][spec]
1711    /// - [MDN reference][mdn]
1712    /// - [`temporal_rs` documentation][temporal_rs-docs]
1713    ///
1714    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.gettimezonetransition
1715    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/getTimeZoneTransition
1716    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.get_time_zone_transition_with_provider
1717    fn get_time_zone_transition(
1718        this: &JsValue,
1719        args: &[JsValue],
1720        context: &mut Context,
1721    ) -> JsResult<JsValue> {
1722        // 1. Let zonedDateTime be the this value.
1723        // 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
1724        // 3. Let timeZone be zonedDateTime.[[TimeZone]].
1725        let object = this.as_object();
1726        let zdt = object
1727            .as_ref()
1728            .and_then(JsObject::downcast_ref::<Self>)
1729            .ok_or_else(|| {
1730                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1731            })?;
1732
1733        let direction_param = args.get_or_undefined(0);
1734        // 4. If directionParam is undefined, throw a TypeError exception.
1735        if direction_param.is_undefined() {
1736            return Err(JsNativeError::typ()
1737                .with_message("getTimeZoneTransition directionParam cannot be undefined.")
1738                .into());
1739        }
1740        // 5. If directionParam is a String, then
1741        let options_obj = if let Some(param_str) = direction_param.as_string() {
1742            // a. Let paramString be directionParam.
1743            // b. Set directionParam to OrdinaryObjectCreate(null).
1744            let obj = JsObject::with_null_proto();
1745            // c. Perform ! CreateDataPropertyOrThrow(directionParam, "direction", paramString).
1746            obj.create_data_property_or_throw(
1747                js_string!("direction"),
1748                JsValue::from(param_str.clone()),
1749                context,
1750            )?;
1751            obj
1752        // 6. Else,
1753        } else {
1754            // a. Set directionParam to ? GetOptionsObject(directionParam).
1755            get_options_object(direction_param)?
1756        };
1757
1758        // TODO: step 7
1759        // 7. Let direction be ? GetDirectionOption(directionParam).
1760        let direction =
1761            get_option::<TransitionDirection>(&options_obj, js_string!("direction"), context)?
1762                .ok_or_else(|| {
1763                    JsNativeError::range().with_message("direction option is required.")
1764                })?;
1765
1766        // Step 8-12
1767        let result = zdt
1768            .inner
1769            .get_time_zone_transition_with_provider(direction, context.timezone_provider())?;
1770
1771        match result {
1772            Some(zdt) => create_temporal_zoneddatetime(zdt, None, context).map(Into::into),
1773            None => Ok(JsValue::null()),
1774        }
1775    }
1776
1777    /// 6.3.47 `Temporal.ZonedDateTime.prototype.toInstant ( )`
1778    ///
1779    /// More information:
1780    ///
1781    /// - [ECMAScript Temporal proposal][spec]
1782    /// - [MDN reference][mdn]
1783    /// - [`temporal_rs` documentation][temporal_rs-docs]
1784    ///
1785    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toinstant
1786    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toInstant
1787    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_instant
1788    fn to_instant(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1789        let object = this.as_object();
1790        let zdt = object
1791            .as_ref()
1792            .and_then(JsObject::downcast_ref::<Self>)
1793            .ok_or_else(|| {
1794                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1795            })?;
1796
1797        create_temporal_instant(zdt.inner.to_instant(), None, context)
1798    }
1799
1800    /// 6.3.48 `Temporal.ZonedDateTime.prototype.toPlainDate ( )`
1801    ///
1802    /// More information:
1803    ///
1804    /// - [ECMAScript Temporal proposal][spec]
1805    /// - [MDN reference][mdn]
1806    /// - [`temporal_rs` documentation][temporal_rs-docs]
1807    ///
1808    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaindate
1809    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toPlainDate
1810    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_plain_date
1811    fn to_plain_date(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1812        let object = this.as_object();
1813        let zdt = object
1814            .as_ref()
1815            .and_then(JsObject::downcast_ref::<Self>)
1816            .ok_or_else(|| {
1817                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1818            })?;
1819
1820        let inner = zdt.inner.to_plain_date();
1821        create_temporal_date(inner, None, context).map(Into::into)
1822    }
1823
1824    /// 6.3.49 `Temporal.ZonedDateTime.prototype.toPlainTime ( )`
1825    ///
1826    /// More information:
1827    ///
1828    /// - [ECMAScript Temporal proposal][spec]
1829    /// - [MDN reference][mdn]
1830    /// - [`temporal_rs` documentation][temporal_rs-docs]
1831    ///
1832    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaintime
1833    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toPlainTime
1834    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_plain_time
1835    fn to_plain_time(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1836        let object = this.as_object();
1837        let zdt = object
1838            .as_ref()
1839            .and_then(JsObject::downcast_ref::<Self>)
1840            .ok_or_else(|| {
1841                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1842            })?;
1843
1844        let new = zdt.inner.to_plain_time();
1845        create_temporal_time(new, None, context).map(Into::into)
1846    }
1847
1848    /// 6.3.50 `Temporal.ZonedDateTime.prototype.toPlainDateTime ( )`
1849    ///
1850    /// More information:
1851    ///
1852    /// - [ECMAScript Temporal proposal][spec]
1853    /// - [MDN reference][mdn]
1854    /// - [`temporal_rs` documentation][temporal_rs-docs]
1855    ///
1856    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaindatetime
1857    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toPlainDateTime
1858    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_plain_datetime
1859    fn to_plain_date_time(
1860        this: &JsValue,
1861        _: &[JsValue],
1862        context: &mut Context,
1863    ) -> JsResult<JsValue> {
1864        let object = this.as_object();
1865        let zdt = object
1866            .as_ref()
1867            .and_then(JsObject::downcast_ref::<Self>)
1868            .ok_or_else(|| {
1869                JsNativeError::typ().with_message("the this object must be a ZonedDateTime object.")
1870            })?;
1871
1872        let new = zdt.inner.to_plain_date_time();
1873        create_temporal_datetime(new, None, context).map(Into::into)
1874    }
1875}
1876
1877// ==== ZonedDateTime Abstract Operations ====
1878
1879/// 6.5.3 `CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , newTarget ] )`
1880pub(crate) fn create_temporal_zoneddatetime(
1881    inner: ZonedDateTimeInner,
1882    new_target: Option<&JsValue>,
1883    context: &mut Context,
1884) -> JsResult<JsObject> {
1885    // 1. Assert: IsValidEpochNanoseconds(epochNanoseconds) is true.
1886    // 2. If newTarget is not present, set newTarget to %Temporal.ZonedDateTime%.
1887    let new_target = new_target.cloned().unwrap_or(
1888        context
1889            .realm()
1890            .intrinsics()
1891            .constructors()
1892            .zoned_date_time()
1893            .constructor()
1894            .into(),
1895    );
1896    // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.ZonedDateTime.prototype%", « [[InitializezdtemporalZonedDateTime]], [[EpochNanoseconds]], [[TimeZone]], [[Calendar]] »).
1897    let prototype = get_prototype_from_constructor(
1898        &new_target,
1899        StandardConstructors::zoned_date_time,
1900        context,
1901    )?;
1902    // 4. Set object.[[EpochNanoseconds]] to epochNanoseconds.
1903    // 5. Set object.[[TimeZone]] to timeZone.
1904    // 6. Set object.[[Calendar]] to calendar.
1905    let obj = JsObject::from_proto_and_data(prototype, ZonedDateTime::new(inner));
1906
1907    // 7. Return object.
1908    Ok(obj)
1909}
1910
1911/// 6.5.2 `ToTemporalZonedDateTime ( item [ , options ] )`
1912pub(crate) fn to_temporal_zoneddatetime(
1913    value: &JsValue,
1914    options: Option<&JsValue>,
1915    context: &mut Context,
1916) -> JsResult<ZonedDateTimeInner> {
1917    // 1. If options is not present, set options to undefined.
1918    // 2. Let offsetBehaviour be option.
1919    // 3. Let matchBehaviour be match-exactly.
1920    // 4. If item is an Object, then
1921    match value.variant() {
1922        JsVariant::Object(object) => {
1923            // a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
1924            if let Some(zdt) = object.downcast_ref::<ZonedDateTime>() {
1925                // i. NOTE: The following steps, and similar ones below, read options
1926                // and perform independent validation in alphabetical order
1927                // (GetTemporalDisambiguationOption reads "disambiguation", GetTemporalOffsetOption
1928                // reads "offset", and GetTemporalOverflowOption reads "overflow").
1929                // ii. Let resolvedOptions be ? GetOptionsObject(options).
1930                let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;
1931                // iii. Perform ? GetTemporalDisambiguationOption(resolvedOptions).
1932                let _disambiguation =
1933                    get_option::<Disambiguation>(&options, js_string!("disambiguation"), context)?
1934                        .unwrap_or(Disambiguation::Compatible);
1935                // iv. Perform ? GetTemporalOffsetOption(resolvedOptions, reject).
1936                let _offset_option =
1937                    get_option::<OffsetDisambiguation>(&options, js_string!("offset"), context)?
1938                        .unwrap_or(OffsetDisambiguation::Reject);
1939                // v. Perform ? GetTemporalOverflowOption(resolvedOptions).
1940                let _overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?
1941                    .unwrap_or_default();
1942                // vi. Return ! CreateTemporalZonedDateTime(item.[[EpochNanoseconds]], item.[[TimeZone]], item.[[Calendar]]).
1943                return Ok(zdt.inner.as_ref().clone());
1944            }
1945            let partial = to_partial_zoneddatetime(&object, context)?;
1946            // f. If offsetString is unset, the
1947            // i. Set offsetBehaviour to wall.
1948            // g. Let resolvedOptions be ? GetOptionsObject(options).
1949            let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;
1950            // h. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).
1951            let disambiguation =
1952                get_option::<Disambiguation>(&options, js_string!("disambiguation"), context)?;
1953            // i. Let offsetOption be ? GetTemporalOffsetOption(resolvedOptions, reject).
1954            let offset_option =
1955                get_option::<OffsetDisambiguation>(&options, js_string!("offset"), context)?;
1956            // j. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
1957            let overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1958            // k. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).
1959            // l. Let isoDate be result.[[ISODate]].
1960            // m. Let time be result.[[Time]].
1961            Ok(ZonedDateTimeInner::from_partial_with_provider(
1962                partial,
1963                overflow,
1964                disambiguation,
1965                offset_option,
1966                context.timezone_provider(),
1967            )?)
1968        }
1969        JsVariant::String(zdt_source) => {
1970            // b. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[+Zoned] »).
1971            let parsed = ParsedZonedDateTime::from_utf8_with_provider(
1972                zdt_source.to_std_string_escaped().as_bytes(),
1973                context.timezone_provider(),
1974            )?;
1975            // c. Let annotation be result.[[TimeZone]].[[TimeZoneAnnotation]].
1976            // d. Assert: annotation is not empty.
1977            // e. Let timeZone be ? ToTemporalTimeZoneIdentifier(annotation).
1978            // f. Let offsetString be result.[[TimeZone]].[[OffsetString]].
1979            // g. If result.[[TimeZone]].[[Z]] is true, then
1980            // i. Set offsetBehaviour to exact.
1981            // h. Else if offsetString is empty, then
1982            // i. Set offsetBehaviour to wall.
1983            // i. Let calendar be result.[[Calendar]].
1984            // j. If calendar is empty, set calendar to "iso8601".
1985            // k. Set calendar to ? CanonicalizeCalendar(calendar).
1986            // l. Set matchBehaviour to match-minutes.
1987            // m. Let resolvedOptions be ? GetOptionsObject(options).
1988            let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;
1989            // n. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).
1990            let disambiguation =
1991                get_option::<Disambiguation>(&options, js_string!("disambiguation"), context)?
1992                    .unwrap_or(Disambiguation::Compatible);
1993            // o. Let offsetOption be ? GetTemporalOffsetOption(resolvedOptions, reject).
1994            let offset_option =
1995                get_option::<OffsetDisambiguation>(&options, js_string!("offset"), context)?
1996                    .unwrap_or(OffsetDisambiguation::Reject);
1997            // p. Perform ? GetTemporalOverflowOption(resolvedOptions).
1998            let _overflow = get_option::<Overflow>(&options, js_string!("overflow"), context)?;
1999            // q. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).
2000            // r. Let time be result.[[Time]].
2001            // 6. Let offsetNanoseconds be 0.
2002            // 7. If offsetBehaviour is option, then
2003            //        a. Set offsetNanoseconds to ! ParseDateTimeUTCOffset(offsetString).
2004            // 8. Let epochNanoseconds be ? InterpretISODateTimeOffset(isoDate, time, offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour).
2005            Ok(ZonedDateTimeInner::from_parsed_with_provider(
2006                parsed,
2007                disambiguation,
2008                offset_option,
2009                context.timezone_provider(),
2010            )?)
2011        }
2012        // 5. Else,
2013        // a. If item is not a String, throw a TypeError exception.
2014        _ => Err(JsNativeError::typ()
2015            .with_message("Temporal.ZonedDateTime.from only accepts an object or string.")
2016            .into()),
2017    }
2018    // 9. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
2019}
2020
2021pub(crate) fn to_temporal_timezone_identifier(
2022    value: &JsValue,
2023    context: &mut Context,
2024) -> JsResult<TimeZone> {
2025    // 1. If temporalTimeZoneLike is an Object, then
2026    //    a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] internal slot, then
2027    if let Some(obj) = value.as_object()
2028        && let Some(zdt) = obj.downcast_ref::<ZonedDateTime>()
2029    {
2030        // i. Return temporalTimeZoneLike.[[TimeZone]].
2031        return Ok(*zdt.inner.time_zone());
2032    }
2033
2034    // 2. If temporalTimeZoneLike is not a String, throw a TypeError exception.
2035    let Some(tz_string) = value.as_string() else {
2036        return Err(JsNativeError::typ()
2037            .with_message("timeZone must be a string or Temporal.ZonedDateTime")
2038            .into());
2039    };
2040
2041    // 3. Let parseResult be ? ParseTemporalTimeZoneString(temporalTimeZoneLike).
2042    // 4. Let offsetMinutes be parseResult.[[OffsetMinutes]].
2043    // 5. If offsetMinutes is not empty, return FormatOffsetTimeZoneIdentifier(offsetMinutes).
2044    // 6. Let name be parseResult.[[Name]].
2045    // 7. Let timeZoneIdentifierRecord be GetAvailableNamedTimeZoneIdentifier(name).
2046    // 8. If timeZoneIdentifierRecord is empty, throw a RangeError exception.
2047    // 9. Return timeZoneIdentifierRecord.[[Identifier]].
2048    let timezone = TimeZone::try_from_str_with_provider(
2049        &tz_string.to_std_string_escaped(),
2050        context.timezone_provider(),
2051    )?;
2052
2053    Ok(timezone)
2054}
2055
2056fn to_offset_string(value: &JsValue, context: &mut Context) -> JsResult<UtcOffset> {
2057    // 1. Let offset be ? ToPrimitive(argument, string).
2058    let offset = value.to_primitive(context, PreferredType::String)?;
2059    // 2. If offset is not a String, throw a TypeError exception.
2060    let Some(offset_string) = offset.as_string() else {
2061        return Err(JsNativeError::typ()
2062            .with_message("offset must be a String.")
2063            .into());
2064    };
2065    // 3. Perform ? ParseDateTimeUTCOffset(offset).
2066    let result = UtcOffset::from_str(&offset_string.to_std_string_escaped())?;
2067    // 4. Return offset.
2068    Ok(result)
2069}
2070
2071pub(crate) fn to_partial_zoneddatetime(
2072    partial_object: &JsObject,
2073    context: &mut Context,
2074) -> JsResult<PartialZonedDateTime> {
2075    // NOTE (nekevss): Why do we have to list out all of the get operations? Well, order of operations Watson!
2076    // b. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).
2077    // c. Let fields be ? PrepareCalendarFields(calendar, item, « year, month, month-code, day », « hour, minute, second, millisecond, microsecond, nanosecond, offset, time-zone », « time-zone »).
2078    let calendar = get_temporal_calendar_slot_value_with_default(partial_object, context)?;
2079    let (fields, timezone) = to_zoned_date_time_fields(
2080        partial_object,
2081        &calendar,
2082        ZdtFieldsType::TimeZoneRequired,
2083        context,
2084    )?;
2085    Ok(PartialZonedDateTime {
2086        fields,
2087        timezone,
2088        calendar,
2089    })
2090}
2091
2092/// This distinguishes the type of `PrepareCalendarField` call used.
2093#[derive(Debug, Clone, Copy, PartialEq)]
2094pub enum ZdtFieldsType {
2095    /// Do not call to the `timeZone` property.
2096    NoTimeZone,
2097    /// Call to `timeZone`, value can be undefined.
2098    TimeZoneNotRequired,
2099    /// Call to `timeZone`, value must exist.
2100    TimeZoneRequired,
2101}
2102
2103pub(crate) fn to_zoned_date_time_fields(
2104    partial_object: &JsObject,
2105    calendar: &Calendar,
2106    zdt_fields_type: ZdtFieldsType,
2107    context: &mut Context,
2108) -> JsResult<(ZonedDateTimeFields, Option<TimeZone>)> {
2109    let day = partial_object
2110        .get(js_string!("day"), context)?
2111        .map(|v| {
2112            let finite = v.to_finitef64(context)?;
2113            finite
2114                .as_positive_integer_with_truncation()
2115                .map_err(JsError::from)
2116        })
2117        .transpose()?;
2118    let hour = partial_object
2119        .get(js_string!("hour"), context)?
2120        .map(|v| {
2121            let finite = v.to_finitef64(context)?;
2122            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())
2123        })
2124        .transpose()?;
2125    // TODO: `temporal_rs` needs a `has_era` method
2126    let has_no_era = calendar.kind() == AnyCalendarKind::Iso
2127        || calendar.kind() == AnyCalendarKind::Chinese
2128        || calendar.kind() == AnyCalendarKind::Dangi;
2129    let (era, era_year) = if has_no_era {
2130        (None, None)
2131    } else {
2132        let era = partial_object
2133            .get(js_string!("era"), context)?
2134            .map(|v| {
2135                let v = v.to_primitive(context, PreferredType::String)?;
2136                let Some(era) = v.as_string() else {
2137                    return Err(JsError::from(
2138                        JsNativeError::typ()
2139                            .with_message("The monthCode field value must be a string."),
2140                    ));
2141                };
2142                // TODO: double check if an invalid monthCode is a range or type error.
2143                TinyAsciiStr::<19>::try_from_str(&era.to_std_string_escaped())
2144                    .map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string())))
2145            })
2146            .transpose()?;
2147        let era_year = partial_object
2148            .get(js_string!("eraYear"), context)?
2149            .map(|v| {
2150                let finite = v.to_finitef64(context)?;
2151                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())
2152            })
2153            .transpose()?;
2154        (era, era_year)
2155    };
2156    let microsecond = partial_object
2157        .get(js_string!("microsecond"), context)?
2158        .map(|v| {
2159            let finite = v.to_finitef64(context)?;
2160            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())
2161        })
2162        .transpose()?;
2163
2164    let millisecond = partial_object
2165        .get(js_string!("millisecond"), context)?
2166        .map(|v| {
2167            let finite = v.to_finitef64(context)?;
2168            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())
2169        })
2170        .transpose()?;
2171
2172    let minute = partial_object
2173        .get(js_string!("minute"), context)?
2174        .map(|v| {
2175            let finite = v.to_finitef64(context)?;
2176            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())
2177        })
2178        .transpose()?;
2179
2180    let month = partial_object
2181        .get(js_string!("month"), context)?
2182        .map(|v| {
2183            let finite = v.to_finitef64(context)?;
2184            finite
2185                .as_positive_integer_with_truncation()
2186                .map_err(JsError::from)
2187        })
2188        .transpose()?;
2189
2190    let month_code = partial_object
2191        .get(js_string!("monthCode"), context)?
2192        .map(|v| {
2193            let v = v.to_primitive(context, PreferredType::String)?;
2194            let Some(month_code) = v.as_string() else {
2195                return Err(JsNativeError::typ()
2196                    .with_message("The monthCode field value must be a string.")
2197                    .into());
2198            };
2199            MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(JsError::from)
2200        })
2201        .transpose()?;
2202
2203    let nanosecond = partial_object
2204        .get(js_string!("nanosecond"), context)?
2205        .map(|v| {
2206            let finite = v.to_finitef64(context)?;
2207            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())
2208        })
2209        .transpose()?;
2210
2211    let offset = partial_object
2212        .get(js_string!("offset"), context)?
2213        .map(|v| to_offset_string(v, context))
2214        .transpose()?;
2215
2216    let second = partial_object
2217        .get(js_string!("second"), context)?
2218        .map(|v| {
2219            let finite = v.to_finitef64(context)?;
2220            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())
2221        })
2222        .transpose()?;
2223
2224    let time_zone = match zdt_fields_type {
2225        ZdtFieldsType::NoTimeZone => None,
2226        ZdtFieldsType::TimeZoneNotRequired | ZdtFieldsType::TimeZoneRequired => {
2227            let time_zone = partial_object
2228                .get(js_string!("timeZone"), context)?
2229                .map(|v| to_temporal_timezone_identifier(v, context))
2230                .transpose()?;
2231            if zdt_fields_type == ZdtFieldsType::TimeZoneRequired && time_zone.is_none() {
2232                return Err(JsNativeError::typ()
2233                    .with_message("timeZone is required to construct ZonedDateTime.")
2234                    .into());
2235            }
2236            time_zone
2237        }
2238    };
2239
2240    let year = partial_object
2241        .get(js_string!("year"), context)?
2242        .map(|v| {
2243            let finite = v.to_finitef64(context)?;
2244            Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())
2245        })
2246        .transpose()?;
2247
2248    let calendar_fields = CalendarFields::new()
2249        .with_optional_year(year)
2250        .with_optional_month(month)
2251        .with_optional_month_code(month_code)
2252        .with_optional_day(day)
2253        .with_era(era)
2254        .with_era_year(era_year);
2255
2256    let time = PartialTime::new()
2257        .with_hour(hour)
2258        .with_minute(minute)
2259        .with_second(second)
2260        .with_millisecond(millisecond)
2261        .with_microsecond(microsecond)
2262        .with_nanosecond(nanosecond);
2263
2264    Ok((
2265        ZonedDateTimeFields {
2266            calendar_fields,
2267            time,
2268            offset,
2269        },
2270        time_zone,
2271    ))
2272}