boa/builtins/date/
mod.rs

1#[cfg(test)]
2mod tests;
3
4use crate::{
5    builtins::BuiltIn,
6    context::StandardObjects,
7    gc::{empty_trace, Finalize, Trace},
8    object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
9    property::Attribute,
10    symbol::WellKnownSymbols,
11    value::{JsValue, PreferredType},
12    BoaProfiler, Context, JsResult, JsString,
13};
14use chrono::{prelude::*, Duration, LocalResult};
15use std::fmt::Display;
16
17use super::JsArgs;
18
19/// The number of nanoseconds in a millisecond.
20const NANOS_PER_MS: i64 = 1_000_000;
21/// The number of milliseconds in an hour.
22const MILLIS_PER_HOUR: i64 = 3_600_000;
23/// The number of milliseconds in a minute.
24const MILLIS_PER_MINUTE: i64 = 60_000;
25/// The number of milliseconds in a second.
26const MILLIS_PER_SECOND: i64 = 1000;
27
28#[inline]
29fn is_zero_or_normal_opt(value: Option<f64>) -> bool {
30    value
31        .map(|value| value == 0f64 || value.is_normal())
32        .unwrap_or(true)
33}
34
35macro_rules! check_normal_opt {
36    ($($v:expr),+) => {
37        $(is_zero_or_normal_opt($v.into()) &&)+ true
38    };
39}
40
41#[inline]
42fn ignore_ambiguity<T>(result: LocalResult<T>) -> Option<T> {
43    match result {
44        LocalResult::Ambiguous(v, _) => Some(v),
45        LocalResult::Single(v) => Some(v),
46        LocalResult::None => None,
47    }
48}
49
50macro_rules! getter_method {
51    ($name:ident) => {{
52        fn get_value(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
53            Ok(JsValue::new(this_time_value(this, context)?.$name()))
54        }
55        get_value
56    }};
57    (Self::$name:ident) => {{
58        fn get_value(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
59            Ok(JsValue::new(Date::$name()))
60        }
61        get_value
62    }};
63}
64
65#[derive(Debug, Finalize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
66pub struct Date(Option<NaiveDateTime>);
67
68impl Display for Date {
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        match self.to_local() {
71            Some(v) => write!(f, "{}", v.format("%a %b %d %Y %H:%M:%S GMT%:z")),
72            _ => write!(f, "Invalid Date"),
73        }
74    }
75}
76
77unsafe impl Trace for Date {
78    // Date is a stack value, it doesn't require tracing.
79    // only safe if `chrono` never implements `Trace` for `NaiveDateTime`
80    empty_trace!();
81}
82
83impl Default for Date {
84    fn default() -> Self {
85        Self(Some(Utc::now().naive_utc()))
86    }
87}
88
89impl BuiltIn for Date {
90    const NAME: &'static str = "Date";
91
92    fn attribute() -> Attribute {
93        Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
94    }
95
96    fn init(context: &mut Context) -> (&'static str, JsValue, Attribute) {
97        let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
98
99        let date_object = ConstructorBuilder::new(context, Self::constructor)
100            .name(Self::NAME)
101            .length(Self::LENGTH)
102            .method(getter_method!(get_date), "getDate", 0)
103            .method(getter_method!(get_day), "getDay", 0)
104            .method(getter_method!(get_full_year), "getFullYear", 0)
105            .method(getter_method!(get_hours), "getHours", 0)
106            .method(getter_method!(get_milliseconds), "getMilliseconds", 0)
107            .method(getter_method!(get_minutes), "getMinutes", 0)
108            .method(getter_method!(get_month), "getMonth", 0)
109            .method(getter_method!(get_seconds), "getSeconds", 0)
110            .method(getter_method!(get_time), "getTime", 0)
111            .method(getter_method!(get_year), "getYear", 0)
112            .method(Self::get_timezone_offset, "getTimezoneOffset", 0)
113            .method(getter_method!(get_utc_date), "getUTCDate", 0)
114            .method(getter_method!(get_utc_day), "getUTCDay", 0)
115            .method(getter_method!(get_utc_full_year), "getUTCFullYear", 0)
116            .method(getter_method!(get_utc_hours), "getUTCHours", 0)
117            .method(
118                getter_method!(get_utc_milliseconds),
119                "getUTCMilliseconds",
120                0,
121            )
122            .method(getter_method!(get_utc_minutes), "getUTCMinutes", 0)
123            .method(getter_method!(get_utc_month), "getUTCMonth", 0)
124            .method(getter_method!(get_utc_seconds), "getUTCSeconds", 0)
125            .method(Self::set_date, "setDate", 1)
126            .method(Self::set_full_year, "setFullYear", 3)
127            .method(Self::set_hours, "setHours", 4)
128            .method(Self::set_milliseconds, "setMilliseconds", 1)
129            .method(Self::set_minutes, "setMinutes", 3)
130            .method(Self::set_month, "setMonth", 2)
131            .method(Self::set_seconds, "setSeconds", 2)
132            .method(Self::set_year, "setYear", 1)
133            .method(Self::set_time, "setTime", 1)
134            .method(Self::set_utc_date, "setUTCDate", 1)
135            .method(Self::set_utc_full_year, "setUTCFullYear", 3)
136            .method(Self::set_utc_hours, "setUTCHours", 4)
137            .method(Self::set_utc_milliseconds, "setUTCMilliseconds", 1)
138            .method(Self::set_utc_minutes, "setUTCMinutes", 3)
139            .method(Self::set_utc_month, "setUTCMonth", 2)
140            .method(Self::set_utc_seconds, "setUTCSeconds", 2)
141            .method(Self::to_date_string, "toDateString", 0)
142            .method(getter_method!(to_gmt_string), "toGMTString", 0)
143            .method(Self::to_iso_string, "toISOString", 0)
144            .method(Self::to_json, "toJSON", 1)
145            // Locale strings
146            .method(Self::to_string, "toString", 0)
147            .method(Self::to_time_string, "toTimeString", 0)
148            .method(getter_method!(to_utc_string), "toUTCString", 0)
149            .method(getter_method!(value_of), "valueOf", 0)
150            .method(
151                Self::to_primitive,
152                (WellKnownSymbols::to_primitive(), "[Symbol.toPrimitive]"),
153                1,
154            )
155            .static_method(Self::now, "now", 0)
156            .static_method(Self::parse, "parse", 1)
157            .static_method(Self::utc, "UTC", 7)
158            .build();
159
160        (Self::NAME, date_object.into(), Self::attribute())
161    }
162}
163
164impl Date {
165    /// The amount of arguments this function object takes.
166    pub(crate) const LENGTH: usize = 7;
167
168    /// Check if the time (number of milliseconds) is in the expected range.
169    /// Returns None if the time is not in the range, otherwise returns the time itself in option.
170    ///
171    /// More information:
172    ///  - [ECMAScript reference][spec]
173    ///
174    /// [spec]: https://tc39.es/ecma262/#sec-timeclip
175    #[inline]
176    pub fn time_clip(time: f64) -> Option<f64> {
177        if time.abs() > 8.64e15 {
178            None
179        } else {
180            Some(time)
181        }
182    }
183
184    /// Converts the `Date` to a local `DateTime`.
185    ///
186    /// If the `Date` is invalid (i.e. NAN), this function will return `None`.
187    #[inline]
188    pub fn to_local(self) -> Option<DateTime<Local>> {
189        self.0
190            .map(|utc| Local::now().timezone().from_utc_datetime(&utc))
191    }
192
193    /// Converts the `Date` to a UTC `DateTime`.
194    ///
195    /// If the `Date` is invalid (i.e. NAN), this function will return `None`.
196    pub fn to_utc(self) -> Option<DateTime<Utc>> {
197        self.0
198            .map(|utc| Utc::now().timezone().from_utc_datetime(&utc))
199    }
200
201    /// Optionally sets the individual components of the `Date`.
202    ///
203    /// Each component does not have to be within the range of valid values. For example, if `month` is too large
204    /// then `year` will be incremented by the required amount.
205    #[allow(clippy::too_many_arguments)]
206    pub fn set_components(
207        &mut self,
208        utc: bool,
209        year: Option<f64>,
210        month: Option<f64>,
211        day: Option<f64>,
212        hour: Option<f64>,
213        minute: Option<f64>,
214        second: Option<f64>,
215        millisecond: Option<f64>,
216    ) {
217        #[inline]
218        fn num_days_in(year: i32, month: u32) -> Option<u32> {
219            let month = month + 1; // zero-based for calculations
220
221            Some(
222                NaiveDate::from_ymd_opt(
223                    match month {
224                        12 => year.checked_add(1)?,
225                        _ => year,
226                    },
227                    match month {
228                        12 => 1,
229                        _ => month + 1,
230                    },
231                    1,
232                )?
233                .signed_duration_since(NaiveDate::from_ymd_opt(year, month, 1)?)
234                .num_days() as u32,
235            )
236        }
237
238        #[inline]
239        fn fix_month(year: i32, month: i32) -> Option<(i32, u32)> {
240            let year = year.checked_add(month / 12)?;
241
242            if month < 0 {
243                let year = year.checked_sub(1)?;
244                let month = (11 + (month + 1) % 12) as u32;
245                Some((year, month))
246            } else {
247                let month = (month % 12) as u32;
248                Some((year, month))
249            }
250        }
251
252        #[inline]
253        fn fix_day(mut year: i32, mut month: i32, mut day: i32) -> Option<(i32, u32, u32)> {
254            loop {
255                if day < 0 {
256                    let (fixed_year, fixed_month) = fix_month(year, month.checked_sub(1)?)?;
257
258                    year = fixed_year;
259                    month = fixed_month as i32;
260                    day += num_days_in(fixed_year, fixed_month)? as i32;
261                } else {
262                    let (fixed_year, fixed_month) = fix_month(year, month)?;
263                    let num_days = num_days_in(fixed_year, fixed_month)? as i32;
264
265                    if day >= num_days {
266                        day -= num_days;
267                        month = month.checked_add(1)?;
268                    } else {
269                        break;
270                    }
271                }
272            }
273
274            let (fixed_year, fixed_month) = fix_month(year, month)?;
275            Some((fixed_year, fixed_month, day as u32))
276        }
277
278        // If any of the args are infinity or NaN, return an invalid date.
279        if !check_normal_opt!(year, month, day, hour, minute, second, millisecond) {
280            self.0 = None;
281            return;
282        }
283
284        let naive = if utc {
285            self.to_utc().map(|dt| dt.naive_utc())
286        } else {
287            self.to_local().map(|dt| dt.naive_local())
288        };
289
290        self.0 = naive.and_then(|naive| {
291            let year = year.unwrap_or_else(|| naive.year() as f64) as i32;
292            let month = month.unwrap_or_else(|| naive.month0() as f64) as i32;
293            let day = (day.unwrap_or_else(|| naive.day() as f64) as i32).checked_sub(1)?;
294            let hour = hour.unwrap_or_else(|| naive.hour() as f64) as i64;
295            let minute = minute.unwrap_or_else(|| naive.minute() as f64) as i64;
296            let second = second.unwrap_or_else(|| naive.second() as f64) as i64;
297            let millisecond = millisecond
298                .unwrap_or_else(|| naive.nanosecond() as f64 / NANOS_PER_MS as f64)
299                as i64;
300
301            let (year, month, day) = fix_day(year, month, day)?;
302
303            let duration_hour = Duration::milliseconds(hour.checked_mul(MILLIS_PER_HOUR)?);
304            let duration_minute = Duration::milliseconds(minute.checked_mul(MILLIS_PER_MINUTE)?);
305            let duration_second = Duration::milliseconds(second.checked_mul(MILLIS_PER_SECOND)?);
306            let duration_milisecond = Duration::milliseconds(millisecond);
307
308            let duration = duration_hour
309                .checked_add(&duration_minute)?
310                .checked_add(&duration_second)?
311                .checked_add(&duration_milisecond)?;
312
313            NaiveDate::from_ymd_opt(year, month + 1, day + 1)
314                .and_then(|dt| dt.and_hms(0, 0, 0).checked_add_signed(duration))
315                .and_then(|dt| {
316                    if utc {
317                        Some(Utc.from_utc_datetime(&dt).naive_utc())
318                    } else {
319                        ignore_ambiguity(Local.from_local_datetime(&dt)).map(|dt| dt.naive_utc())
320                    }
321                })
322                .filter(|dt| Self::time_clip(dt.timestamp_millis() as f64).is_some())
323        });
324    }
325
326    /// `Date()`
327    ///
328    /// Creates a JavaScript `Date` instance that represents a single moment in time in a platform-independent format.
329    ///
330    /// More information:
331    ///  - [ECMAScript reference][spec]
332    ///  - [MDN documentation][mdn]
333    ///
334    /// [spec]: https://tc39.es/ecma262/#sec-date-constructor
335    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
336    pub(crate) fn constructor(
337        new_target: &JsValue,
338        args: &[JsValue],
339        context: &mut Context,
340    ) -> JsResult<JsValue> {
341        if new_target.is_undefined() {
342            Ok(Self::make_date_string())
343        } else {
344            let prototype = get_prototype_from_constructor(
345                new_target,
346                StandardObjects::object_object,
347                context,
348            )?;
349            let obj = context.construct_object();
350            obj.set_prototype_instance(prototype.into());
351            let this = obj.into();
352            if args.is_empty() {
353                Ok(Self::make_date_now(&this))
354            } else if args.len() == 1 {
355                Self::make_date_single(&this, args, context)
356            } else {
357                Self::make_date_multiple(&this, args, context)
358            }
359        }
360    }
361
362    /// `Date()`
363    ///
364    /// The `Date()` function is used to create a string that represent the current date and time.
365    ///
366    /// More information:
367    ///  - [ECMAScript reference][spec]
368    ///  - [MDN documentation][mdn]
369    ///
370    /// [spec]: https://tc39.es/ecma262/#sec-date-constructor
371    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
372    pub(crate) fn make_date_string() -> JsValue {
373        JsValue::new(Local::now().to_rfc3339())
374    }
375
376    /// `Date()`
377    ///
378    /// The newly-created `Date` object represents the current date and time as of the time of instantiation.
379    ///
380    /// More information:
381    ///  - [ECMAScript reference][spec]
382    ///  - [MDN documentation][mdn]
383    ///
384    /// [spec]: https://tc39.es/ecma262/#sec-date-constructor
385    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
386    pub(crate) fn make_date_now(this: &JsValue) -> JsValue {
387        let date = Date::default();
388        this.set_data(ObjectData::date(date));
389        this.clone()
390    }
391
392    /// `Date(value)`
393    ///
394    /// The newly-created `Date` object represents the value provided to the constructor.
395    ///
396    /// More information:
397    ///  - [ECMAScript reference][spec]
398    ///  - [MDN documentation][mdn]
399    ///
400    /// [spec]: https://tc39.es/ecma262/#sec-date-constructor
401    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
402    pub(crate) fn make_date_single(
403        this: &JsValue,
404        args: &[JsValue],
405        context: &mut Context,
406    ) -> JsResult<JsValue> {
407        let value = &args[0];
408        let tv = match this_time_value(value, context) {
409            Ok(dt) => dt.0,
410            _ => match value.to_primitive(context, PreferredType::Default)? {
411                JsValue::String(ref str) => match chrono::DateTime::parse_from_rfc3339(str) {
412                    Ok(dt) => Some(dt.naive_utc()),
413                    _ => None,
414                },
415                tv => {
416                    let tv = tv.to_number(context)?;
417                    if tv.is_nan() {
418                        None
419                    } else {
420                        let secs = (tv / 1_000f64) as i64;
421                        let nsecs = ((tv % 1_000f64) * 1_000_000f64) as u32;
422                        NaiveDateTime::from_timestamp_opt(secs, nsecs)
423                    }
424                }
425            },
426        };
427
428        let tv = tv.filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some());
429        let date = Date(tv);
430        this.set_data(ObjectData::date(date));
431        Ok(this.clone())
432    }
433
434    /// `Date(year, month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ])`
435    ///
436    /// The newly-created `Date` object represents the date components provided to the constructor.
437    ///
438    /// More information:
439    ///  - [ECMAScript reference][spec]
440    ///  - [MDN documentation][mdn]
441    ///
442    /// [spec]: https://tc39.es/ecma262/#sec-date-constructor
443    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
444    pub(crate) fn make_date_multiple(
445        this: &JsValue,
446        args: &[JsValue],
447        context: &mut Context,
448    ) -> JsResult<JsValue> {
449        let mut year = args[0].to_number(context)?;
450        let month = args[1].to_number(context)?;
451        let day = args
452            .get(2)
453            .map_or(Ok(1f64), |value| value.to_number(context))?;
454        let hour = args
455            .get(3)
456            .map_or(Ok(0f64), |value| value.to_number(context))?;
457        let min = args
458            .get(4)
459            .map_or(Ok(0f64), |value| value.to_number(context))?;
460        let sec = args
461            .get(5)
462            .map_or(Ok(0f64), |value| value.to_number(context))?;
463        let milli = args
464            .get(6)
465            .map_or(Ok(0f64), |value| value.to_number(context))?;
466
467        // If any of the args are infinity or NaN, return an invalid date.
468        if !check_normal_opt!(year, month, day, hour, min, sec, milli) {
469            let date = Date(None);
470            this.set_data(ObjectData::date(date));
471            return Ok(this.clone());
472        }
473
474        if (0.0..=99.0).contains(&year) {
475            year += 1900.0
476        }
477
478        let mut date = Self(
479            NaiveDateTime::from_timestamp_opt(0, 0)
480                .and_then(|local| ignore_ambiguity(Local.from_local_datetime(&local)))
481                .map(|local| local.naive_utc())
482                .filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some()),
483        );
484
485        date.set_components(
486            false,
487            Some(year),
488            Some(month),
489            Some(day),
490            Some(hour),
491            Some(min),
492            Some(sec),
493            Some(milli),
494        );
495
496        this.set_data(ObjectData::date(date));
497
498        Ok(this.clone())
499    }
500
501    /// `Date.prototype[@@toPrimitive]`
502    ///
503    /// The [@@toPrimitive]() method converts a Date object to a primitive value.
504    ///
505    /// More information:
506    ///  - [ECMAScript reference][spec]
507    ///  - [MDN documentation][mdn]
508    ///
509    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype-@@toprimitive
510    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/@@toPrimitive
511    #[allow(clippy::wrong_self_convention)]
512    pub(crate) fn to_primitive(
513        this: &JsValue,
514        args: &[JsValue],
515        context: &mut Context,
516    ) -> JsResult<JsValue> {
517        // 1. Let O be the this value.
518        // 2. If Type(O) is not Object, throw a TypeError exception.
519        let o = if let Some(o) = this.as_object() {
520            o
521        } else {
522            return context.throw_type_error("Date.prototype[@@toPrimitive] called on non object");
523        };
524
525        let hint = args.get_or_undefined(0);
526
527        let try_first = match hint.as_string().map(|s| s.as_str()) {
528            // 3. If hint is "string" or "default", then
529            // a. Let tryFirst be string.
530            Some("string") | Some("default") => PreferredType::String,
531            // 4. Else if hint is "number", then
532            // a. Let tryFirst be number.
533            Some("number") => PreferredType::Number,
534            // 5. Else, throw a TypeError exception.
535            _ => {
536                return context
537                    .throw_type_error("Date.prototype[@@toPrimitive] called with invalid hint")
538            }
539        };
540
541        // 6. Return ? OrdinaryToPrimitive(O, tryFirst).
542        o.ordinary_to_primitive(context, try_first)
543    }
544
545    /// `Date.prototype.getDate()`
546    ///
547    /// The `getDate()` method returns the day of the month for the specified date according to local time.
548    ///
549    /// More information:
550    ///  - [ECMAScript reference][spec]
551    ///  - [MDN documentation][mdn]
552    ///
553    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getdate
554    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDate
555    pub fn get_date(&self) -> f64 {
556        self.to_local().map_or(f64::NAN, |dt| dt.day() as f64)
557    }
558
559    /// `Date.prototype.getDay()`
560    ///
561    /// The `getDay()` method returns the day of the week for the specified date according to local time, where 0
562    /// represents Sunday.
563    ///
564    /// More information:
565    ///  - [ECMAScript reference][spec]
566    ///  - [MDN documentation][mdn]
567    ///
568    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getday
569    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDay
570    pub fn get_day(&self) -> f64 {
571        self.to_local().map_or(f64::NAN, |dt| {
572            let weekday = dt.weekday() as u32;
573            let weekday = (weekday + 1) % 7; // 0 represents Monday in Chrono
574            weekday as f64
575        })
576    }
577
578    /// `Date.prototype.getFullYear()`
579    ///
580    /// The `getFullYear()` method returns the year of the specified date according to local time.
581    ///
582    /// More information:
583    ///  - [ECMAScript reference][spec]
584    ///  - [MDN documentation][mdn]
585    ///
586    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getfullyear
587    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getFullYear
588    pub fn get_full_year(&self) -> f64 {
589        self.to_local().map_or(f64::NAN, |dt| dt.year() as f64)
590    }
591
592    /// `Date.prototype.getHours()`
593    ///
594    /// The `getHours()` method returns the hour for the specified date, according to local time.
595    ///
596    /// More information:
597    ///  - [ECMAScript reference][spec]
598    ///  - [MDN documentation][mdn]
599    ///
600    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gethours
601    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getHours
602    pub fn get_hours(&self) -> f64 {
603        self.to_local().map_or(f64::NAN, |dt| dt.hour() as f64)
604    }
605
606    /// `Date.prototype.getMilliseconds()`
607    ///
608    /// The `getMilliseconds()` method returns the milliseconds in the specified date according to local time.
609    ///
610    /// More information:
611    ///  - [ECMAScript reference][spec]
612    ///  - [MDN documentation][mdn]
613    ///
614    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getmilliseconds
615    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMilliseconds
616    pub fn get_milliseconds(&self) -> f64 {
617        self.to_local()
618            .map_or(f64::NAN, |dt| dt.nanosecond() as f64 / NANOS_PER_MS as f64)
619    }
620
621    /// `Date.prototype.getMinutes()`
622    ///
623    /// The `getMinutes()` method returns the minutes in the specified date according to local time.
624    ///
625    /// More information:
626    ///  - [ECMAScript reference][spec]
627    ///  - [MDN documentation][mdn]
628    ///
629    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getminutes
630    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMinutes
631    pub fn get_minutes(&self) -> f64 {
632        self.to_local().map_or(f64::NAN, |dt| dt.minute() as f64)
633    }
634
635    /// `Date.prototype.getMonth()`
636    ///
637    /// The `getMonth()` method returns the month in the specified date according to local time, as a zero-based value
638    /// (where zero indicates the first month of the year).
639    ///
640    /// More information:
641    ///  - [ECMAScript reference][spec]
642    ///  - [MDN documentation][mdn]
643    ///
644    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getmonth
645    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMonth
646    pub fn get_month(&self) -> f64 {
647        self.to_local().map_or(f64::NAN, |dt| dt.month0() as f64)
648    }
649
650    /// `Date.prototype.getSeconds()`
651    ///
652    /// The `getSeconds()` method returns the seconds in the specified date according to local time.
653    ///
654    /// More information:
655    ///  - [ECMAScript reference][spec]
656    ///  - [MDN documentation][mdn]
657    ///
658    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getseconds
659    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getSeconds
660    pub fn get_seconds(&self) -> f64 {
661        self.to_local().map_or(f64::NAN, |dt| dt.second() as f64)
662    }
663
664    /// `Date.prototype.getYear()`
665    ///
666    /// The getYear() method returns the year in the specified date according to local time. Because getYear() does not
667    /// return full years ("year 2000 problem"), it is no longer used and has been replaced by the getFullYear() method.
668    ///
669    /// More information:
670    ///  - [ECMAScript reference][spec]
671    ///  - [MDN documentation][mdn]
672    ///
673    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getyear
674    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getYear
675    pub fn get_year(&self) -> f64 {
676        self.to_local()
677            .map_or(f64::NAN, |dt| dt.year() as f64 - 1900f64)
678    }
679
680    /// `Date.prototype.getTime()`
681    ///
682    /// The `getTime()` method returns the number of milliseconds since the Unix Epoch.
683    ///
684    /// More information:
685    ///  - [ECMAScript reference][spec]
686    ///  - [MDN documentation][mdn]
687    ///
688    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettime
689    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime
690    pub fn get_time(&self) -> f64 {
691        self.to_utc()
692            .map_or(f64::NAN, |dt| dt.timestamp_millis() as f64)
693    }
694
695    /// `Date.prototype.getTimeZoneOffset()`
696    ///
697    /// The getTimezoneOffset() method returns the time zone difference, in minutes, from current locale (host system
698    /// settings) to UTC.
699    ///
700    /// More information:
701    ///  - [ECMAScript reference][spec]
702    ///  - [MDN documentation][mdn]
703    ///
704    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettimezoneoffset
705    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset
706    #[inline]
707    pub fn get_timezone_offset(
708        this: &JsValue,
709        _: &[JsValue],
710        context: &mut Context,
711    ) -> JsResult<JsValue> {
712        // 1. Let t be ? thisTimeValue(this value).
713        let t = this_time_value(this, context)?;
714
715        // 2. If t is NaN, return NaN.
716        if t.0.is_none() {
717            return Ok(JsValue::nan());
718        }
719
720        // 3. Return (t - LocalTime(t)) / msPerMinute.
721        Ok(JsValue::new(
722            -Local::now().offset().local_minus_utc() as f64 / 60f64,
723        ))
724    }
725
726    /// `Date.prototype.getUTCDate()`
727    ///
728    /// The `getUTCDate()` method returns the day (date) of the month in the specified date according to universal time.
729    ///
730    /// More information:
731    ///  - [ECMAScript reference][spec]
732    ///  - [MDN documentation][mdn]
733    ///
734    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcdate
735    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDate
736    pub fn get_utc_date(&self) -> f64 {
737        self.to_utc().map_or(f64::NAN, |dt| dt.day() as f64)
738    }
739
740    /// `Date.prototype.getUTCDay()`
741    ///
742    /// The `getUTCDay()` method returns the day of the week in the specified date according to universal time, where 0
743    /// represents Sunday.
744    ///
745    /// More information:
746    ///  - [ECMAScript reference][spec]
747    ///  - [MDN documentation][mdn]
748    ///
749    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcday
750    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDay
751    pub fn get_utc_day(&self) -> f64 {
752        self.to_utc().map_or(f64::NAN, |dt| {
753            let weekday = dt.weekday() as u32;
754            let weekday = (weekday + 1) % 7; // 0 represents Monday in Chrono
755            weekday as f64
756        })
757    }
758
759    /// `Date.prototype.getUTCFullYear()`
760    ///
761    /// The `getUTCFullYear()` method returns the year in the specified date according to universal time.
762    ///
763    /// More information:
764    ///  - [ECMAScript reference][spec]
765    ///  - [MDN documentation][mdn]
766    ///
767    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcfullyear
768    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCFullYear
769    pub fn get_utc_full_year(&self) -> f64 {
770        self.to_utc().map_or(f64::NAN, |dt| dt.year() as f64)
771    }
772
773    /// `Date.prototype.getUTCHours()`
774    ///
775    /// The `getUTCHours()` method returns the hours in the specified date according to universal time.
776    ///
777    /// More information:
778    ///  - [ECMAScript reference][spec]
779    ///  - [MDN documentation][mdn]
780    ///
781    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutchours
782    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCHours
783    pub fn get_utc_hours(&self) -> f64 {
784        self.to_utc().map_or(f64::NAN, |dt| dt.hour() as f64)
785    }
786
787    /// `Date.prototype.getUTCMilliseconds()`
788    ///
789    /// The `getUTCMilliseconds()` method returns the milliseconds portion of the time object's value.
790    ///
791    /// More information:
792    ///  - [ECMAScript reference][spec]
793    ///  - [MDN documentation][mdn]
794    ///
795    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcmilliseconds
796    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds
797    pub fn get_utc_milliseconds(&self) -> f64 {
798        self.to_utc()
799            .map_or(f64::NAN, |dt| dt.nanosecond() as f64 / NANOS_PER_MS as f64)
800    }
801
802    /// `Date.prototype.getUTCMinutes()`
803    ///
804    /// The `getUTCMinutes()` method returns the minutes in the specified date according to universal time.
805    ///
806    /// More information:
807    ///  - [ECMAScript reference][spec]
808    ///  - [MDN documentation][mdn]
809    ///
810    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcminutes
811    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMinutes
812    pub fn get_utc_minutes(&self) -> f64 {
813        self.to_utc().map_or(f64::NAN, |dt| dt.minute() as f64)
814    }
815
816    /// `Date.prototype.getUTCMonth()`
817    ///
818    /// The `getUTCMonth()` returns the month of the specified date according to universal time, as a zero-based value
819    /// (where zero indicates the first month of the year).
820    ///
821    /// More information:
822    ///  - [ECMAScript reference][spec]
823    ///  - [MDN documentation][mdn]
824    ///
825    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcmonth
826    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMonth
827    pub fn get_utc_month(&self) -> f64 {
828        self.to_utc().map_or(f64::NAN, |dt| dt.month0() as f64)
829    }
830
831    /// `Date.prototype.getUTCSeconds()`
832    ///
833    /// The `getUTCSeconds()` method returns the seconds in the specified date according to universal time.
834    ///
835    /// More information:
836    ///  - [ECMAScript reference][spec]
837    ///  - [MDN documentation][mdn]
838    ///
839    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcseconds
840    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCSeconds
841    pub fn get_utc_seconds(&self) -> f64 {
842        self.to_utc().map_or(f64::NAN, |dt| dt.second() as f64)
843    }
844
845    /// `Date.prototype.setDate()`
846    ///
847    /// The `setDate()` method sets the day of the `Date` object relative to the beginning of the currently set
848    /// month.
849    ///
850    /// More information:
851    ///  - [ECMAScript reference][spec]
852    ///  - [MDN documentation][mdn]
853    ///
854    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setdate
855    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setDate
856    pub fn set_date(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
857        // 1. Let t be LocalTime(? thisTimeValue(this value)).
858        let mut t = this_time_value(this, context)?;
859
860        // 2. Let dt be ? ToNumber(date).
861        let dt = args
862            .get(0)
863            .cloned()
864            .unwrap_or_default()
865            .to_number(context)?;
866
867        // 3. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)).
868        t.set_components(false, None, None, Some(dt), None, None, None, None);
869
870        // 4. Let u be TimeClip(UTC(newDate)).
871        let u = t.get_time();
872
873        // 5. Set the [[DateValue]] internal slot of this Date object to u.
874        this.set_data(ObjectData::date(t));
875
876        // 6. Return u.
877        Ok(u.into())
878    }
879
880    /// `Date.prototype.setFullYear()`
881    ///
882    /// The `setFullYear()` method sets the full year for a specified date according to local time. Returns new
883    /// timestamp.
884    ///
885    /// More information:
886    ///  - [ECMAScript reference][spec]
887    ///  - [MDN documentation][mdn]
888    ///
889    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setfullyear
890    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setFullYear
891    pub fn set_full_year(
892        this: &JsValue,
893        args: &[JsValue],
894        context: &mut Context,
895    ) -> JsResult<JsValue> {
896        // 1. Let t be ? thisTimeValue(this value).
897        let mut t = this_time_value(this, context)?;
898
899        // 2. If t is NaN, set t to +0đť”˝; otherwise, set t to LocalTime(t).
900        if t.0.is_none() {
901            t.0 = NaiveDateTime::from_timestamp_opt(0, 0)
902                .and_then(|local| ignore_ambiguity(Local.from_local_datetime(&local)))
903                .map(|local| local.naive_utc())
904                .filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some());
905        }
906
907        // 3. Let y be ? ToNumber(year).
908        let y = args
909            .get(0)
910            .cloned()
911            .unwrap_or_default()
912            .to_number(context)?;
913
914        // 4. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month).
915        let m = if let Some(m) = args.get(1) {
916            Some(m.to_number(context)?)
917        } else {
918            None
919        };
920
921        // 5. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date).
922        let dt = if let Some(dt) = args.get(2) {
923            Some(dt.to_number(context)?)
924        } else {
925            None
926        };
927
928        // 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)).
929        t.set_components(false, Some(y), m, dt, None, None, None, None);
930
931        // 7. Let u be TimeClip(UTC(newDate)).
932        let u = t.get_time();
933
934        // 8. Set the [[DateValue]] internal slot of this Date object to u.
935        this.set_data(ObjectData::date(t));
936
937        // 9. Return u.
938        Ok(u.into())
939    }
940
941    /// `Date.prototype.setHours()`
942    ///
943    /// The `setHours()` method sets the hours for a specified date according to local time, and returns the number
944    /// of milliseconds since January 1, 1970 00:00:00 UTC until the time represented by the updated `Date`
945    /// instance.
946    ///
947    /// More information:
948    ///  - [ECMAScript reference][spec]
949    ///  - [MDN documentation][mdn]
950    ///
951    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.sethours
952    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setHours
953    pub fn set_hours(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
954        // 1. Let t be LocalTime(? thisTimeValue(this value)).
955        let mut t = this_time_value(this, context)?;
956
957        // 2. Let h be ? ToNumber(hour).
958        let h = args
959            .get(0)
960            .cloned()
961            .unwrap_or_default()
962            .to_number(context)?;
963
964        // 3. If min is not present, let m be MinFromTime(t); otherwise, let m be ? ToNumber(min).
965        let m = if let Some(m) = args.get(1) {
966            Some(m.to_number(context)?)
967        } else {
968            None
969        };
970
971        // 4. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec).
972        let sec = if let Some(sec) = args.get(2) {
973            Some(sec.to_number(context)?)
974        } else {
975            None
976        };
977
978        // 5. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
979        let milli = if let Some(milli) = args.get(3) {
980            Some(milli.to_number(context)?)
981        } else {
982            None
983        };
984
985        // 6. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)).
986        t.set_components(false, None, None, None, Some(h), m, sec, milli);
987
988        // 7. Let u be TimeClip(UTC(date)).
989        let u = t.get_time();
990
991        // 8. Set the [[DateValue]] internal slot of this Date object to u.
992        this.set_data(ObjectData::date(t));
993
994        // 9. Return u.
995        Ok(u.into())
996    }
997
998    /// `Date.prototype.setMilliseconds()`
999    ///
1000    /// The `setMilliseconds()` method sets the milliseconds for a specified date according to local time.
1001    ///
1002    /// More information:
1003    ///  - [ECMAScript reference][spec]
1004    ///  - [MDN documentation][mdn]
1005    ///
1006    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds
1007    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMilliseconds
1008    pub fn set_milliseconds(
1009        this: &JsValue,
1010        args: &[JsValue],
1011        context: &mut Context,
1012    ) -> JsResult<JsValue> {
1013        // 1. Let t be LocalTime(? thisTimeValue(this value)).
1014        let mut t = this_time_value(this, context)?;
1015
1016        // 2. Set ms to ? ToNumber(ms).
1017        let ms = args
1018            .get(0)
1019            .cloned()
1020            .unwrap_or_default()
1021            .to_number(context)?;
1022
1023        // 3. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms).
1024        t.set_components(false, None, None, None, None, None, None, Some(ms));
1025
1026        // 4. Let u be TimeClip(UTC(MakeDate(Day(t), time))).
1027        let u = t.get_time();
1028
1029        // 5. Set the [[DateValue]] internal slot of this Date object to u.
1030        this.set_data(ObjectData::date(t));
1031
1032        // 6. Return u.
1033        Ok(u.into())
1034    }
1035
1036    /// `Date.prototype.setMinutes()`
1037    ///
1038    /// The `setMinutes()` method sets the minutes for a specified date according to local time.
1039    ///
1040    /// More information:
1041    ///  - [ECMAScript reference][spec]
1042    ///  - [MDN documentation][mdn]
1043    ///
1044    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setminutes
1045    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMinutes
1046    pub fn set_minutes(
1047        this: &JsValue,
1048        args: &[JsValue],
1049        context: &mut Context,
1050    ) -> JsResult<JsValue> {
1051        // 1. Let t be LocalTime(? thisTimeValue(this value)).
1052        let mut t = this_time_value(this, context)?;
1053
1054        // 2. Let m be ? ToNumber(min).
1055        let m = args
1056            .get(0)
1057            .cloned()
1058            .unwrap_or_default()
1059            .to_number(context)?;
1060
1061        // 3. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec).
1062        let s = if let Some(s) = args.get(1) {
1063            Some(s.to_number(context)?)
1064        } else {
1065            None
1066        };
1067
1068        // 4. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
1069        let milli = if let Some(milli) = args.get(2) {
1070            Some(milli.to_number(context)?)
1071        } else {
1072            None
1073        };
1074
1075        // 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).
1076        t.set_components(false, None, None, None, None, Some(m), s, milli);
1077
1078        // 6. Let u be TimeClip(UTC(date)).
1079        let u = t.get_time();
1080
1081        // 7. Set the [[DateValue]] internal slot of this Date object to u.
1082        this.set_data(ObjectData::date(t));
1083
1084        // 8. Return u.
1085        Ok(u.into())
1086    }
1087
1088    /// `Date.prototype.setMonth()`
1089    ///
1090    /// The `setMonth()` method sets the month for a specified date according to the currently set year.
1091    ///
1092    /// More information:
1093    ///  - [ECMAScript reference][spec]
1094    ///  - [MDN documentation][mdn]
1095    ///
1096    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setmonth
1097    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth
1098    pub fn set_month(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1099        // 1. Let t be LocalTime(? thisTimeValue(this value)).
1100        let mut t = this_time_value(this, context)?;
1101
1102        // 2. Let m be ? ToNumber(month).
1103        let m = args
1104            .get(0)
1105            .cloned()
1106            .unwrap_or_default()
1107            .to_number(context)?;
1108
1109        // 3. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date).
1110        let dt = if let Some(date) = args.get(1) {
1111            Some(date.to_number(context)?)
1112        } else {
1113            None
1114        };
1115
1116        // 4. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)).
1117        t.set_components(false, None, Some(m), dt, None, None, None, None);
1118
1119        // 5. Let u be TimeClip(UTC(newDate)).
1120        let u = t.get_time();
1121
1122        // 6. Set the [[DateValue]] internal slot of this Date object to u.
1123        this.set_data(ObjectData::date(t));
1124
1125        // 7. Return u.
1126        Ok(u.into())
1127    }
1128
1129    /// `Date.prototype.setSeconds()`
1130    ///
1131    /// The `setSeconds()` method sets the seconds for a specified date according to local time.
1132    ///
1133    /// More information:
1134    ///  - [ECMAScript reference][spec]
1135    ///  - [MDN documentation][mdn]
1136    ///
1137    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setseconds
1138    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setSeconds
1139    pub fn set_seconds(
1140        this: &JsValue,
1141        args: &[JsValue],
1142        context: &mut Context,
1143    ) -> JsResult<JsValue> {
1144        // 1. Let t be LocalTime(? thisTimeValue(this value)).
1145        let mut t = this_time_value(this, context)?;
1146
1147        // 2. Let s be ? ToNumber(sec).
1148        let s = args
1149            .get(0)
1150            .cloned()
1151            .unwrap_or_default()
1152            .to_number(context)?;
1153
1154        // 3. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
1155        let milli = if let Some(milli) = args.get(1) {
1156            Some(milli.to_number(context)?)
1157        } else {
1158            None
1159        };
1160
1161        // 4. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)).
1162        t.set_components(false, None, None, None, None, None, Some(s), milli);
1163
1164        // 5. Let u be TimeClip(UTC(date)).
1165        let u = t.get_time();
1166
1167        // 6. Set the [[DateValue]] internal slot of this Date object to u.
1168        this.set_data(ObjectData::date(t));
1169
1170        // 7. Return u.
1171        Ok(u.into())
1172    }
1173
1174    /// `Date.prototype.setYear()`
1175    ///
1176    /// The `setYear()` method sets the year for a specified date according to local time.
1177    ///
1178    /// More information:
1179    ///  - [ECMAScript reference][spec]
1180    ///  - [MDN documentation][mdn]
1181    ///
1182    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setyear
1183    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setYear
1184    pub fn set_year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1185        // 1. Let t be ? thisTimeValue(this value).
1186        let mut t = this_time_value(this, context)?;
1187
1188        // 2. If t is NaN, set t to +0đť”˝; otherwise, set t to LocalTime(t).
1189        if t.0.is_none() {
1190            t.0 = NaiveDateTime::from_timestamp_opt(0, 0)
1191                .and_then(|local| ignore_ambiguity(Local.from_local_datetime(&local)))
1192                .map(|local| local.naive_utc())
1193                .filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some());
1194        }
1195
1196        // 3. Let y be ? ToNumber(year).
1197        let mut y = args
1198            .get(0)
1199            .cloned()
1200            .unwrap_or_default()
1201            .to_number(context)?;
1202
1203        // 4. If y is NaN, then
1204        if y.is_nan() {
1205            // a. Set the [[DateValue]] internal slot of this Date object to NaN.
1206            this.set_data(ObjectData::date(Date(None)));
1207
1208            // b. Return NaN.
1209            return Ok(JsValue::nan());
1210        }
1211
1212        // 5. Let yi be ! ToIntegerOrInfinity(y).
1213        // 6. If 0 ≤ yi ≤ 99, let yyyy be 1900𝔽 + 𝔽(yi).
1214        // 7. Else, let yyyy be y.
1215        if (0f64..=99f64).contains(&y) {
1216            y += 1900f64;
1217        }
1218
1219        // 8. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)).
1220        // 9. Let date be UTC(MakeDate(d, TimeWithinDay(t))).
1221        t.set_components(false, Some(y), None, None, None, None, None, None);
1222
1223        // 10. Set the [[DateValue]] internal slot of this Date object to TimeClip(date).
1224        this.set_data(ObjectData::date(t));
1225
1226        // 11. Return the value of the [[DateValue]] internal slot of this Date object.
1227        Ok(t.get_time().into())
1228    }
1229
1230    /// `Date.prototype.setTime()`
1231    ///
1232    /// The `setTime()` method sets the Date object to the time represented by a number of milliseconds since
1233    /// January 1, 1970, 00:00:00 UTC.
1234    ///
1235    /// More information:
1236    ///  - [ECMAScript reference][spec]
1237    ///  - [MDN documentation][mdn]
1238    ///
1239    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.settime
1240    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime
1241    pub fn set_time(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1242        // 1. Perform ? thisTimeValue(this value).
1243        this_time_value(this, context)?;
1244
1245        // 2. Let t be ? ToNumber(time).
1246        let t = if let Some(t) = args.get(0) {
1247            let t = t.to_number(context)?;
1248            let seconds = (t / 1_000f64) as i64;
1249            let nanoseconds = ((t % 1_000f64) * 1_000_000f64) as u32;
1250            Date(
1251                ignore_ambiguity(Local.timestamp_opt(seconds, nanoseconds))
1252                    .map(|dt| dt.naive_utc()),
1253            )
1254        } else {
1255            Date(None)
1256        };
1257
1258        // 3. Let v be TimeClip(t).
1259        let v = t.get_time();
1260
1261        // 4. Set the [[DateValue]] internal slot of this Date object to v.
1262        this.set_data(ObjectData::date(t));
1263
1264        // 5. Return v.
1265        Ok(v.into())
1266    }
1267
1268    /// `Date.prototype.setUTCDate()`
1269    ///
1270    /// The `setUTCDate()` method sets the day of the month for a specified date according to universal time.
1271    ///
1272    /// More information:
1273    ///  - [ECMAScript reference][spec]
1274    ///  - [MDN documentation][mdn]
1275    ///
1276    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcdate
1277    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCDate
1278    pub fn set_utc_date(
1279        this: &JsValue,
1280        args: &[JsValue],
1281        context: &mut Context,
1282    ) -> JsResult<JsValue> {
1283        // 1. Let t be ? thisTimeValue(this value).
1284        let mut t = this_time_value(this, context)?;
1285
1286        // 2. Let dt be ? ToNumber(date).
1287        let dt = args
1288            .get(0)
1289            .cloned()
1290            .unwrap_or_default()
1291            .to_number(context)?;
1292
1293        // 3. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)).
1294        t.set_components(true, None, None, Some(dt), None, None, None, None);
1295
1296        // 4. Let v be TimeClip(newDate).
1297        let v = t.get_time();
1298
1299        // 5. Set the [[DateValue]] internal slot of this Date object to v.
1300        this.set_data(ObjectData::date(t));
1301
1302        // 6. Return v.
1303        Ok(v.into())
1304    }
1305
1306    /// `Date.prototype.setFullYear()`
1307    ///
1308    /// The `setFullYear()` method sets the full year for a specified date according to local time. Returns new
1309    /// timestamp.
1310    ///
1311    /// More information:
1312    ///  - [ECMAScript reference][spec]
1313    ///  - [MDN documentation][mdn]
1314    ///
1315    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcfullyear
1316    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCFullYear
1317    pub fn set_utc_full_year(
1318        this: &JsValue,
1319        args: &[JsValue],
1320        context: &mut Context,
1321    ) -> JsResult<JsValue> {
1322        // 1. Let t be ? thisTimeValue(this value).
1323        let mut t = this_time_value(this, context)?;
1324
1325        // 2. If t is NaN, set t to +0đť”˝.
1326        if t.0.is_none() {
1327            t.0 = NaiveDateTime::from_timestamp_opt(0, 0)
1328                .and_then(|local| ignore_ambiguity(Local.from_local_datetime(&local)))
1329                .map(|local| local.naive_utc())
1330                .filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some());
1331        }
1332
1333        // 3. Let y be ? ToNumber(year).
1334        let y = args
1335            .get(0)
1336            .cloned()
1337            .unwrap_or_default()
1338            .to_number(context)?;
1339
1340        // 4. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month).
1341        let m = if let Some(m) = args.get(1) {
1342            Some(m.to_number(context)?)
1343        } else {
1344            None
1345        };
1346
1347        // 5. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date).
1348        let dt = if let Some(dt) = args.get(2) {
1349            Some(dt.to_number(context)?)
1350        } else {
1351            None
1352        };
1353
1354        // 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)).
1355        t.set_components(true, Some(y), m, dt, None, None, None, None);
1356
1357        // 7. Let v be TimeClip(newDate).
1358        let v = t.get_time();
1359
1360        // 8. Set the [[DateValue]] internal slot of this Date object to v.
1361        this.set_data(ObjectData::date(t));
1362
1363        // 9. Return v.
1364        Ok(v.into())
1365    }
1366
1367    /// `Date.prototype.setUTCHours()`
1368    ///
1369    /// The `setUTCHours()` method sets the hour for a specified date according to universal time, and returns the
1370    /// number of milliseconds since  January 1, 1970 00:00:00 UTC until the time represented by the updated `Date`
1371    /// instance.
1372    ///
1373    /// More information:
1374    ///  - [ECMAScript reference][spec]
1375    ///  - [MDN documentation][mdn]
1376    ///
1377    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutchours
1378    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCHours
1379    pub fn set_utc_hours(
1380        this: &JsValue,
1381        args: &[JsValue],
1382        context: &mut Context,
1383    ) -> JsResult<JsValue> {
1384        // 1. Let t be ? thisTimeValue(this value).
1385        let mut t = this_time_value(this, context)?;
1386
1387        // 2. Let h be ? ToNumber(hour).
1388        let h = args
1389            .get(0)
1390            .cloned()
1391            .unwrap_or_default()
1392            .to_number(context)?;
1393
1394        // 3. If min is not present, let m be MinFromTime(t); otherwise, let m be ? ToNumber(min).
1395        let m = if let Some(m) = args.get(1) {
1396            Some(m.to_number(context)?)
1397        } else {
1398            None
1399        };
1400
1401        // 4. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec).
1402        let sec = if let Some(s) = args.get(2) {
1403            Some(s.to_number(context)?)
1404        } else {
1405            None
1406        };
1407
1408        // 5. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms).
1409        let ms = if let Some(ms) = args.get(3) {
1410            Some(ms.to_number(context)?)
1411        } else {
1412            None
1413        };
1414
1415        // 6. Let newDate be MakeDate(Day(t), MakeTime(h, m, s, milli)).
1416        t.set_components(true, None, None, None, Some(h), m, sec, ms);
1417
1418        // 7. Let v be TimeClip(newDate).
1419        let v = t.get_time();
1420
1421        // 8. Set the [[DateValue]] internal slot of this Date object to v.
1422        this.set_data(ObjectData::date(t));
1423
1424        // 9. Return v.
1425        Ok(v.into())
1426    }
1427
1428    /// `Date.prototype.setUTCMilliseconds()`
1429    ///
1430    /// The `setUTCMilliseconds()` method sets the milliseconds for a specified date according to universal time.
1431    ///
1432    /// More information:
1433    ///  - [ECMAScript reference][spec]
1434    ///  - [MDN documentation][mdn]
1435    ///
1436    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcmilliseconds
1437    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMilliseconds
1438    pub fn set_utc_milliseconds(
1439        this: &JsValue,
1440        args: &[JsValue],
1441        context: &mut Context,
1442    ) -> JsResult<JsValue> {
1443        // 1. Let t be ? thisTimeValue(this value).
1444        let mut t = this_time_value(this, context)?;
1445
1446        // 2. Let milli be ? ToNumber(ms).
1447        let ms = args
1448            .get(0)
1449            .cloned()
1450            .unwrap_or_default()
1451            .to_number(context)?;
1452
1453        // 3. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli).
1454        t.set_components(true, None, None, None, None, None, None, Some(ms));
1455
1456        // 4. Let v be TimeClip(MakeDate(Day(t), time)).
1457        let v = t.get_time();
1458
1459        // 5. Set the [[DateValue]] internal slot of this Date object to v.
1460        this.set_data(ObjectData::date(t));
1461
1462        // 6. Return v.
1463        Ok(v.into())
1464    }
1465
1466    /// `Date.prototype.setUTCMinutes()`
1467    ///
1468    /// The `setUTCMinutes()` method sets the minutes for a specified date according to universal time.
1469    ///
1470    /// More information:
1471    ///  - [ECMAScript reference][spec]
1472    ///  - [MDN documentation][mdn]
1473    ///
1474    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcminutes
1475    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMinutes
1476    pub fn set_utc_minutes(
1477        this: &JsValue,
1478        args: &[JsValue],
1479        context: &mut Context,
1480    ) -> JsResult<JsValue> {
1481        // 1. Let t be ? thisTimeValue(this value).
1482        let mut t = this_time_value(this, context)?;
1483
1484        // 2. Let m be ? ToNumber(min).
1485        let m = args
1486            .get(0)
1487            .cloned()
1488            .unwrap_or_default()
1489            .to_number(context)?;
1490
1491        // 3. If sec is not present, let s be SecFromTime(t).
1492        // 4. Else,
1493        let s = if let Some(s) = args.get(1) {
1494            // a. Let s be ? ToNumber(sec).
1495            Some(s.to_number(context)?)
1496        } else {
1497            None
1498        };
1499
1500        // 5. If ms is not present, let milli be msFromTime(t).
1501        // 6. Else,
1502        let milli = if let Some(ms) = args.get(2) {
1503            // a. Let milli be ? ToNumber(ms).
1504            Some(ms.to_number(context)?)
1505        } else {
1506            None
1507        };
1508
1509        // 7. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).
1510        t.set_components(true, None, None, None, None, Some(m), s, milli);
1511
1512        // 8. Let v be TimeClip(date).
1513        let v = t.get_time();
1514
1515        // 9. Set the [[DateValue]] internal slot of this Date object to v.
1516        this.set_data(ObjectData::date(t));
1517
1518        // 10. Return v.
1519        Ok(v.into())
1520    }
1521
1522    /// `Date.prototype.setUTCMonth()`
1523    ///
1524    /// The `setUTCMonth()` method sets the month for a specified date according to universal time.
1525    ///
1526    /// More information:
1527    ///  - [ECMAScript reference][spec]
1528    ///  - [MDN documentation][mdn]
1529    ///
1530    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcmonth
1531    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMonth
1532    pub fn set_utc_month(
1533        this: &JsValue,
1534        args: &[JsValue],
1535        context: &mut Context,
1536    ) -> JsResult<JsValue> {
1537        // 1. Let t be ? thisTimeValue(this value).
1538        let mut t = this_time_value(this, context)?;
1539
1540        // 2. Let m be ? ToNumber(month).
1541        let m = args
1542            .get(0)
1543            .cloned()
1544            .unwrap_or_default()
1545            .to_number(context)?;
1546
1547        // 3. If date is not present, let dt be DateFromTime(t).
1548        // 4. Else,
1549        let dt = if let Some(dt) = args.get(1) {
1550            // a. Let dt be ? ToNumber(date).
1551            Some(dt.to_number(context)?)
1552        } else {
1553            None
1554        };
1555
1556        // 5. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)).
1557        t.set_components(true, None, Some(m), dt, None, None, None, None);
1558
1559        // 6. Let v be TimeClip(newDate).
1560        let v = t.get_time();
1561
1562        // 7. Set the [[DateValue]] internal slot of this Date object to v.
1563        this.set_data(ObjectData::date(t));
1564
1565        // 8. Return v.
1566        Ok(v.into())
1567    }
1568
1569    /// `Date.prototype.setUTCSeconds()`
1570    ///
1571    /// The `setUTCSeconds()` method sets the seconds for a specified date according to universal time.
1572    ///
1573    /// More information:
1574    ///  - [ECMAScript reference][spec]
1575    ///  - [MDN documentation][mdn]
1576    ///
1577    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcseconds
1578    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCSeconds
1579    pub fn set_utc_seconds(
1580        this: &JsValue,
1581        args: &[JsValue],
1582        context: &mut Context,
1583    ) -> JsResult<JsValue> {
1584        // 1. Let t be ? thisTimeValue(this value).
1585        let mut t = this_time_value(this, context)?;
1586
1587        // 2. Let s be ? ToNumber(sec).
1588        let s = args
1589            .get(0)
1590            .cloned()
1591            .unwrap_or_default()
1592            .to_number(context)?;
1593
1594        // 3. If ms is not present, let milli be msFromTime(t).
1595        // 4. Else,
1596        let milli = if let Some(milli) = args.get(1) {
1597            // a. Let milli be ? ToNumber(ms).
1598            Some(milli.to_number(context)?)
1599        } else {
1600            None
1601        };
1602
1603        // 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)).
1604        t.set_components(true, None, None, None, None, None, Some(s), milli);
1605
1606        // 6. Let v be TimeClip(date).
1607        let v = t.get_time();
1608
1609        // 7. Set the [[DateValue]] internal slot of this Date object to v.
1610        this.set_data(ObjectData::date(t));
1611
1612        // 8. Return v.
1613        Ok(v.into())
1614    }
1615
1616    /// `Date.prototype.toDateString()`
1617    ///
1618    /// The `toDateString()` method returns the date portion of a Date object in English.
1619    ///
1620    /// More information:
1621    ///  - [ECMAScript reference][spec]
1622    ///  - [MDN documentation][mdn]
1623    ///
1624    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.todatestring
1625    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toDateString
1626    #[allow(clippy::wrong_self_convention)]
1627    pub fn to_date_string(
1628        this: &JsValue,
1629        _: &[JsValue],
1630        context: &mut Context,
1631    ) -> JsResult<JsValue> {
1632        // 1. Let O be this Date object.
1633        // 2. Let tv be ? thisTimeValue(O).
1634        let tv = this_time_value(this, context)?;
1635
1636        // 3. If tv is NaN, return "Invalid Date".
1637        // 4. Let t be LocalTime(tv).
1638        // 5. Return DateString(t).
1639        if let Some(t) = tv.0 {
1640            Ok(t.format("%a %b %d %Y").to_string().into())
1641        } else {
1642            Ok(JsString::from("Invalid Date").into())
1643        }
1644    }
1645
1646    /// `Date.prototype.toGMTString()`
1647    ///
1648    /// The `toGMTString()` method converts a date to a string, using Internet Greenwich Mean Time (GMT) conventions.
1649    ///
1650    /// More information:
1651    ///  - [ECMAScript reference][spec]
1652    ///  - [MDN documentation][mdn]
1653    ///
1654    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.togmtstring
1655    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toGMTString
1656    pub fn to_gmt_string(self) -> String {
1657        self.to_utc_string()
1658    }
1659
1660    /// `Date.prototype.toISOString()`
1661    ///
1662    /// The `toISOString()` method returns a string in simplified extended ISO format (ISO 8601).
1663    ///
1664    /// More information:
1665    ///  - [ISO 8601][iso8601]
1666    ///  - [ECMAScript reference][spec]
1667    ///  - [MDN documentation][mdn]
1668    ///
1669    /// [iso8601]: http://en.wikipedia.org/wiki/ISO_8601
1670    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toisostring
1671    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
1672    #[allow(clippy::wrong_self_convention)]
1673    pub fn to_iso_string(
1674        this: &JsValue,
1675        _: &[JsValue],
1676        context: &mut Context,
1677    ) -> JsResult<JsValue> {
1678        if let Some(t) = this_time_value(this, context)?.0 {
1679            Ok(Utc::now()
1680                .timezone()
1681                .from_utc_datetime(&t)
1682                .format("%Y-%m-%dT%H:%M:%S.%3fZ")
1683                .to_string()
1684                .into())
1685        } else {
1686            context.throw_range_error("Invalid time value")
1687        }
1688    }
1689
1690    /// `Date.prototype.toJSON()`
1691    ///
1692    /// The `toJSON()` method returns a string representation of the `Date` object.
1693    ///
1694    /// More information:
1695    ///  - [ECMAScript reference][spec]
1696    ///  - [MDN documentation][mdn]
1697    ///
1698    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tojson
1699    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON
1700    #[allow(clippy::wrong_self_convention)]
1701    pub fn to_json(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1702        // 1. Let O be ? ToObject(this value).
1703        let o = this.to_object(context)?;
1704
1705        // 2. Let tv be ? ToPrimitive(O, number).
1706        let tv = this.to_primitive(context, PreferredType::Number)?;
1707
1708        // 3. If Type(tv) is Number and tv is not finite, return null.
1709        if let Some(number) = tv.as_number() {
1710            if !number.is_finite() {
1711                return Ok(JsValue::null());
1712            }
1713        }
1714
1715        // 4. Return ? Invoke(O, "toISOString").
1716        if let Some(to_iso_string) = o.get_method(context, "toISOString")? {
1717            to_iso_string.call(this, &[], context)
1718        } else {
1719            context.throw_type_error("toISOString in undefined")
1720        }
1721    }
1722
1723    /// `Date.prototype.toString()`
1724    ///
1725    /// The toString() method returns a string representing the specified Date object.
1726    ///
1727    /// More information:
1728    ///  - [ECMAScript reference][spec]
1729    ///  - [MDN documentation][mdn]
1730    ///
1731    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tostring
1732    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString
1733    #[allow(clippy::wrong_self_convention)]
1734    pub fn to_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1735        // 1. Let tv be ? thisTimeValue(this value).
1736        let tv = this_time_value(this, context)?;
1737
1738        // 2. Return ToDateString(tv).
1739        if let Some(t) = tv.0 {
1740            Ok(Local::now()
1741                .timezone()
1742                .from_utc_datetime(&t)
1743                .format("%a %b %d %Y %H:%M:%S GMT%z")
1744                .to_string()
1745                .into())
1746        } else {
1747            Ok(JsString::from("Invalid Date").into())
1748        }
1749    }
1750
1751    /// `Date.prototype.toTimeString()`
1752    ///
1753    /// The `toTimeString()` method returns the time portion of a Date object in human readable form in American
1754    /// English.
1755    ///
1756    /// More information:
1757    ///  - [ECMAScript reference][spec]
1758    ///  - [MDN documentation][mdn]
1759    ///
1760    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.totimestring
1761    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTimeString
1762    #[allow(clippy::wrong_self_convention)]
1763    pub fn to_time_string(
1764        this: &JsValue,
1765        _: &[JsValue],
1766        context: &mut Context,
1767    ) -> JsResult<JsValue> {
1768        // 1. Let O be this Date object.
1769        // 2. Let tv be ? thisTimeValue(O).
1770        let tv = this_time_value(this, context)?;
1771
1772        // 3. If tv is NaN, return "Invalid Date".
1773        // 4. Let t be LocalTime(tv).
1774        // 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv).
1775        if let Some(t) = tv.0 {
1776            Ok(Local::now()
1777                .timezone()
1778                .from_utc_datetime(&t)
1779                .format("%H:%M:%S GMT%z")
1780                .to_string()
1781                .into())
1782        } else {
1783            Ok(JsString::from("Invalid Date").into())
1784        }
1785    }
1786
1787    /// `Date.prototype.toUTCString()`
1788    ///
1789    /// The `toUTCString()` method returns a string representing the specified Date object.
1790    ///
1791    /// More information:
1792    ///  - [ECMAScript reference][spec]
1793    ///  - [MDN documentation][mdn]
1794    ///
1795    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toutcstring
1796    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toUTCString
1797    pub fn to_utc_string(self) -> String {
1798        self.to_utc()
1799            .map(|date_time| date_time.format("%a, %d %b %Y %H:%M:%S GMT").to_string())
1800            .unwrap_or_else(|| "Invalid Date".to_string())
1801    }
1802
1803    /// `Date.prototype.valueOf()`
1804    ///
1805    /// The `valueOf()` method returns the primitive value of a `Date` object.
1806    ///
1807    /// More information:
1808    ///  - [ECMAScript reference][spec]
1809    ///  - [MDN documentation][mdn]
1810    ///
1811    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.valueof
1812    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/valueOf
1813    pub fn value_of(&self) -> f64 {
1814        self.get_time()
1815    }
1816
1817    /// `Date.now()`
1818    ///
1819    /// The static `Date.now()` method returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC.
1820    ///
1821    /// More information:
1822    ///  - [ECMAScript reference][spec]
1823    ///  - [MDN documentation][mdn]
1824    ///
1825    /// [spec]: https://tc39.es/ecma262/#sec-date.now
1826    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now
1827    pub(crate) fn now(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
1828        Ok(JsValue::new(Utc::now().timestamp_millis() as f64))
1829    }
1830
1831    /// `Date.parse()`
1832    ///
1833    /// The `Date.parse()` method parses a string representation of a date, and returns the number of milliseconds since
1834    /// January 1, 1970, 00:00:00 UTC or NaN if the string is unrecognized or, in some cases, contains illegal date
1835    /// values.
1836    ///
1837    /// More information:
1838    ///  - [ECMAScript reference][spec]
1839    ///  - [MDN documentation][mdn]
1840    ///
1841    /// [spec]: https://tc39.es/ecma262/#sec-date.parse
1842    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse
1843    pub(crate) fn parse(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1844        // This method is implementation-defined and discouraged, so we just require the same format as the string
1845        // constructor.
1846
1847        if args.is_empty() {
1848            return Ok(JsValue::nan());
1849        }
1850
1851        match DateTime::parse_from_rfc3339(&args[0].to_string(context)?) {
1852            Ok(v) => Ok(JsValue::new(v.naive_utc().timestamp_millis() as f64)),
1853            _ => Ok(JsValue::new(f64::NAN)),
1854        }
1855    }
1856
1857    /// `Date.UTC()`
1858    ///
1859    /// The `Date.UTC()` method accepts parameters similar to the `Date` constructor, but treats them as UTC.
1860    ///
1861    /// More information:
1862    ///  - [ECMAScript reference][spec]
1863    ///  - [MDN documentation][mdn]
1864    ///
1865    /// [spec]: https://tc39.es/ecma262/#sec-date.utc
1866    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC
1867    pub(crate) fn utc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1868        let year = args
1869            .get(0)
1870            .map_or(Ok(f64::NAN), |value| value.to_number(context))?;
1871        let month = args
1872            .get(1)
1873            .map_or(Ok(0f64), |value| value.to_number(context))?;
1874        let day = args
1875            .get(2)
1876            .map_or(Ok(1f64), |value| value.to_number(context))?;
1877        let hour = args
1878            .get(3)
1879            .map_or(Ok(0f64), |value| value.to_number(context))?;
1880        let min = args
1881            .get(4)
1882            .map_or(Ok(0f64), |value| value.to_number(context))?;
1883        let sec = args
1884            .get(5)
1885            .map_or(Ok(0f64), |value| value.to_number(context))?;
1886        let milli = args
1887            .get(6)
1888            .map_or(Ok(0f64), |value| value.to_number(context))?;
1889
1890        if !check_normal_opt!(year, month, day, hour, min, sec, milli) {
1891            return Ok(JsValue::nan());
1892        }
1893
1894        let year = year as i32;
1895        let month = month as u32;
1896        let day = day as u32;
1897        let hour = hour as u32;
1898        let min = min as u32;
1899        let sec = sec as u32;
1900        let milli = milli as u32;
1901
1902        let year = if (0..=99).contains(&year) {
1903            1900 + year
1904        } else {
1905            year
1906        };
1907
1908        NaiveDate::from_ymd_opt(year, month + 1, day)
1909            .and_then(|f| f.and_hms_milli_opt(hour, min, sec, milli))
1910            .and_then(|f| Self::time_clip(f.timestamp_millis() as f64))
1911            .map_or(Ok(JsValue::nan()), |time| Ok(JsValue::new(time)))
1912    }
1913}
1914
1915/// The abstract operation `thisTimeValue` takes argument value.
1916///
1917/// In following descriptions of functions that are properties of the Date prototype object, the phrase “this
1918/// Date object” refers to the object that is the this value for the invocation of the function. If the `Type` of
1919/// the this value is not `Object`, a `TypeError` exception is thrown. The phrase “this time value” within the
1920/// specification of a method refers to the result returned by calling the abstract operation `thisTimeValue` with
1921/// the this value of the method invocation passed as the argument.
1922///
1923/// More information:
1924///  - [ECMAScript reference][spec]
1925///
1926/// [spec]: https://tc39.es/ecma262/#sec-thistimevalue
1927#[inline]
1928pub fn this_time_value(value: &JsValue, context: &mut Context) -> JsResult<Date> {
1929    if let JsValue::Object(ref object) = value {
1930        if let Some(date) = object.borrow().as_date() {
1931            return Ok(*date);
1932        }
1933    }
1934    Err(context.construct_type_error("'this' is not a Date"))
1935}