Skip to main content

boa_engine/builtins/date/
mod.rs

1//! Boa's implementation of ECMAScript's `Date` object.
2//!
3//! More information:
4//!  - [ECMAScript reference][spec]
5//!  - [MDN documentation][mdn]
6//!
7//! [spec]: https://tc39.es/ecma262/#sec-date-objects
8//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
9
10use crate::{
11    Context, JsArgs, JsData, JsResult, JsString,
12    builtins::{
13        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
14        date::utils::{
15            MS_PER_MINUTE, date_from_time, date_string, day, hour_from_time, local_time, make_date,
16            make_day, make_full_year, make_time, min_from_time, month_from_time, ms_from_time,
17            pad_five, pad_four, pad_six, pad_three, pad_two, parse_date, sec_from_time, time_clip,
18            time_string, time_within_day, time_zone_string, to_date_string_t, utc_t, week_day,
19            year_from_time,
20        },
21    },
22    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
23    error::JsNativeError,
24    js_string,
25    object::{JsObject, internal_methods::get_prototype_from_constructor},
26    property::Attribute,
27    realm::Realm,
28    string::StaticJsStrings,
29    symbol::JsSymbol,
30    value::{JsValue, PreferredType},
31};
32use boa_gc::{Finalize, Trace};
33use boa_macros::js_str;
34
35pub(crate) mod utils;
36
37#[cfg(test)]
38mod tests;
39
40/// The internal representation of a `Date` object.
41#[derive(Debug, Copy, Clone, Trace, Finalize, JsData)]
42#[boa_gc(empty_trace)]
43pub struct Date(f64);
44
45impl Date {
46    /// Creates a new `Date`.
47    pub(crate) const fn new(dt: f64) -> Self {
48        Self(dt)
49    }
50
51    /// Creates a new `Date` from the current UTC time of the host.
52    pub(crate) fn utc_now(context: &mut Context) -> Self {
53        Self(context.clock().system_time_millis() as f64)
54    }
55
56    /// Formats this date as an ISO 8601 string for display purposes.
57    ///
58    /// Returns `None` if the date value is not finite (i.e. `Invalid Date`).
59    pub(crate) fn to_iso_display(self) -> Option<String> {
60        let tv = self.0;
61        if !tv.is_finite() {
62            return None;
63        }
64        let year = year_from_time(tv);
65        let year_str = if year.is_positive() && year >= 10000 {
66            format!("+{year:06}")
67        } else if year >= 0 {
68            format!("{year:04}")
69        } else {
70            format!("-{:06}", year.unsigned_abs())
71        };
72        Some(format!(
73            "{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z",
74            year_str,
75            month_from_time(tv) + 1,
76            date_from_time(tv),
77            hour_from_time(tv),
78            min_from_time(tv),
79            sec_from_time(tv),
80            ms_from_time(tv),
81        ))
82    }
83}
84
85impl IntrinsicObject for Date {
86    fn init(realm: &Realm) {
87        let to_utc_string = BuiltInBuilder::callable(realm, Self::to_utc_string)
88            .name(js_string!("toUTCString"))
89            .length(0)
90            .build();
91
92        let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive)
93            .name(js_string!("[Symbol.toPrimitive]"))
94            .length(1)
95            .build();
96
97        let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)
98            .static_method(Self::now, js_string!("now"), 0)
99            .static_method(Self::parse, js_string!("parse"), 1)
100            .static_method(Self::utc, js_string!("UTC"), 7)
101            .method(Self::get_date::<true>, js_string!("getDate"), 0)
102            .method(Self::get_day::<true>, js_string!("getDay"), 0)
103            .method(Self::get_full_year::<true>, js_string!("getFullYear"), 0)
104            .method(Self::get_hours::<true>, js_string!("getHours"), 0)
105            .method(
106                Self::get_milliseconds::<true>,
107                js_string!("getMilliseconds"),
108                0,
109            )
110            .method(Self::get_minutes::<true>, js_string!("getMinutes"), 0)
111            .method(Self::get_month::<true>, js_string!("getMonth"), 0)
112            .method(Self::get_seconds::<true>, js_string!("getSeconds"), 0)
113            .method(Self::get_time, js_string!("getTime"), 0)
114            .method(
115                Self::get_timezone_offset,
116                js_string!("getTimezoneOffset"),
117                0,
118            )
119            .method(Self::get_date::<false>, js_string!("getUTCDate"), 0)
120            .method(Self::get_day::<false>, js_string!("getUTCDay"), 0)
121            .method(
122                Self::get_full_year::<false>,
123                js_string!("getUTCFullYear"),
124                0,
125            )
126            .method(Self::get_hours::<false>, js_string!("getUTCHours"), 0)
127            .method(
128                Self::get_milliseconds::<false>,
129                js_string!("getUTCMilliseconds"),
130                0,
131            )
132            .method(Self::get_minutes::<false>, js_string!("getUTCMinutes"), 0)
133            .method(Self::get_month::<false>, js_string!("getUTCMonth"), 0)
134            .method(Self::get_seconds::<false>, js_string!("getUTCSeconds"), 0)
135            .method(Self::get_year, js_string!("getYear"), 0)
136            .method(Self::set_date::<true>, js_string!("setDate"), 1)
137            .method(Self::set_full_year::<true>, js_string!("setFullYear"), 3)
138            .method(Self::set_hours::<true>, js_string!("setHours"), 4)
139            .method(
140                Self::set_milliseconds::<true>,
141                js_string!("setMilliseconds"),
142                1,
143            )
144            .method(Self::set_minutes::<true>, js_string!("setMinutes"), 3)
145            .method(Self::set_month::<true>, js_string!("setMonth"), 2)
146            .method(Self::set_seconds::<true>, js_string!("setSeconds"), 2)
147            .method(Self::set_time, js_string!("setTime"), 1)
148            .method(Self::set_date::<false>, js_string!("setUTCDate"), 1)
149            .method(
150                Self::set_full_year::<false>,
151                js_string!("setUTCFullYear"),
152                3,
153            )
154            .method(Self::set_hours::<false>, js_string!("setUTCHours"), 4)
155            .method(
156                Self::set_milliseconds::<false>,
157                js_string!("setUTCMilliseconds"),
158                1,
159            )
160            .method(Self::set_minutes::<false>, js_string!("setUTCMinutes"), 3)
161            .method(Self::set_month::<false>, js_string!("setUTCMonth"), 2)
162            .method(Self::set_seconds::<false>, js_string!("setUTCSeconds"), 2)
163            .method(Self::set_year, js_string!("setYear"), 1)
164            .method(Self::to_date_string, js_string!("toDateString"), 0)
165            .method(Self::to_iso_string, js_string!("toISOString"), 0)
166            .method(Self::to_json, js_string!("toJSON"), 1)
167            .method(
168                Self::to_locale_date_string,
169                js_string!("toLocaleDateString"),
170                0,
171            )
172            .method(Self::to_locale_string, js_string!("toLocaleString"), 0)
173            .method(
174                Self::to_locale_time_string,
175                js_string!("toLocaleTimeString"),
176                0,
177            )
178            .method(Self::to_string, js_string!("toString"), 0)
179            .method(Self::to_time_string, js_string!("toTimeString"), 0)
180            .method(Self::value_of, js_string!("valueOf"), 0)
181            .property(
182                js_string!("toGMTString"),
183                to_utc_string.clone(),
184                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
185            )
186            .property(
187                js_string!("toUTCString"),
188                to_utc_string,
189                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
190            )
191            .property(
192                JsSymbol::to_primitive(),
193                to_primitive,
194                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
195            );
196
197        #[cfg(feature = "temporal")]
198        let builder = builder.method(
199            Self::to_temporal_instant,
200            js_string!("toTemporalInstant"),
201            0,
202        );
203
204        builder.build();
205    }
206
207    fn get(intrinsics: &Intrinsics) -> JsObject {
208        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()
209    }
210}
211
212impl BuiltInObject for Date {
213    const NAME: JsString = StaticJsStrings::DATE;
214}
215
216impl BuiltInConstructor for Date {
217    const CONSTRUCTOR_ARGUMENTS: usize = 7;
218    const PROTOTYPE_STORAGE_SLOTS: usize = 48;
219    const CONSTRUCTOR_STORAGE_SLOTS: usize = 3;
220
221    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
222        StandardConstructors::date;
223
224    /// [`Date ( ...values )`][spec]
225    ///
226    /// - When called as a function, returns a string displaying the current time in the UTC timezone.
227    /// - When called as a constructor, it returns a new `Date` object from the provided arguments.
228    ///   The [MDN documentation][mdn] has a more extensive explanation on the usages and return
229    ///   values for all possible arguments.
230    ///
231    /// [spec]: https://tc39.es/ecma262/#sec-date-constructor
232    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
233    fn constructor(
234        new_target: &JsValue,
235        args: &[JsValue],
236        context: &mut Context,
237    ) -> JsResult<JsValue> {
238        // 1. If NewTarget is undefined, then
239        if new_target.is_undefined() {
240            // a. Let now be the time value (UTC) identifying the current time.
241            let now = context.clock().system_time_millis();
242
243            // b. Return ToDateString(now).
244            return Ok(JsValue::from(to_date_string_t(
245                now as f64,
246                context.host_hooks().as_ref(),
247            )));
248        }
249
250        // 2. Let numberOfArgs be the number of elements in values.
251        let dv = match args {
252            // 3. If numberOfArgs = 0, then
253            [] => {
254                // a. Let dv be the time value (UTC) identifying the current time.
255                Self::utc_now(context)
256            }
257            // 4. Else if numberOfArgs = 1, then
258            // a. Let value be values[0].
259            [value] => {
260                // b. If value is an Object and value has a [[DateValue]] internal slot, then
261                let object = value.as_object();
262                let tv =
263                    if let Some(date) = object.as_ref().and_then(JsObject::downcast_ref::<Self>) {
264                        // i. Let tv be value.[[DateValue]].
265                        date.0
266                    }
267                    // c. Else,
268                    else {
269                        // i. Let v be ? ToPrimitive(value).
270                        let v = value.to_primitive(context, PreferredType::Default)?;
271
272                        // ii. If v is a String, then
273                        if let Some(v) = v.as_string() {
274                            // 1. Assert: The next step never returns an abrupt completion because v is a String.
275                            // 2. Let tv be the result of parsing v as a date, in exactly the same manner as for the parse method (21.4.3.2).
276                            let tv = parse_date(&v, context.host_hooks().as_ref());
277                            if let Some(tv) = tv {
278                                tv as f64
279                            } else {
280                                f64::NAN
281                            }
282                        }
283                        // iii. Else,
284                        else {
285                            // 1. Let tv be ? ToNumber(v).
286                            v.to_number(context)?
287                        }
288                    };
289
290                // d. Let dv be TimeClip(tv).
291                Self(time_clip(tv))
292            }
293            // 5. Else,
294            _ => {
295                // Separating this into its own function to simplify the logic.
296                //let dt = Self::construct_date(args, context)?
297                //    .and_then(|dt| context.host_hooks().local_from_naive_local(dt).earliest());
298                //Self(dt.map(|dt| dt.timestamp_millis()))
299
300                // a. Assert: numberOfArgs ≥ 2.
301                // b. Let y be ? ToNumber(values[0]).
302                let y = args.get_or_undefined(0).to_number(context)?;
303
304                // c. Let m be ? ToNumber(values[1]).
305                let m = args.get_or_undefined(1).to_number(context)?;
306
307                // d. If numberOfArgs > 2, let dt be ? ToNumber(values[2]); else let dt be 1𝔽.
308                let dt = args.get(2).map_or(Ok(1.0), |n| n.to_number(context))?;
309
310                // e. If numberOfArgs > 3, let h be ? ToNumber(values[3]); else let h be +0𝔽.
311                let h = args.get(3).map_or(Ok(0.0), |n| n.to_number(context))?;
312
313                // f. If numberOfArgs > 4, let min be ? ToNumber(values[4]); else let min be +0𝔽.
314                let min = args.get(4).map_or(Ok(0.0), |n| n.to_number(context))?;
315
316                // g. If numberOfArgs > 5, let s be ? ToNumber(values[5]); else let s be +0𝔽.
317                let s = args.get(5).map_or(Ok(0.0), |n| n.to_number(context))?;
318
319                // h. If numberOfArgs > 6, let milli be ? ToNumber(values[6]); else let milli be +0𝔽.
320                let milli = args.get(6).map_or(Ok(0.0), |n| n.to_number(context))?;
321
322                // i. Let yr be MakeFullYear(y).
323                let yr = make_full_year(y);
324
325                // j. Let finalDate be MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)).
326                let final_date = make_date(make_day(yr, m, dt), make_time(h, min, s, milli));
327
328                // k. Let dv be TimeClip(UTC(finalDate)).
329                Self(time_clip(utc_t(final_date, context.host_hooks().as_ref())))
330            }
331        };
332
333        // 6. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Date.prototype%", « [[DateValue]] »).
334        let prototype =
335            get_prototype_from_constructor(new_target, StandardConstructors::date, context)?;
336
337        // 7. Set O.[[DateValue]] to dv.
338        let obj =
339            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, dv);
340
341        // 8. Return O.
342        Ok(obj.into())
343    }
344}
345
346impl Date {
347    /// `Date.now()`
348    ///
349    /// The static `Date.now()` method returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC.
350    ///
351    /// More information:
352    ///  - [ECMAScript reference][spec]
353    ///  - [MDN documentation][mdn]
354    ///
355    /// [spec]: https://tc39.es/ecma262/#sec-date.now
356    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now
357    #[allow(clippy::unnecessary_wraps)]
358    pub(crate) fn now(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
359        Ok(JsValue::new(context.clock().system_time_millis()))
360    }
361
362    /// `Date.parse()`
363    ///
364    /// The `Date.parse()` method parses a string representation of a date, and returns the number of milliseconds since
365    /// January 1, 1970, 00:00:00 UTC or `NaN` if the string is unrecognized or, in some cases, contains illegal date
366    /// values.
367    ///
368    /// More information:
369    ///  - [ECMAScript reference][spec]
370    ///  - [MDN documentation][mdn]
371    ///
372    /// [spec]: https://tc39.es/ecma262/#sec-date.parse
373    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
374    pub(crate) fn parse(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
375        let date = args.get_or_undefined(0).to_string(context)?;
376        Ok(parse_date(&date, context.host_hooks().as_ref())
377            .map_or(JsValue::from(f64::NAN), JsValue::from))
378    }
379
380    /// `Date.UTC()`
381    ///
382    /// The `Date.UTC()` method accepts parameters similar to the `Date` constructor, but treats them as UTC.
383    ///
384    /// More information:
385    ///  - [ECMAScript reference][spec]
386    ///  - [MDN documentation][mdn]
387    ///
388    /// [spec]: https://tc39.es/ecma262/#sec-date.utc
389    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC
390    pub(crate) fn utc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
391        // 1. Let y be ? ToNumber(year).
392        let y = args.get_or_undefined(0).to_number(context)?;
393
394        // 2. If month is present, let m be ? ToNumber(month); else let m be +0𝔽.
395        let m = args
396            .get(1)
397            .map_or(Ok(0f64), |value| value.to_number(context))?;
398
399        // 3. If date is present, let dt be ? ToNumber(date); else let dt be 1𝔽.
400        let dt = args
401            .get(2)
402            .map_or(Ok(1f64), |value| value.to_number(context))?;
403
404        // 4. If hours is present, let h be ? ToNumber(hours); else let h be +0𝔽.
405        let h = args
406            .get(3)
407            .map_or(Ok(0f64), |value| value.to_number(context))?;
408
409        // 5. If minutes is present, let min be ? ToNumber(minutes); else let min be +0𝔽.
410        let min = args
411            .get(4)
412            .map_or(Ok(0f64), |value| value.to_number(context))?;
413
414        // 6. If seconds is present, let s be ? ToNumber(seconds); else let s be +0𝔽.
415        let s = args
416            .get(5)
417            .map_or(Ok(0f64), |value| value.to_number(context))?;
418
419        // 7. If ms is present, let milli be ? ToNumber(ms); else let milli be +0𝔽.
420        let milli = args
421            .get(6)
422            .map_or(Ok(0f64), |value| value.to_number(context))?;
423
424        // 8. Let yr be MakeFullYear(y).
425        let yr = make_full_year(y);
426
427        // 9. Return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))).
428        Ok(JsValue::from(time_clip(make_date(
429            make_day(yr, m, dt),
430            make_time(h, min, s, milli),
431        ))))
432    }
433
434    /// [`Date.prototype.getDate ( )`][local] and
435    /// [`Date.prototype.getUTCDate ( )`][utc].
436    ///
437    /// The `getDate()` method returns the day of the month for the specified date.
438    ///
439    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getdate
440    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcdate
441    pub(crate) fn get_date<const LOCAL: bool>(
442        this: &JsValue,
443        _args: &[JsValue],
444        context: &mut Context,
445    ) -> JsResult<JsValue> {
446        // 1. Let dateObject be the this value.
447        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
448        // 3. Let t be dateObject.[[DateValue]].
449        let t = this
450            .as_object()
451            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
452            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
453            .0;
454
455        // 4. If t is NaN, return NaN.
456        if t.is_nan() {
457            return Ok(JsValue::new(f64::NAN));
458        }
459
460        if LOCAL {
461            // 5. Return DateFromTime(LocalTime(t)).
462            Ok(JsValue::from(date_from_time(local_time(
463                t,
464                context.host_hooks().as_ref(),
465            ))))
466        } else {
467            // 5. Return DateFromTime(t).
468            Ok(JsValue::from(date_from_time(t)))
469        }
470    }
471
472    /// [`Date.prototype.getDay ( )`][local] and
473    /// [`Date.prototype.getUTCDay ( )`][utc].
474    ///
475    /// The `getDay()` method returns the day of the week for the specified date, where 0 represents
476    /// Sunday.
477    ///
478    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getday
479    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcday
480    pub(crate) fn get_day<const LOCAL: bool>(
481        this: &JsValue,
482        _args: &[JsValue],
483        context: &mut Context,
484    ) -> JsResult<JsValue> {
485        // 1. Let dateObject be the this value.
486        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
487        // 3. Let t be dateObject.[[DateValue]].
488        let t = this
489            .as_object()
490            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
491            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
492            .0;
493
494        // 4. If t is NaN, return NaN.
495        if t.is_nan() {
496            return Ok(JsValue::from(f64::NAN));
497        }
498
499        if LOCAL {
500            // 5. Return WeekDay(LocalTime(t)).
501            Ok(JsValue::from(week_day(local_time(
502                t,
503                context.host_hooks().as_ref(),
504            ))))
505        } else {
506            // 5. Return WeekDay(t).
507            Ok(JsValue::from(week_day(t)))
508        }
509    }
510
511    /// [`Date.prototype.getYear()`][spec].
512    ///
513    /// The `getYear()` method returns the year in the specified date according to local time.
514    /// Because `getYear()` does not return full years ("year 2000 problem"), it is no longer used
515    /// and has been replaced by the `getFullYear()` method.
516    ///
517    /// More information:
518    ///  - [MDN documentation][mdn]
519    ///
520    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getyear
521    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getYear
522    pub(crate) fn get_year(
523        this: &JsValue,
524        _args: &[JsValue],
525        context: &mut Context,
526    ) -> JsResult<JsValue> {
527        // 1. Let dateObject be the this value.
528        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
529        // 3. Let t be dateObject.[[DateValue]].
530        let t = this
531            .as_object()
532            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
533            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
534            .0;
535
536        // 4. If t is NaN, return NaN.
537        if t.is_nan() {
538            return Ok(JsValue::from(f64::NAN));
539        }
540
541        // 5. Return YearFromTime(LocalTime(t)) - 1900𝔽.
542        Ok(JsValue::from(
543            year_from_time(local_time(t, context.host_hooks().as_ref())) - 1900,
544        ))
545    }
546
547    /// [`Date.prototype.getFullYear ( )`][local] and
548    /// [`Date.prototype.getUTCFullYear ( )`][utc].
549    ///
550    /// The `getFullYear()` method returns the year of the specified date.
551    ///
552    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getfullyear
553    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcfullyear
554    pub(crate) fn get_full_year<const LOCAL: bool>(
555        this: &JsValue,
556        _args: &[JsValue],
557        context: &mut Context,
558    ) -> JsResult<JsValue> {
559        // 1. Let dateObject be the this value.
560        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
561        // 3. Let t be dateObject.[[DateValue]].
562        let t = this
563            .as_object()
564            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
565            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
566            .0;
567
568        // 4. If t is NaN, return NaN.
569        if t.is_nan() {
570            return Ok(JsValue::from(f64::NAN));
571        }
572
573        if LOCAL {
574            // 5. Return YearFromTime(LocalTime(t)).
575            Ok(JsValue::from(year_from_time(local_time(
576                t,
577                context.host_hooks().as_ref(),
578            ))))
579        } else {
580            // 5. Return YearFromTime(t).
581            Ok(JsValue::from(year_from_time(t)))
582        }
583    }
584
585    /// [`Date.prototype.getHours ( )`][local] and
586    /// [`Date.prototype.getUTCHours ( )`][utc].
587    ///
588    /// The `getHours()` method returns the hour for the specified date.
589    ///
590    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.gethours
591    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutchours
592    pub(crate) fn get_hours<const LOCAL: bool>(
593        this: &JsValue,
594        _args: &[JsValue],
595        context: &mut Context,
596    ) -> JsResult<JsValue> {
597        // 1. Let dateObject be the this value.
598        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
599        // 3. Let t be dateObject.[[DateValue]].
600        let t = this
601            .as_object()
602            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
603            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
604            .0;
605
606        // 4. If t is NaN, return NaN.
607        if t.is_nan() {
608            return Ok(JsValue::from(f64::NAN));
609        }
610
611        if LOCAL {
612            // 5. Return HourFromTime(LocalTime(t)).
613            Ok(JsValue::from(hour_from_time(local_time(
614                t,
615                context.host_hooks().as_ref(),
616            ))))
617        } else {
618            // 5. Return HourFromTime(t).
619            Ok(JsValue::from(hour_from_time(t)))
620        }
621    }
622
623    /// [`Date.prototype.getMilliseconds ( )`][local] and
624    /// [`Date.prototype.getUTCMilliseconds ( )`][utc].
625    ///
626    /// The `getMilliseconds()` method returns the milliseconds in the specified date.
627    ///
628    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getmilliseconds
629    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcmilliseconds
630    pub(crate) fn get_milliseconds<const LOCAL: bool>(
631        this: &JsValue,
632        _args: &[JsValue],
633        context: &mut Context,
634    ) -> JsResult<JsValue> {
635        // 1. Let dateObject be the this value.
636        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
637        // 3. Let t be dateObject.[[DateValue]].
638        let t = this
639            .as_object()
640            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
641            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
642            .0;
643
644        // 4. If t is NaN, return NaN.
645        if t.is_nan() {
646            return Ok(JsValue::from(f64::NAN));
647        }
648
649        if LOCAL {
650            // 5. Return msFromTime(LocalTime(t)).
651            Ok(JsValue::from(ms_from_time(local_time(
652                t,
653                context.host_hooks().as_ref(),
654            ))))
655        } else {
656            // 5. Return msFromTime(t).
657            Ok(JsValue::from(ms_from_time(t)))
658        }
659    }
660
661    /// [`Date.prototype.getMinutes ( )`][local] and
662    /// [`Date.prototype.getUTCMinutes ( )`][utc].
663    ///
664    /// The `getMinutes()` method returns the minutes in the specified date.
665    ///
666    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getminutes
667    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcminutes
668    pub(crate) fn get_minutes<const LOCAL: bool>(
669        this: &JsValue,
670        _args: &[JsValue],
671        context: &mut Context,
672    ) -> JsResult<JsValue> {
673        // 1. Let dateObject be the this value.
674        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
675        // 3. Let t be dateObject.[[DateValue]].
676        let t = this
677            .as_object()
678            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
679            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
680            .0;
681
682        // 4. If t is NaN, return NaN.
683        if t.is_nan() {
684            return Ok(JsValue::from(f64::NAN));
685        }
686
687        if LOCAL {
688            // 5. Return MinFromTime(LocalTime(t)).
689            Ok(JsValue::from(min_from_time(local_time(
690                t,
691                context.host_hooks().as_ref(),
692            ))))
693        } else {
694            // 5. Return MinFromTime(t).
695            Ok(JsValue::from(min_from_time(t)))
696        }
697    }
698
699    /// [`Date.prototype.getMonth ( )`][local] and
700    /// [`Date.prototype.getUTCMonth ( )`][utc].
701    ///
702    /// The `getMonth()` method returns the month in the specified date, as a zero-based value
703    /// (where zero indicates the first month of the year).
704    ///
705    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getmonth
706    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcmonth
707    pub(crate) fn get_month<const LOCAL: bool>(
708        this: &JsValue,
709        _args: &[JsValue],
710        context: &mut Context,
711    ) -> JsResult<JsValue> {
712        // 1. Let dateObject be the this value.
713        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
714        // 3. Let t be dateObject.[[DateValue]].
715        let t = this
716            .as_object()
717            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
718            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
719            .0;
720
721        // 4. If t is NaN, return NaN.
722        if t.is_nan() {
723            return Ok(JsValue::from(f64::NAN));
724        }
725
726        if LOCAL {
727            // 5. Return MonthFromTime(LocalTime(t)).
728            Ok(JsValue::from(month_from_time(local_time(
729                t,
730                context.host_hooks().as_ref(),
731            ))))
732        } else {
733            // 5. Return MonthFromTime(t).
734            Ok(JsValue::from(month_from_time(t)))
735        }
736    }
737
738    /// [`Date.prototype.getSeconds ( )`][local] and
739    /// [`Date.prototype.getUTCSeconds ( )`][utc].
740    ///
741    /// The `getSeconds()` method returns the seconds in the specified date.
742    ///
743    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getseconds
744    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcseconds
745    pub(crate) fn get_seconds<const LOCAL: bool>(
746        this: &JsValue,
747        _args: &[JsValue],
748        context: &mut Context,
749    ) -> JsResult<JsValue> {
750        // 1. Let dateObject be the this value.
751        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
752        // 3. Let t be dateObject.[[DateValue]].
753        let t = this
754            .as_object()
755            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
756            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
757            .0;
758
759        // 4. If t is NaN, return NaN.
760        if t.is_nan() {
761            return Ok(JsValue::from(f64::NAN));
762        }
763
764        if LOCAL {
765            // 5. Return SecFromTime(LocalTime(t)).
766            Ok(JsValue::from(sec_from_time(local_time(
767                t,
768                context.host_hooks().as_ref(),
769            ))))
770        } else {
771            // 5. Return SecFromTime(t).
772            Ok(JsValue::from(sec_from_time(t)))
773        }
774    }
775
776    /// `Date.prototype.getTime()`.
777    ///
778    /// The `getTime()` method returns the number of milliseconds since the Unix Epoch.
779    ///
780    /// More information:
781    ///  - [ECMAScript reference][spec]
782    ///  - [MDN documentation][mdn]
783    ///
784    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettime
785    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime
786    pub(crate) fn get_time(
787        this: &JsValue,
788        _args: &[JsValue],
789        _context: &mut Context,
790    ) -> JsResult<JsValue> {
791        // 1. Let dateObject be the this value.
792        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
793        // 3. Return dateObject.[[DateValue]].
794        Ok(this
795            .as_object()
796            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
797            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
798            .0
799            .into())
800    }
801
802    /// `Date.prototype.getTimeZoneOffset()`.
803    ///
804    /// The `getTimezoneOffset()` method returns the time zone difference, in minutes, from current locale (host system
805    /// settings) to UTC.
806    ///
807    /// More information:
808    ///  - [ECMAScript reference][spec]
809    ///  - [MDN documentation][mdn]
810    ///
811    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettimezoneoffset
812    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset
813    pub(crate) fn get_timezone_offset(
814        this: &JsValue,
815        _: &[JsValue],
816        context: &mut Context,
817    ) -> JsResult<JsValue> {
818        // 1. Let dateObject be the this value.
819        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
820        // 3. Let t be dateObject.[[DateValue]].
821        let t = this
822            .as_object()
823            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
824            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
825            .0;
826
827        // 4. If t is NaN, return NaN.
828        if t.is_nan() {
829            return Ok(JsValue::from(f64::NAN));
830        }
831
832        // 5. Return (t - LocalTime(t)) / msPerMinute.
833        Ok(JsValue::from(
834            (t - local_time(t, context.host_hooks().as_ref())) / MS_PER_MINUTE,
835        ))
836    }
837
838    /// [`Date.prototype.setDate ( date )`][local] and
839    /// [`Date.prototype.setUTCDate ( date )`][utc].
840    ///
841    /// The `setDate()` method sets the day of the `Date` object relative to the beginning of the
842    /// currently set month.
843    ///
844    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setdate
845    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcdate
846    pub(crate) fn set_date<const LOCAL: bool>(
847        this: &JsValue,
848        args: &[JsValue],
849        context: &mut Context,
850    ) -> JsResult<JsValue> {
851        // 1. Let dateObject be the this value.
852        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
853        let object = this.as_object();
854        let date = object
855            .as_ref()
856            .and_then(JsObject::downcast_ref::<Date>)
857            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
858
859        // 3. Let t be dateObject.[[DateValue]].
860        let mut t = date.0;
861
862        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
863        // ToNumber() may call userland code which can modify the underlying date
864        // which will cause a panic. In order to avoid this, we drop the borrow,
865        // here and only `downcast_mut` when date will be modified.
866        drop(date);
867
868        // 4. Let dt be ? ToNumber(date).
869        let dt = args.get_or_undefined(0).to_number(context)?;
870
871        // 5. If t is NaN, return NaN.
872        if t.is_nan() {
873            return Ok(JsValue::from(f64::NAN));
874        }
875
876        if LOCAL {
877            // 6. Set t to LocalTime(t).
878            t = local_time(t, context.host_hooks().as_ref());
879        }
880
881        // 7. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)).
882        let new_date = make_date(
883            make_day(year_from_time(t).into(), month_from_time(t).into(), dt),
884            time_within_day(t),
885        );
886
887        let u = if LOCAL {
888            // 8. Let u be TimeClip(UTC(newDate)).
889            time_clip(utc_t(new_date, context.host_hooks().as_ref()))
890        } else {
891            // 8. Let v be TimeClip(newDate).
892            time_clip(new_date)
893        };
894
895        let object = this.as_object();
896        let mut date_mut = object
897            .as_ref()
898            .and_then(JsObject::downcast_mut::<Date>)
899            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
900
901        // 9. Set dateObject.[[DateValue]] to u.
902        date_mut.0 = u;
903
904        // 10. Return u.
905        Ok(JsValue::from(u))
906    }
907
908    /// [`Date.prototype.setFullYear ( year [ , month [ , date ] ] )`][local] and
909    /// [Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] )][utc].
910    ///
911    /// The `setFullYear()` method sets the full year for a specified date and returns the new
912    /// timestamp.
913    ///
914    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setfullyear
915    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcfullyear
916    pub(crate) fn set_full_year<const LOCAL: bool>(
917        this: &JsValue,
918        args: &[JsValue],
919        context: &mut Context,
920    ) -> JsResult<JsValue> {
921        // 1. Let dateObject be the this value.
922        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
923        let object = this.as_object();
924        let date = object
925            .as_ref()
926            .and_then(JsObject::downcast_ref::<Date>)
927            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
928
929        // 3. Let t be dateObject.[[DateValue]].
930        let t = date.0;
931
932        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
933        // ToNumber() may call userland code which can modify the underlying date
934        // which will cause a panic. In order to avoid this, we drop the borrow,
935        // here and only `downcast_mut` when date will be modified.
936        drop(date);
937
938        let t = if LOCAL {
939            // 5. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t).
940            if t.is_nan() {
941                0.0
942            } else {
943                local_time(t, context.host_hooks().as_ref())
944            }
945        } else {
946            // 4. If t is NaN, set t to +0𝔽.
947            if t.is_nan() { 0.0 } else { t }
948        };
949
950        // 4. Let y be ? ToNumber(year).
951        let y = args.get_or_undefined(0).to_number(context)?;
952
953        // 6. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month).
954        let m = if let Some(month) = args.get(1) {
955            month.to_number(context)?
956        } else {
957            month_from_time(t).into()
958        };
959
960        // 7. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date).
961        let dt = if let Some(date) = args.get(2) {
962            date.to_number(context)?
963        } else {
964            date_from_time(t).into()
965        };
966
967        // 8. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)).
968        let new_date = make_date(make_day(y, m, dt), time_within_day(t));
969
970        let u = if LOCAL {
971            // 9. Let u be TimeClip(UTC(newDate)).
972            time_clip(utc_t(new_date, context.host_hooks().as_ref()))
973        } else {
974            // 9. Let u be TimeClip(newDate).
975            time_clip(new_date)
976        };
977
978        let object = this.as_object();
979        let mut date_mut = object
980            .as_ref()
981            .and_then(JsObject::downcast_mut::<Date>)
982            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
983
984        // 10. Set dateObject.[[DateValue]] to u.
985        date_mut.0 = u;
986
987        // 11. Return u.
988        Ok(JsValue::from(u))
989    }
990
991    /// [`Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] )`][local] and
992    /// [`Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] )`][utc].
993    ///
994    /// The `setHours()` method sets the hours for a specified date, and returns the number
995    /// of milliseconds since January 1, 1970 00:00:00 UTC until the time represented by the
996    /// updated `Date` instance.
997    ///
998    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.sethours
999    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutchours
1000    #[allow(clippy::many_single_char_names)]
1001    pub(crate) fn set_hours<const LOCAL: bool>(
1002        this: &JsValue,
1003        args: &[JsValue],
1004        context: &mut Context,
1005    ) -> JsResult<JsValue> {
1006        // 1. Let dateObject be the this value.
1007        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1008        let object = this.as_object();
1009        let date = object
1010            .as_ref()
1011            .and_then(JsObject::downcast_ref::<Date>)
1012            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1013
1014        // 3. Let t be dateObject.[[DateValue]].
1015        let mut t = date.0;
1016
1017        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
1018        // ToNumber() may call userland code which can modify the underlying date
1019        // which will cause a panic. In order to avoid this, we drop the borrow,
1020        // here and only `downcast_mut` when date will be modified.
1021        drop(date);
1022
1023        // 4. Let h be ? ToNumber(hour).
1024        let h = args.get_or_undefined(0).to_number(context)?;
1025
1026        // 5. If min is present, let m be ? ToNumber(min).
1027        let m = args.get(1).map(|v| v.to_number(context)).transpose()?;
1028
1029        // 6. If sec is present, let s be ? ToNumber(sec).
1030        let s = args.get(2).map(|v| v.to_number(context)).transpose()?;
1031
1032        // 7. If ms is present, let milli be ? ToNumber(ms).
1033        let milli = args.get(3).map(|v| v.to_number(context)).transpose()?;
1034
1035        // 8. If t is NaN, return NaN.
1036        if t.is_nan() {
1037            return Ok(JsValue::from(f64::NAN));
1038        }
1039
1040        if LOCAL {
1041            // 9. Set t to LocalTime(t).
1042            t = local_time(t, context.host_hooks().as_ref());
1043        }
1044
1045        // 10. If min is not present, let m be MinFromTime(t).
1046        let m: f64 = m.unwrap_or_else(|| min_from_time(t).into());
1047
1048        // 11. If sec is not present, let s be SecFromTime(t).
1049        let s = s.unwrap_or_else(|| sec_from_time(t).into());
1050
1051        // 12. If ms is not present, let milli be msFromTime(t).
1052        let milli = milli.unwrap_or_else(|| ms_from_time(t).into());
1053
1054        // 13. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)).
1055        let date = make_date(day(t), make_time(h, m, s, milli));
1056
1057        let u = if LOCAL {
1058            // 14. Let u be TimeClip(UTC(date)).
1059            time_clip(utc_t(date, context.host_hooks().as_ref()))
1060        } else {
1061            // 14. Let u be TimeClip(date).
1062            time_clip(date)
1063        };
1064
1065        let object = this.as_object();
1066        let mut date_mut = object
1067            .as_ref()
1068            .and_then(JsObject::downcast_mut::<Date>)
1069            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1070
1071        // 15. Set dateObject.[[DateValue]] to u.
1072        date_mut.0 = u;
1073
1074        // 16. Return u.
1075        Ok(JsValue::from(u))
1076    }
1077
1078    /// [`Date.prototype.setMilliseconds ( ms )`[local] and
1079    /// [`Date.prototype.setUTCMilliseconds ( ms )`][utc].
1080    ///
1081    /// The `setMilliseconds()` method sets the milliseconds for a specified date according to local time.
1082    ///
1083    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds
1084    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcmilliseconds
1085    pub(crate) fn set_milliseconds<const LOCAL: bool>(
1086        this: &JsValue,
1087        args: &[JsValue],
1088        context: &mut Context,
1089    ) -> JsResult<JsValue> {
1090        // 1. Let dateObject be the this value.
1091        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1092        let object = this.as_object();
1093        let date = object
1094            .as_ref()
1095            .and_then(JsObject::downcast_ref::<Date>)
1096            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1097
1098        // 3. Let t be dateObject.[[DateValue]].
1099        let mut t = date.0;
1100
1101        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
1102        // ToNumber() may call userland code which can modify the underlying date
1103        // which will cause a panic. In order to avoid this, we drop the borrow,
1104        // here and only `downcast_mut` when date will be modified.
1105        drop(date);
1106
1107        // 4. Set ms to ? ToNumber(ms).
1108        let ms = args.get_or_undefined(0).to_number(context)?;
1109
1110        // 5. If t is NaN, return NaN.
1111        if t.is_nan() {
1112            return Ok(JsValue::from(f64::NAN));
1113        }
1114
1115        if LOCAL {
1116            // 6. Set t to LocalTime(t).
1117            t = local_time(t, context.host_hooks().as_ref());
1118        }
1119
1120        // 7. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms).
1121        let time = make_time(
1122            hour_from_time(t).into(),
1123            min_from_time(t).into(),
1124            sec_from_time(t).into(),
1125            ms,
1126        );
1127
1128        let u = if LOCAL {
1129            // 8. Let u be TimeClip(UTC(MakeDate(Day(t), time))).
1130            time_clip(utc_t(
1131                make_date(day(t), time),
1132                context.host_hooks().as_ref(),
1133            ))
1134        } else {
1135            // 8. Let u be TimeClip(MakeDate(Day(t), time)).
1136            time_clip(make_date(day(t), time))
1137        };
1138
1139        let object = this.as_object();
1140        let mut date_mut = object
1141            .as_ref()
1142            .and_then(JsObject::downcast_mut::<Date>)
1143            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1144
1145        // 9. Set dateObject.[[DateValue]] to u.
1146        date_mut.0 = u;
1147
1148        // 10. Return u.
1149        Ok(JsValue::from(u))
1150    }
1151
1152    /// [`Date.prototype.setMinutes ( min [ , sec [ , ms ] ] )`][local] and
1153    /// [`Date.prototype.setUTCMinutes ( min [ , sec [ , ms ] ] )`][utc].
1154    ///
1155    /// The `setMinutes()` method sets the minutes for a specified date.
1156    ///
1157    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setminutes
1158    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcminutes
1159    pub(crate) fn set_minutes<const LOCAL: bool>(
1160        this: &JsValue,
1161        args: &[JsValue],
1162        context: &mut Context,
1163    ) -> JsResult<JsValue> {
1164        // 1. Let dateObject be the this value.
1165        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1166        let object = this.as_object();
1167        let date = object
1168            .as_ref()
1169            .and_then(JsObject::downcast_ref::<Date>)
1170            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1171
1172        // 3. Let t be dateObject.[[DateValue]].
1173        let mut t = date.0;
1174
1175        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
1176        // ToNumber() may call userland code which can modify the underlying date
1177        // which will cause a panic. In order to avoid this, we drop the borrow,
1178        // here and only `downcast_mut` when date will be modified.
1179        drop(date);
1180
1181        // 4. Let m be ? ToNumber(min).
1182        let m = args.get_or_undefined(0).to_number(context)?;
1183
1184        // 5. If sec is present, let s be ? ToNumber(sec).
1185        let s = args.get(1).map(|v| v.to_number(context)).transpose()?;
1186
1187        // 6. If ms is present, let milli be ? ToNumber(ms).
1188        let milli = args.get(2).map(|v| v.to_number(context)).transpose()?;
1189
1190        // 7. If t is NaN, return NaN.
1191        if t.is_nan() {
1192            return Ok(JsValue::from(f64::NAN));
1193        }
1194
1195        if LOCAL {
1196            // 8. Set t to LocalTime(t).
1197            t = local_time(t, context.host_hooks().as_ref());
1198        }
1199
1200        // 9. If sec is not present, let s be SecFromTime(t).
1201        let s = s.unwrap_or_else(|| sec_from_time(t).into());
1202
1203        // 10. If ms is not present, let milli be msFromTime(t).
1204        let milli = milli.unwrap_or_else(|| ms_from_time(t).into());
1205
1206        // 11. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).
1207        let date = make_date(day(t), make_time(hour_from_time(t).into(), m, s, milli));
1208
1209        let u = if LOCAL {
1210            // 12. Let u be TimeClip(UTC(date)).
1211            time_clip(utc_t(date, context.host_hooks().as_ref()))
1212        } else {
1213            // 12. Let u be TimeClip(date).
1214            time_clip(date)
1215        };
1216
1217        let object = this.as_object();
1218        let mut date_mut = object
1219            .as_ref()
1220            .and_then(JsObject::downcast_mut::<Date>)
1221            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1222
1223        // 13. Set dateObject.[[DateValue]] to u.
1224        date_mut.0 = u;
1225
1226        // 14. Return u.
1227        Ok(JsValue::from(u))
1228    }
1229
1230    /// [`Date.prototype.setMonth ( month [ , date ] )`][local] and
1231    /// [`Date.prototype.setUTCMonth ( month [ , date ] )`][utc].
1232    ///
1233    /// The `setMonth()` method sets the month for a specified date according to the currently set
1234    /// year.
1235    ///
1236    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setmonth
1237    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcmonth
1238    pub(crate) fn set_month<const LOCAL: bool>(
1239        this: &JsValue,
1240        args: &[JsValue],
1241        context: &mut Context,
1242    ) -> JsResult<JsValue> {
1243        // 1. Let dateObject be the this value.
1244        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1245        let object = this.as_object();
1246        let date = object
1247            .as_ref()
1248            .and_then(JsObject::downcast_ref::<Date>)
1249            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1250
1251        // 3. Let t be dateObject.[[DateValue]].
1252        let mut t = date.0;
1253
1254        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
1255        // ToNumber() may call userland code which can modify the underlying date
1256        // which will cause a panic. In order to avoid this, we drop the borrow,
1257        // here and only `downcast_mut` when date will be modified.
1258        drop(date);
1259
1260        // 4. Let m be ? ToNumber(month).
1261        let m = args.get_or_undefined(0).to_number(context)?;
1262
1263        // 5. If date is present, let dt be ? ToNumber(date).
1264        let dt = args.get(1).map(|v| v.to_number(context)).transpose()?;
1265
1266        // 6. If t is NaN, return NaN.
1267        if t.is_nan() {
1268            return Ok(JsValue::from(f64::NAN));
1269        }
1270
1271        // 7. Set t to LocalTime(t).
1272        if LOCAL {
1273            t = local_time(t, context.host_hooks().as_ref());
1274        }
1275
1276        // 8. If date is not present, let dt be DateFromTime(t).
1277        let dt = dt.unwrap_or_else(|| date_from_time(t).into());
1278
1279        // 9. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)).
1280        let new_date = make_date(
1281            make_day(year_from_time(t).into(), m, dt),
1282            time_within_day(t),
1283        );
1284
1285        let u = if LOCAL {
1286            // 10. Let u be TimeClip(UTC(newDate)).
1287            time_clip(utc_t(new_date, context.host_hooks().as_ref()))
1288        } else {
1289            // 10. Let u be TimeClip(newDate).
1290            time_clip(new_date)
1291        };
1292
1293        let object = this.as_object();
1294        let mut date_mut = object
1295            .as_ref()
1296            .and_then(JsObject::downcast_mut::<Date>)
1297            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1298
1299        // 11. Set dateObject.[[DateValue]] to u.
1300        date_mut.0 = u;
1301
1302        // 12. Return u.
1303        Ok(JsValue::from(u))
1304    }
1305
1306    /// [`Date.prototype.setSeconds ( sec [ , ms ] )`[local] and
1307    /// [`Date.prototype.setUTCSeconds ( sec [ , ms ] )`][utc].
1308    ///
1309    /// The `setSeconds()` method sets the seconds for a specified date.
1310    ///
1311    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setseconds
1312    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcseconds
1313    pub(crate) fn set_seconds<const LOCAL: bool>(
1314        this: &JsValue,
1315        args: &[JsValue],
1316        context: &mut Context,
1317    ) -> JsResult<JsValue> {
1318        // 1. Let dateObject be the this value.
1319        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1320        let object = this.as_object();
1321        let date = object
1322            .as_ref()
1323            .and_then(JsObject::downcast_ref::<Date>)
1324            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1325
1326        // 3. Let t be dateObject.[[DateValue]].
1327        let mut t = date.0;
1328
1329        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
1330        // ToNumber() may call userland code which can modify the underlying date
1331        // which will cause a panic. In order to avoid this, we drop the borrow,
1332        // here and only `downcast_mut` when date will be modified.
1333        drop(date);
1334
1335        // 4. Let s be ? ToNumber(sec).
1336        let s = args.get_or_undefined(0).to_number(context)?;
1337
1338        // 5. If ms is present, let milli be ? ToNumber(ms).
1339        let milli = args.get(1).map(|v| v.to_number(context)).transpose()?;
1340
1341        // 6. If t is NaN, return NaN.
1342        if t.is_nan() {
1343            return Ok(JsValue::from(f64::NAN));
1344        }
1345
1346        // 7. Set t to LocalTime(t).
1347        if LOCAL {
1348            t = local_time(t, context.host_hooks().as_ref());
1349        }
1350
1351        // 8. If ms is not present, let milli be msFromTime(t).
1352        let milli = milli.unwrap_or_else(|| ms_from_time(t).into());
1353
1354        // 9. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)).
1355        let date = make_date(
1356            day(t),
1357            make_time(hour_from_time(t).into(), min_from_time(t).into(), s, milli),
1358        );
1359
1360        let u = if LOCAL {
1361            // 10. Let u be TimeClip(UTC(date)).
1362            time_clip(utc_t(date, context.host_hooks().as_ref()))
1363        } else {
1364            // 10. Let u be TimeClip(date).
1365            time_clip(date)
1366        };
1367
1368        let object = this.as_object();
1369        let mut date_mut = object
1370            .as_ref()
1371            .and_then(JsObject::downcast_mut::<Date>)
1372            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1373
1374        // 11. Set dateObject.[[DateValue]] to u.
1375        date_mut.0 = u;
1376
1377        // 12. Return u.
1378        Ok(JsValue::from(u))
1379    }
1380
1381    /// [`Date.prototype.setYear()`][spec].
1382    ///
1383    /// The `setYear()` method sets the year for a specified date according to local time.
1384    ///
1385    /// # Note
1386    ///
1387    /// The [`Self::set_full_year`] method is preferred for nearly all purposes, because it avoids
1388    /// the “year 2000 problem.”
1389    ///
1390    /// More information:
1391    ///  - [MDN documentation][mdn]
1392    ///
1393    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setYear
1394    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setyear
1395    pub(crate) fn set_year(
1396        this: &JsValue,
1397        args: &[JsValue],
1398        context: &mut Context,
1399    ) -> JsResult<JsValue> {
1400        // 1. Let dateObject be the this value.
1401        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1402        let object = this.as_object();
1403        let date = object
1404            .as_ref()
1405            .and_then(JsObject::downcast_ref::<Date>)
1406            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1407
1408        // 3. Let t be dateObject.[[DateValue]].
1409        let t = date.0;
1410
1411        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
1412        // ToNumber() may call userland code which can modify the underlying date
1413        // which will cause a panic. In order to avoid this, we drop the borrow,
1414        // here and only `downcast_mut` when date will be modified.
1415        drop(date);
1416
1417        // 4. Let y be ? ToNumber(year).
1418        let y = args.get_or_undefined(0).to_number(context)?;
1419
1420        // 5. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t).
1421        let t = if t.is_nan() {
1422            0.0
1423        } else {
1424            local_time(t, context.host_hooks().as_ref())
1425        };
1426
1427        // 6. Let yyyy be MakeFullYear(y).
1428        let yyyy = make_full_year(y);
1429
1430        // 7. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)).
1431        let d = make_day(yyyy, month_from_time(t).into(), date_from_time(t).into());
1432
1433        // 8. Let date be MakeDate(d, TimeWithinDay(t)).
1434        let date = make_date(d, time_within_day(t));
1435
1436        // 9. Let u be TimeClip(UTC(date)).
1437        let u = time_clip(utc_t(date, context.host_hooks().as_ref()));
1438
1439        let object = this.as_object();
1440        let mut date_mut = object
1441            .as_ref()
1442            .and_then(JsObject::downcast_mut::<Date>)
1443            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1444
1445        // 10. Set dateObject.[[DateValue]] to u.
1446        date_mut.0 = u;
1447
1448        // 11. Return u.
1449        Ok(JsValue::from(u))
1450    }
1451
1452    /// [`Date.prototype.setTime()`][spec].
1453    ///
1454    /// The `setTime()` method sets the Date object to the time represented by a number of milliseconds
1455    /// since January 1, 1970, 00:00:00 UTC.
1456    ///
1457    /// More information:
1458    ///  - [MDN documentation][mdn]
1459    ///
1460    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.settime
1461    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime
1462    pub(crate) fn set_time(
1463        this: &JsValue,
1464        args: &[JsValue],
1465        context: &mut Context,
1466    ) -> JsResult<JsValue> {
1467        // 1. Let dateObject be the this value.
1468        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1469        let object = this.as_object();
1470        let date = object
1471            .as_ref()
1472            .and_then(JsObject::downcast_ref::<Date>)
1473            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1474
1475        // 3. Let t be ? ToNumber(time).
1476        let t = args.get_or_undefined(0).to_number(context)?;
1477
1478        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.
1479        // ToNumber() may call userland code which can modify the underlying date
1480        // which will cause a panic. In order to avoid this, we drop the borrow,
1481        // here and only `downcast_mut` when date will be modified.
1482        drop(date);
1483
1484        // 4. Let v be TimeClip(t).
1485        let v = time_clip(t);
1486
1487        let object = this.as_object();
1488        let mut date_mut = object
1489            .as_ref()
1490            .and_then(JsObject::downcast_mut::<Date>)
1491            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?;
1492
1493        // 5. Set dateObject.[[DateValue]] to v.
1494        date_mut.0 = v;
1495
1496        // 6. Return v.
1497        Ok(JsValue::from(v))
1498    }
1499
1500    /// [`Date.prototype.toDateString()`][spec].
1501    ///
1502    /// The `toDateString()` method returns the date portion of a Date object in English.
1503    ///
1504    /// More information:
1505    ///  - [MDN documentation][mdn]
1506    ///
1507    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.todatestring
1508    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toDateString
1509    pub(crate) fn to_date_string(
1510        this: &JsValue,
1511        _: &[JsValue],
1512        context: &mut Context,
1513    ) -> JsResult<JsValue> {
1514        // 1. Let dateObject be the this value.
1515        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1516        // 3. Let tv be dateObject.[[DateValue]].
1517        let tv = this
1518            .as_object()
1519            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1520            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1521            .0;
1522
1523        // 4. If tv is NaN, return "Invalid Date".
1524        if tv.is_nan() {
1525            return Ok(js_string!("Invalid Date").into());
1526        }
1527
1528        // 5. Let t be LocalTime(tv).
1529        let t = local_time(tv, context.host_hooks().as_ref());
1530
1531        // 6. Return DateString(t).
1532        Ok(JsValue::from(date_string(t)))
1533    }
1534
1535    /// [`Date.prototype.toISOString()`][spec].
1536    ///
1537    /// The `toISOString()` method returns a string in simplified extended ISO format
1538    /// ([ISO 8601][iso8601]).
1539    ///
1540    /// More information:
1541    ///  - [MDN documentation][mdn]
1542    ///
1543    /// [iso8601]: http://en.wikipedia.org/wiki/ISO_8601
1544    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toisostring
1545    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
1546    pub(crate) fn to_iso_string(
1547        this: &JsValue,
1548        _: &[JsValue],
1549        _: &mut Context,
1550    ) -> JsResult<JsValue> {
1551        // 1. Let dateObject be the this value.
1552        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1553        // 3. Let tv be dateObject.[[DateValue]].
1554        let tv = this
1555            .as_object()
1556            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1557            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1558            .0;
1559
1560        // 4. If tv is not finite, throw a RangeError exception.
1561        if !tv.is_finite() {
1562            return Err(JsNativeError::range()
1563                .with_message("Invalid time value")
1564                .into());
1565        }
1566
1567        // 5. If tv corresponds with a year that cannot be represented in the Date Time String Format, throw a RangeError exception.
1568        // 6. Return a String representation of tv in the Date Time String Format on the UTC time scale,
1569        //    including all format elements and the UTC offset representation "Z".
1570        let year = year_from_time(tv);
1571        let year = if year >= 10000 {
1572            js_string!(js_str!("+"), pad_six(year.unsigned_abs(), &mut [0; 6]))
1573        } else if year >= 0 {
1574            pad_four(year.unsigned_abs(), &mut [0; 4]).into()
1575        } else {
1576            js_string!(js_str!("-"), pad_six(year.unsigned_abs(), &mut [0; 6]))
1577        };
1578        let mut binding = [0; 2];
1579        let month = pad_two(month_from_time(tv) + 1, &mut binding);
1580        let mut binding = [0; 2];
1581        let day = pad_two(date_from_time(tv), &mut binding);
1582        let mut binding = [0; 2];
1583        let hour = pad_two(hour_from_time(tv), &mut binding);
1584        let mut binding = [0; 2];
1585        let minute = pad_two(min_from_time(tv), &mut binding);
1586        let mut binding = [0; 2];
1587        let second = pad_two(sec_from_time(tv), &mut binding);
1588        let mut binding = [0; 3];
1589        let millisecond = pad_three(ms_from_time(tv), &mut binding);
1590
1591        Ok(JsValue::from(js_string!(
1592            &year,
1593            js_str!("-"),
1594            month,
1595            js_str!("-"),
1596            day,
1597            js_str!("T"),
1598            hour,
1599            js_str!(":"),
1600            minute,
1601            js_str!(":"),
1602            second,
1603            js_str!("."),
1604            millisecond,
1605            js_str!("Z")
1606        )))
1607    }
1608
1609    /// [`Date.prototype.toJSON()`][spec].
1610    ///
1611    /// The `toJSON()` method returns a string representation of the `Date` object.
1612    ///
1613    /// More information:
1614    ///  - [MDN documentation][mdn]
1615    ///
1616    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tojson
1617    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON
1618    pub(crate) fn to_json(
1619        this: &JsValue,
1620        _: &[JsValue],
1621        context: &mut Context,
1622    ) -> JsResult<JsValue> {
1623        // 1. Let O be ? ToObject(this value).
1624        let o = this.to_object(context)?;
1625
1626        // 2. Let tv be ? ToPrimitive(O, number).
1627        let tv = this.to_primitive(context, PreferredType::Number)?;
1628
1629        // 3. If Type(tv) is Number and tv is not finite, return null.
1630        if tv.as_number().is_some_and(|x| !f64::is_finite(x)) {
1631            return Ok(JsValue::null());
1632        }
1633
1634        // 4. Return ? Invoke(O, "toISOString").
1635        let func = o.get(js_string!("toISOString"), context)?;
1636        func.call(this, &[], context)
1637    }
1638
1639    /// [`Date.prototype.toLocaleDateString()`][spec].
1640    ///
1641    /// The `toLocaleDateString()` method returns the date portion of the given Date instance according
1642    /// to language-specific conventions.
1643    ///
1644    /// More information:
1645    ///  - [MDN documentation][mdn]
1646    ///
1647    /// [spec]: https://tc39.es/ecma402/#sup-date.prototype.tolocaledatestring
1648    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString
1649    #[allow(
1650        unused_variables,
1651        reason = "`args` and `context` are used when the `intl` feature is enabled"
1652    )]
1653    pub(crate) fn to_locale_date_string(
1654        this: &JsValue,
1655        args: &[JsValue],
1656        context: &mut Context,
1657    ) -> JsResult<JsValue> {
1658        #[cfg(feature = "intl")]
1659        {
1660            use crate::builtins::intl::date_time_format::{
1661                FormatDefaults, FormatType, format_date_time_locale,
1662            };
1663            // 1. Let dateObject be the this value.
1664            // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1665            // 3. Let x be dateObject.[[DateValue]].
1666            let t = this
1667                .as_object()
1668                .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1669                .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1670                .0;
1671            // 4. If x is NaN, return "Invalid Date".
1672            if t.is_nan() {
1673                return Ok(JsValue::new(js_string!("Invalid Date")));
1674            }
1675            // 5. Let dateFormat be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, locales, options, date, date).
1676            // 6. Return ! FormatDateTime(dateFormat, x).
1677            let locales = args.get_or_undefined(0);
1678            let options = args.get_or_undefined(1);
1679            format_date_time_locale(
1680                locales,
1681                options,
1682                FormatType::Date,
1683                FormatDefaults::Date,
1684                t,
1685                context,
1686            )
1687        }
1688        #[cfg(not(feature = "intl"))]
1689        {
1690            Self::to_string(this, &[], context)
1691        }
1692    }
1693
1694    /// [`Date.prototype.toLocaleString()`][spec].
1695    ///
1696    /// The `toLocaleString()` method returns a string representing the specified Date object.
1697    ///
1698    /// More information:
1699    ///  - [MDN documentation][mdn]
1700    ///
1701    /// [spec]: https://tc39.es/ecma402/#sup-date.prototype.tolocalestring
1702    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
1703    #[allow(
1704        unused_variables,
1705        reason = "`args` and `context` are used when the `intl` feature is enabled"
1706    )]
1707    pub(crate) fn to_locale_string(
1708        this: &JsValue,
1709        args: &[JsValue],
1710        context: &mut Context,
1711    ) -> JsResult<JsValue> {
1712        #[cfg(feature = "intl")]
1713        {
1714            use crate::builtins::intl::date_time_format::{
1715                FormatDefaults, FormatType, format_date_time_locale,
1716            };
1717            // 1. Let dateObject be the this value.
1718            // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1719            // 3. Let x be dateObject.[[DateValue]].
1720            let t = this
1721                .as_object()
1722                .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1723                .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1724                .0;
1725            // 4. If x is NaN, return "Invalid Date".
1726            if t.is_nan() {
1727                return Ok(JsValue::new(js_string!("Invalid Date")));
1728            }
1729            // 5. Let dateFormat be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, locales, options, any, all).
1730            // 6. Return ! FormatDateTime(dateFormat, x).
1731            let locales = args.get_or_undefined(0);
1732            let options = args.get_or_undefined(1);
1733            format_date_time_locale(
1734                locales,
1735                options,
1736                FormatType::Any,
1737                FormatDefaults::All,
1738                t,
1739                context,
1740            )
1741        }
1742        #[cfg(not(feature = "intl"))]
1743        {
1744            Self::to_string(this, &[], context)
1745        }
1746    }
1747
1748    /// [`Date.prototype.toLocaleTimeString()`][spec].
1749    ///
1750    /// The `toLocaleTimeString()` method returns the time portion of a Date object in human readable
1751    /// form in American English.
1752    ///
1753    /// More information:
1754    ///  - [MDN documentation][mdn]
1755    ///
1756    /// [spec]: https://tc39.es/ecma402/#sup-date.prototype.tolocaletimestring
1757    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString
1758    #[allow(
1759        unused_variables,
1760        reason = "`args` and `context` are used when the `intl` feature is enabled"
1761    )]
1762    pub(crate) fn to_locale_time_string(
1763        this: &JsValue,
1764        args: &[JsValue],
1765        context: &mut Context,
1766    ) -> JsResult<JsValue> {
1767        #[cfg(feature = "intl")]
1768        {
1769            use crate::builtins::intl::date_time_format::{
1770                FormatDefaults, FormatType, format_date_time_locale,
1771            };
1772            // 1. Let dateObject be the this value.
1773            // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1774            // 3. Let x be dateObject.[[DateValue]].
1775            let t = this
1776                .as_object()
1777                .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1778                .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1779                .0;
1780            // 4. If x is NaN, return "Invalid Date".
1781            if t.is_nan() {
1782                return Ok(JsValue::new(js_string!("Invalid Date")));
1783            }
1784            // 5. Let timeFormat be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, locales, options, time, time).
1785            // 6. Return ! FormatDateTime(timeFormat, x).
1786            let locales = args.get_or_undefined(0);
1787            let options = args.get_or_undefined(1);
1788            format_date_time_locale(
1789                locales,
1790                options,
1791                FormatType::Time,
1792                FormatDefaults::Time,
1793                t,
1794                context,
1795            )
1796        }
1797        #[cfg(not(feature = "intl"))]
1798        {
1799            Self::to_string(this, &[], context)
1800        }
1801    }
1802
1803    /// [`Date.prototype.toString()`][spec].
1804    ///
1805    /// The `toString()` method returns a string representing the specified Date object.
1806    ///
1807    /// More information:
1808    ///  - [MDN documentation][mdn]
1809    ///
1810    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tostring
1811    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString
1812    pub(crate) fn to_string(
1813        this: &JsValue,
1814        _: &[JsValue],
1815        context: &mut Context,
1816    ) -> JsResult<JsValue> {
1817        // 1. Let dateObject be the this value.
1818        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1819        // 3. Let tv be dateObject.[[DateValue]].
1820        let tv = this
1821            .as_object()
1822            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1823            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1824            .0;
1825
1826        // 4. Return ToDateString(tv).
1827        Ok(JsValue::from(to_date_string_t(
1828            tv,
1829            context.host_hooks().as_ref(),
1830        )))
1831    }
1832
1833    /// [`Date.prototype.toTimeString()`][spec].
1834    ///
1835    /// The `toTimeString()` method returns the time portion of a Date object in human readable form
1836    /// in American English.
1837    ///
1838    /// More information:
1839    ///  - [MDN documentation][mdn]
1840    ///
1841    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.totimestring
1842    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTimeString
1843    pub(crate) fn to_time_string(
1844        this: &JsValue,
1845        _: &[JsValue],
1846        context: &mut Context,
1847    ) -> JsResult<JsValue> {
1848        // 1. Let dateObject be the this value.
1849        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1850        // 3. Let tv be dateObject.[[DateValue]].
1851        let tv = this
1852            .as_object()
1853            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1854            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1855            .0;
1856
1857        // 4. If tv is NaN, return "Invalid Date".
1858        if tv.is_nan() {
1859            return Ok(js_string!("Invalid Date").into());
1860        }
1861
1862        // 5. Let t be LocalTime(tv).
1863        let t = local_time(tv, context.host_hooks().as_ref());
1864
1865        // 6. Return the string-concatenation of TimeString(t) and TimeZoneString(tv).
1866        Ok(JsValue::from(js_string!(
1867            &time_string(t),
1868            &time_zone_string(t, context.host_hooks().as_ref())
1869        )))
1870    }
1871
1872    /// [`Date.prototype.toUTCString()`][spec].
1873    ///
1874    /// The `toUTCString()` method returns a string representing the specified Date object.
1875    ///
1876    /// More information:
1877    ///  - [MDN documentation][mdn]
1878    ///
1879    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toutcstring
1880    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toUTCString
1881    pub(crate) fn to_utc_string(
1882        this: &JsValue,
1883        _args: &[JsValue],
1884        _context: &mut Context,
1885    ) -> JsResult<JsValue> {
1886        // 1. Let dateObject be the this value.
1887        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1888        // 3. Let tv be dateObject.[[DateValue]].
1889        let tv = this
1890            .as_object()
1891            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1892            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1893            .0;
1894
1895        // 4. If tv is NaN, return "Invalid Date".
1896        if tv.is_nan() {
1897            return Ok(js_string!("Invalid Date").into());
1898        }
1899
1900        // 5. Let weekday be the Name of the entry in Table 63 with the Number WeekDay(tv).
1901        let weekday = match week_day(tv) {
1902            0 => js_str!("Sun"),
1903            1 => js_str!("Mon"),
1904            2 => js_str!("Tue"),
1905            3 => js_str!("Wed"),
1906            4 => js_str!("Thu"),
1907            5 => js_str!("Fri"),
1908            6 => js_str!("Sat"),
1909            _ => unreachable!(),
1910        };
1911
1912        // 6. Let month be the Name of the entry in Table 64 with the Number MonthFromTime(tv).
1913        let month = match month_from_time(tv) {
1914            0 => js_str!("Jan"),
1915            1 => js_str!("Feb"),
1916            2 => js_str!("Mar"),
1917            3 => js_str!("Apr"),
1918            4 => js_str!("May"),
1919            5 => js_str!("Jun"),
1920            6 => js_str!("Jul"),
1921            7 => js_str!("Aug"),
1922            8 => js_str!("Sep"),
1923            9 => js_str!("Oct"),
1924            10 => js_str!("Nov"),
1925            11 => js_str!("Dec"),
1926            _ => unreachable!(),
1927        };
1928
1929        // 7. Let day be ToZeroPaddedDecimalString(ℝ(DateFromTime(tv)), 2).
1930        let mut binding = [0; 2];
1931        let day = pad_two(date_from_time(tv), &mut binding);
1932
1933        // 8. Let yv be YearFromTime(tv).
1934        let yv = year_from_time(tv);
1935
1936        // 9. If yv is +0𝔽 or yv > +0𝔽, let yearSign be the empty String; otherwise, let yearSign be "-".
1937        let year_sign = if yv >= 0 { js_str!("") } else { js_str!("-") };
1938
1939        // 10. Let paddedYear be ToZeroPaddedDecimalString(abs(ℝ(yv)), 4).
1940        let yv = yv.unsigned_abs();
1941        let padded_year: JsString = if yv >= 100_000 {
1942            pad_six(yv, &mut [0; 6]).into()
1943        } else if yv >= 10000 {
1944            pad_five(yv, &mut [0; 5]).into()
1945        } else {
1946            pad_four(yv, &mut [0; 4]).into()
1947        };
1948
1949        // 11. Return the string-concatenation of
1950        // weekday,
1951        // ",",
1952        // the code unit 0x0020 (SPACE),
1953        // day,
1954        // the code unit 0x0020 (SPACE),
1955        // month,
1956        // the code unit 0x0020 (SPACE),
1957        // yearSign,
1958        // paddedYear,
1959        // the code unit 0x0020 (SPACE),
1960        // and TimeString(tv).
1961        Ok(JsValue::from(js_string!(
1962            weekday,
1963            js_str!(","),
1964            js_str!(" "),
1965            day,
1966            js_str!(" "),
1967            month,
1968            js_str!(" "),
1969            year_sign,
1970            &padded_year,
1971            js_str!(" "),
1972            &time_string(tv)
1973        )))
1974    }
1975
1976    /// [`Date.prototype.valueOf()`][spec].
1977    ///
1978    /// The `valueOf()` method returns the primitive value of a `Date` object.
1979    ///
1980    /// More information:
1981    ///  - [MDN documentation][mdn]
1982    ///
1983    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.valueof
1984    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/valueOf
1985    pub(crate) fn value_of(
1986        this: &JsValue,
1987        _args: &[JsValue],
1988        _context: &mut Context,
1989    ) -> JsResult<JsValue> {
1990        // 1. Let dateObject be the this value.
1991        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
1992        // 3. Return dateObject.[[DateValue]].
1993        Ok(this
1994            .as_object()
1995            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
1996            .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?
1997            .0
1998            .into())
1999    }
2000
2001    /// [`Date.prototype [ @@toPrimitive ] ( hint )`][spec].
2002    ///
2003    /// The <code>\[@@toPrimitive\]()</code> method converts a Date object to a primitive value.
2004    ///
2005    /// More information:
2006    ///  - [MDN documentation][mdn]
2007    ///
2008    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype-@@toprimitive
2009    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/@@toPrimitive
2010    pub(crate) fn to_primitive(
2011        this: &JsValue,
2012        args: &[JsValue],
2013        context: &mut Context,
2014    ) -> JsResult<JsValue> {
2015        // 1. Let O be the this value.
2016        // 2. If Type(O) is not Object, throw a TypeError exception.
2017        let o = this.as_object().ok_or_else(|| {
2018            JsNativeError::typ().with_message("Date.prototype[@@toPrimitive] called on non object")
2019        })?;
2020
2021        let hint = args.get_or_undefined(0);
2022
2023        let try_first = match hint.as_string() {
2024            // 3. If hint is "string" or "default", then
2025            // a. Let tryFirst be string.
2026            Some(string) if string == "string" || string == "default" => PreferredType::String,
2027            // 4. Else if hint is "number", then
2028            // a. Let tryFirst be number.
2029            Some(number) if number == "number" => PreferredType::Number,
2030            // 5. Else, throw a TypeError exception.
2031            _ => {
2032                return Err(JsNativeError::typ()
2033                    .with_message("Date.prototype[@@toPrimitive] called with invalid hint")
2034                    .into());
2035            }
2036        };
2037
2038        // 6. Return ? OrdinaryToPrimitive(O, tryFirst).
2039        o.ordinary_to_primitive(context, try_first)
2040    }
2041
2042    /// 14.9.1 `Date.prototype.toTemporalInstant ()`
2043    ///
2044    /// Returns a JavaScript Date as a `Temporal.Instant`.
2045    ///
2046    /// More information:
2047    ///  - [Temporal Proposal][spec]
2048    ///  - [MDN documentation][mdn]
2049    ///
2050    /// [spec]: https://tc39.es/proposal-temporal/#sec-date.prototype.totemporalinstant
2051    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTemporalInstant/
2052    #[cfg(feature = "temporal")]
2053    pub(crate) fn to_temporal_instant(
2054        this: &JsValue,
2055        _: &[JsValue],
2056        context: &mut Context,
2057    ) -> JsResult<JsValue> {
2058        use crate::{builtins::temporal::create_temporal_instant, js_error};
2059        use num_traits::FromPrimitive;
2060        use temporal_rs::Instant;
2061
2062        const NS_PER_MILLISECOND: i128 = 1_000_000;
2063        // 1. Let dateObject be the this value.
2064        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).
2065        // 3. Let t be dateObject.[[DateValue]].
2066        let t = this
2067            .as_object()
2068            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())
2069            .ok_or_else(|| js_error!(TypeError: "'this' is not a Date"))?
2070            .0;
2071
2072        // 4. Let ns be ? NumberToBigInt(t) × ℤ(10**6).
2073        let ns = i128::from_f64(t)
2074            .ok_or(js_error!(RangeError: "[[DateValue]] is not an integral number."))?
2075            * NS_PER_MILLISECOND;
2076        // 5. Return ! CreateTemporalInstant(ns).
2077        create_temporal_instant(Instant::try_new(ns)?, None, context)
2078    }
2079}