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}