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}