From 1ae48441fd03b43b4a00dbf0aebdb4197d85042f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Juli=C3=A1n=20Espina?= Date: Tue, 22 Nov 2022 20:04:05 +0000 Subject: [PATCH] Refactor the `Date` builtin (#2449) Just a general cleanup of the `Date` builtin to use slightly better patterns and to fix our warnings about deprecated functions. About the regressed tests. It seems to be a `chrono` bug, so I opened up an issue (https://github.com/chronotope/chrono/issues/884) for it and they've already opened a PR fixing it (https://github.com/chronotope/chrono/pull/885). However, while checking out the remaining failing tests, I realized there's a more fundamental limitation with the library. Currently, [`chrono`](https://github.com/chronotope/chrono) specifies: > Date types are limited in about +/- 262,000 years from the common epoch. While the [ECMAScript spec](https://tc39.es/ecma262/#sec-time-values-and-time-range) says: > The smaller range supported by a time value as specified in this section is approximately -273,790 to 273,790 years relative to 1970. The range allowed by the spec is barely outside of the range supported by `chrono`! This is why the remaining `Date` tests fail. Seeing that, I would like to ping @djc and @esheppa (the maintainers of `chrono`) to ask if it would be feasible to add a feature, akin to the `large-dates` feature from the `time` crate, that expands the supported range of `chrono`. EDIT: Filed https://github.com/chronotope/chrono/issues/886 --- boa_engine/src/builtins/date/mod.rs | 2506 ++++++++------------ boa_engine/src/builtins/date/tests.rs | 711 +++--- boa_engine/src/builtins/date/utils.rs | 200 ++ boa_engine/src/builtins/set/mod.rs | 26 +- boa_engine/src/builtins/set/ordered_set.rs | 6 + boa_engine/src/builtins/weak/weak_ref.rs | 2 +- boa_engine/src/context/mod.rs | 1 + boa_engine/src/object/builtins/jsdate.rs | 112 +- boa_engine/src/object/mod.rs | 11 + boa_engine/src/value/integer.rs | 26 +- boa_engine/src/value/mod.rs | 26 +- boa_engine/src/vm/mod.rs | 7 +- 12 files changed, 1701 insertions(+), 1933 deletions(-) create mode 100644 boa_engine/src/builtins/date/utils.rs diff --git a/boa_engine/src/builtins/date/mod.rs b/boa_engine/src/builtins/date/mod.rs index 2319ce053b..2e0776ae19 100644 --- a/boa_engine/src/builtins/date/mod.rs +++ b/boa_engine/src/builtins/date/mod.rs @@ -1,3 +1,5 @@ +mod utils; +use utils::{make_date, make_day, make_time, replace_params, time_clip, DateParameters}; #[cfg(test)] mod tests; @@ -12,45 +14,69 @@ use crate::{ }, string::utf16, symbol::WellKnownSymbols, - value::{IntegerOrInfinity, JsValue, PreferredType}, + value::{IntegerOrNan, JsValue, PreferredType}, Context, JsError, JsResult, }; use boa_profiler::Profiler; -use chrono::{prelude::*, Duration, LocalResult}; +use chrono::prelude::*; use std::fmt::Display; use tap::{Conv, Pipe}; -/// The number of nanoseconds in a millisecond. -const NANOS_PER_MS: i64 = 1_000_000; -/// The number of milliseconds in an hour. -const MILLIS_PER_HOUR: i64 = 3_600_000; -/// The number of milliseconds in a minute. -const MILLIS_PER_MINUTE: i64 = 60_000; -/// The number of milliseconds in a second. -const MILLIS_PER_SECOND: i64 = 1000; - -#[inline] -fn is_zero_or_normal_opt(value: Option) -> bool { - value.map_or(true, |value| value == 0f64 || value.is_normal()) +/// Extracts `Some` from an `Option` or returns `NaN` if the object contains `None`. +macro_rules! some_or_nan { + ($v:expr) => { + match $v { + Some(dt) => dt, + None => return Ok(JsValue::from(f64::NAN)), + } + }; } -macro_rules! check_normal_opt { - ($($v:expr),+) => { - $(is_zero_or_normal_opt($v.into()) &&)+ true +/// Gets a mutable reference to the inner `Date` object of `val` and stores it on `var`, or returns +/// a `TypeError` if `val` is not a `Date` object. +macro_rules! get_mut_date { + (let $var:ident = $val:expr) => { + let mut $var = $val + .as_object() + .map(JsObject::borrow_mut) + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?; + let $var = $var + .as_date_mut() + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))?; }; } +/// Abstract operation [`thisTimeValue`][spec]. +/// +/// [spec]: https://tc39.es/ecma262/#sec-thistimevalue #[inline] -fn ignore_ambiguity(result: LocalResult) -> Option { - match result { - LocalResult::Ambiguous(v, _) | LocalResult::Single(v) => Some(v), - LocalResult::None => None, - } +pub(super) fn this_time_value(value: &JsValue) -> JsResult> { + Ok(value + .as_object() + .and_then(|obj| obj.borrow().as_date().copied()) + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Date"))? + .0) } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Date(Option); +impl Date { + /// Creates a new `Date`. + #[inline] + pub(crate) fn new(dt: Option) -> Self { + Self(dt) + } + + /// Converts the `Date` into a `JsValue`, mapping `None` to `NaN` and `Some(datetime)` to + /// `JsValue::from(datetime.timestamp_millis())`. + #[inline] + fn as_value(&self) -> JsValue { + self.0 + .map_or_else(|| f64::NAN.into(), |dt| dt.timestamp_millis().into()) + } +} + impl Display for Date { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(v) = self.to_local() { @@ -80,42 +106,42 @@ impl BuiltIn for Date { ) .name(Self::NAME) .length(Self::LENGTH) - .method(Self::get_date, "getDate", 0) - .method(Self::get_day, "getDay", 0) - .method(Self::get_full_year, "getFullYear", 0) - .method(Self::get_hours, "getHours", 0) - .method(Self::get_milliseconds, "getMilliseconds", 0) - .method(Self::get_minutes, "getMinutes", 0) - .method(Self::get_month, "getMonth", 0) - .method(Self::get_seconds, "getSeconds", 0) + .method(Self::get_date::, "getDate", 0) + .method(Self::get_day::, "getDay", 0) + .method(Self::get_full_year::, "getFullYear", 0) + .method(Self::get_hours::, "getHours", 0) + .method(Self::get_milliseconds::, "getMilliseconds", 0) + .method(Self::get_minutes::, "getMinutes", 0) + .method(Self::get_month::, "getMonth", 0) + .method(Self::get_seconds::, "getSeconds", 0) .method(Self::get_time, "getTime", 0) .method(Self::get_timezone_offset, "getTimezoneOffset", 0) - .method(Self::get_utc_date, "getUTCDate", 0) - .method(Self::get_utc_day, "getUTCDay", 0) - .method(Self::get_utc_full_year, "getUTCFullYear", 0) - .method(Self::get_utc_hours, "getUTCHours", 0) - .method(Self::get_utc_milliseconds, "getUTCMilliseconds", 0) - .method(Self::get_utc_minutes, "getUTCMinutes", 0) - .method(Self::get_utc_month, "getUTCMonth", 0) - .method(Self::get_utc_seconds, "getUTCSeconds", 0) + .method(Self::get_date::, "getUTCDate", 0) + .method(Self::get_day::, "getUTCDay", 0) + .method(Self::get_full_year::, "getUTCFullYear", 0) + .method(Self::get_hours::, "getUTCHours", 0) + .method(Self::get_milliseconds::, "getUTCMilliseconds", 0) + .method(Self::get_minutes::, "getUTCMinutes", 0) + .method(Self::get_month::, "getUTCMonth", 0) + .method(Self::get_seconds::, "getUTCSeconds", 0) .method(Self::get_year, "getYear", 0) .static_method(Self::now, "now", 0) .static_method(Self::parse, "parse", 1) - .method(Self::set_date, "setDate", 1) - .method(Self::set_full_year, "setFullYear", 3) - .method(Self::set_hours, "setHours", 4) - .method(Self::set_milliseconds, "setMilliseconds", 1) - .method(Self::set_minutes, "setMinutes", 3) - .method(Self::set_month, "setMonth", 2) - .method(Self::set_seconds, "setSeconds", 2) + .method(Self::set_date::, "setDate", 1) + .method(Self::set_full_year::, "setFullYear", 3) + .method(Self::set_hours::, "setHours", 4) + .method(Self::set_milliseconds::, "setMilliseconds", 1) + .method(Self::set_minutes::, "setMinutes", 3) + .method(Self::set_month::, "setMonth", 2) + .method(Self::set_seconds::, "setSeconds", 2) .method(Self::set_time, "setTime", 1) - .method(Self::set_utc_date, "setUTCDate", 1) - .method(Self::set_utc_full_year, "setUTCFullYear", 3) - .method(Self::set_utc_hours, "setUTCHours", 4) - .method(Self::set_utc_milliseconds, "setUTCMilliseconds", 1) - .method(Self::set_utc_minutes, "setUTCMinutes", 3) - .method(Self::set_utc_month, "setUTCMonth", 2) - .method(Self::set_utc_seconds, "setUTCSeconds", 2) + .method(Self::set_date::, "setUTCDate", 1) + .method(Self::set_full_year::, "setUTCFullYear", 3) + .method(Self::set_hours::, "setUTCHours", 4) + .method(Self::set_milliseconds::, "setUTCMilliseconds", 1) + .method(Self::set_minutes::, "setUTCMinutes", 3) + .method(Self::set_month::, "setUTCMonth", 2) + .method(Self::set_seconds::, "setUTCSeconds", 2) .method(Self::set_year, "setYear", 1) .method(Self::to_date_string, "toDateString", 0) .method(Self::to_gmt_string, "toGMTString", 0) @@ -144,627 +170,479 @@ impl Date { /// The amount of arguments this function object takes. pub(crate) const LENGTH: usize = 7; - /// Check if the time (number of milliseconds) is in the expected range. - /// Returns None if the time is not in the range, otherwise returns the time itself in option. + /// [`Date ( ...values )`][spec] /// - /// More information: - /// - [ECMAScript reference][spec] + /// - When called as a function, returns a string displaying the current time in the UTC timezone. + /// - When called as a constructor, it returns a new `Date` object from the provided arguments. + /// The [MDN documentation][mdn] has a more extensive explanation on the usages and return + /// values for all possible arguments. /// - /// [spec]: https://tc39.es/ecma262/#sec-timeclip - #[inline] - pub fn time_clip(time: f64) -> Option { - // 1. If time is not finite, return NaN. - // 2. If abs(โ„(time)) > 8.64 ร— 1015, return NaN. - // 3. Return ๐”ฝ(! ToIntegerOrInfinity(time)). - if time.is_nan() { - return None; - } - match IntegerOrInfinity::from(time) { - IntegerOrInfinity::Integer(i) if i.abs() <= 864i64 * 10i64.pow(13) => Some(i), - _ => None, + /// [spec]: https://tc39.es/ecma262/#sec-date-constructor + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date + pub(crate) fn constructor( + new_target: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { + // 1. If NewTarget is undefined, then + if new_target.is_undefined() { + // a. Let now be the time value (UTC) identifying the current time. + // b. Return ToDateString(now). + return Ok(JsValue::new( + Local::now() + .format("%a %b %d %Y %H:%M:%S GMT%:z") + .to_string(), + )); } - } + // 2. Let numberOfArgs be the number of elements in values. + let dv = match args { + // 3. If numberOfArgs = 0, then + [] => { + // a. Let dv be the time value (UTC) identifying the current time. + Date::default() + } + // 4. Else if numberOfArgs = 1, then + // a. Let value be values[0]. + [value] => match value + .as_object() + .and_then(|obj| obj.borrow().as_date().copied()) + { + // b. If value is an Object and value has a [[DateValue]] internal slot, then + Some(dt) => { + // i. Let tv be ! thisTimeValue(value). + dt + } + // c. Else, + None => { + // i. Let v be ? ToPrimitive(value). + match value.to_primitive(context, PreferredType::Default)? { + // ii. If v is a String, then + JsValue::String(ref str) => { + // 1. Assert: The next step never returns an abrupt completion because v is a String. + // 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). + Date( + str.to_std_string() + .ok() + .and_then(|s| { + chrono::DateTime::parse_from_rfc3339(s.as_str()).ok() + }) + .map(|dt| dt.naive_utc()), + ) + } + // iii. Else, + v => { + // Directly convert to integer + // 1. Let tv be ? ToNumber(v). + Date( + v.to_integer_or_nan(context)? + .as_integer() + // d. Let dv be TimeClip(tv). + .and_then(time_clip) + .and_then(NaiveDateTime::from_timestamp_millis), + ) + } + } + } + }, + // 5. Else, + _ => { + // Separating this into its own function to simplify the logic. + Date( + Self::construct_date(args, context)? + .and_then(|dt| Local.from_local_datetime(&dt).earliest()) + .map(|dt| dt.naive_utc()), + ) + } + }; - /// Converts the `Date` to a local `DateTime`. - /// - /// If the `Date` is invalid (i.e. NAN), this function will return `None`. - #[inline] - pub fn to_local(self) -> Option> { - self.0 - .map(|utc| Local::now().timezone().from_utc_datetime(&utc)) - } + // 6. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Date.prototype%", ยซ [[DateValue]] ยป). + let prototype = + get_prototype_from_constructor(new_target, StandardConstructors::date, context)?; - /// Converts the `Date` to a UTC `DateTime`. - /// - /// If the `Date` is invalid (i.e. NAN), this function will return `None`. - pub fn to_utc(self) -> Option> { - self.0 - .map(|utc| Utc::now().timezone().from_utc_datetime(&utc)) + // 7. Set O.[[DateValue]] to dv. + let obj = JsObject::from_proto_and_data(prototype, ObjectData::date(dv)); + + // 8. Return O. + Ok(obj.into()) } - /// Optionally sets the individual components of the `Date`. - /// - /// Each component does not have to be within the range of valid values. For example, if `month` is too large - /// then `year` will be incremented by the required amount. - #[allow(clippy::too_many_arguments)] - pub fn set_components( - &mut self, - utc: bool, - year: Option, - month: Option, - day: Option, - hour: Option, - minute: Option, - second: Option, - millisecond: Option, - ) { - #[inline] - fn num_days_in(year: i32, month: u32) -> Option { - let month = month + 1; // zero-based for calculations - - Some( - NaiveDate::from_ymd_opt( - match month { - 12 => year.checked_add(1)?, - _ => year, - }, - match month { - 12 => 1, - _ => month + 1, - }, - 1, - )? - .signed_duration_since(NaiveDate::from_ymd_opt(year, month, 1)?) - .num_days() as u32, - ) - } + /// Gets the timestamp from a list of component values. + fn construct_date( + values: &[JsValue], + context: &mut Context, + ) -> JsResult> { + // 1. Let y be ? ToNumber(year). + let Some(mut year) = values.get_or_undefined(0).to_integer_or_nan(context)?.as_integer() else { + return Ok(None); + }; - #[inline] - fn fix_month(year: i32, month: i32) -> Option<(i32, u32)> { - let year = year.checked_add(month / 12)?; - - if month < 0 { - let year = year.checked_sub(1)?; - let month = (11 + (month + 1) % 12) as u32; - Some((year, month)) - } else { - let month = (month % 12) as u32; - Some((year, month)) - } - } + // 2. If month is present, let m be ? ToNumber(month); else let m be +0๐”ฝ. + let Some(month) = values.get(1).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; - #[inline] - fn fix_day(mut year: i32, mut month: i32, mut day: i32) -> Option<(i32, u32, u32)> { - loop { - if day < 0 { - let (fixed_year, fixed_month) = fix_month(year, month.checked_sub(1)?)?; - - year = fixed_year; - month = fixed_month as i32; - day += num_days_in(fixed_year, fixed_month)? as i32; - } else { - let (fixed_year, fixed_month) = fix_month(year, month)?; - let num_days = num_days_in(fixed_year, fixed_month)? as i32; - - if day >= num_days { - day -= num_days; - month = month.checked_add(1)?; - } else { - break; - } - } - } + // 3. If date is present, let dt be ? ToNumber(date); else let dt be 1๐”ฝ. + let Some(date) = values.get(2).map_or(Ok(Some(1)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; - let (fixed_year, fixed_month) = fix_month(year, month)?; - Some((fixed_year, fixed_month, day as u32)) - } + // 4. If hours is present, let h be ? ToNumber(hours); else let h be +0๐”ฝ. + let Some(hour) = values.get(3).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; - // If any of the args are infinity or NaN, return an invalid date. - if !check_normal_opt!(year, month, day, hour, minute, second, millisecond) { - self.0 = None; - return; - } + // 5. If minutes is present, let min be ? ToNumber(minutes); else let min be +0๐”ฝ. + let Some(min) = values.get(4).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; - let naive = if utc { - self.to_utc().map(|dt| dt.naive_utc()) - } else { - self.to_local().map(|dt| dt.naive_local()) + // 6. If seconds is present, let s be ? ToNumber(seconds); else let s be +0๐”ฝ. + let Some(sec) = values.get(5).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); }; - self.0 = naive.and_then(|naive| { - let year = year.unwrap_or_else(|| f64::from(naive.year())) as i32; - let month = month.unwrap_or_else(|| f64::from(naive.month0())) as i32; - let day = (day.unwrap_or_else(|| f64::from(naive.day())) as i32).checked_sub(1)?; - let hour = hour.unwrap_or_else(|| f64::from(naive.hour())) as i64; - let minute = minute.unwrap_or_else(|| f64::from(naive.minute())) as i64; - let second = second.unwrap_or_else(|| f64::from(naive.second())) as i64; - let millisecond = millisecond - .unwrap_or_else(|| f64::from(naive.nanosecond()) / NANOS_PER_MS as f64) - as i64; - - let (year, month, day) = fix_day(year, month, day)?; - - let duration_hour = Duration::milliseconds(hour.checked_mul(MILLIS_PER_HOUR)?); - let duration_minute = Duration::milliseconds(minute.checked_mul(MILLIS_PER_MINUTE)?); - let duration_second = Duration::milliseconds(second.checked_mul(MILLIS_PER_SECOND)?); - let duration_millisecond = Duration::milliseconds(millisecond); - - let duration = duration_hour - .checked_add(&duration_minute)? - .checked_add(&duration_second)? - .checked_add(&duration_millisecond)?; - - NaiveDate::from_ymd_opt(year, month + 1, day + 1) - .and_then(|dt| dt.and_hms(0, 0, 0).checked_add_signed(duration)) - .and_then(|dt| { - if utc { - Some(Utc.from_utc_datetime(&dt).naive_utc()) - } else { - ignore_ambiguity(Local.from_local_datetime(&dt)).map(|dt| dt.naive_utc()) - } - }) - .filter(|dt| Self::time_clip(dt.timestamp_millis() as f64).is_some()) - }); - } + // 7. If ms is present, let milli be ? ToNumber(ms); else let milli be +0๐”ฝ. + let Some(ms) = values.get(6).map_or(Ok(Some(0)), |value| { + value + .to_integer_or_nan(context) + .map(IntegerOrNan::as_integer) + })? else { + return Ok(None); + }; - /// `Date()` - /// - /// Creates a JavaScript `Date` instance that represents a single moment in time in a platform-independent format. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date-constructor - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date - pub(crate) fn constructor( - new_target: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - if new_target.is_undefined() { - Ok(Self::make_date_string()) - } else { - let prototype = - get_prototype_from_constructor(new_target, StandardConstructors::date, context)?; - Ok(if args.is_empty() { - Self::make_date_now(prototype) - } else if args.len() == 1 { - Self::make_date_single(prototype, args, context)? - } else { - Self::make_date_multiple(prototype, args, context)? - } - .into()) + // 8. If y is NaN, let yr be NaN. + // 9. Else, + // a. Let yi be ! ToIntegerOrInfinity(y). + // b. If 0 โ‰ค yi โ‰ค 99, let yr be 1900๐”ฝ + ๐”ฝ(yi); otherwise, let yr be y. + if (0..=99).contains(&year) { + year += 1900; } - } - /// Utility: Returns `Date` object with current datetime - pub(crate) fn date_create(prototype: Option, context: &mut Context) -> JsObject { - let prototype = - prototype.unwrap_or_else(|| context.intrinsics().constructors().date().prototype()); + // 10. Return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))). + // PLEASE RUST TEAM GIVE US TRY BLOCKS ;-; + let timestamp = (move || { + let day = make_day(year, month, date)?; + let time = make_time(hour, min, sec, ms)?; + make_date(day, time) + })(); - Self::make_date_now(prototype) + Ok(timestamp + .and_then(time_clip) + .and_then(NaiveDateTime::from_timestamp_millis)) } - /// `Date()` + /// `Date.now()` /// - /// The `Date()` function is used to create a string that represent the current date and time. + /// The static `Date.now()` method returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date-constructor - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date - pub(crate) fn make_date_string() -> JsValue { - JsValue::new(Local::now().to_rfc3339()) + /// [spec]: https://tc39.es/ecma262/#sec-date.now + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now + #[allow(clippy::unnecessary_wraps)] + pub(crate) fn now(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + Ok(JsValue::new(Utc::now().timestamp_millis())) } - /// `Date()` + /// `Date.parse()` /// - /// The newly-created `Date` object represents the current date and time as of the time of instantiation. + /// The `Date.parse()` method parses a string representation of a date, and returns the number of milliseconds since + /// January 1, 1970, 00:00:00 UTC or `NaN` if the string is unrecognized or, in some cases, contains illegal date + /// values. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date-constructor - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date - pub(crate) fn make_date_now(prototype: JsObject) -> JsObject { - JsObject::from_proto_and_data(prototype, ObjectData::date(Self::default())) + /// [spec]: https://tc39.es/ecma262/#sec-date.parse + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse + pub(crate) fn parse(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + // This method is implementation-defined and discouraged, so we just require the same format as the string + // constructor. + + let date = some_or_nan!(args.get(0)); + + let date = date.to_string(context)?; + + Ok(date + .to_std_string() + .ok() + .and_then(|s| DateTime::parse_from_rfc3339(s.as_str()).ok()) + .and_then(|date| time_clip(date.naive_utc().timestamp_millis())) + .map_or_else(|| JsValue::from(f64::NAN), JsValue::from)) } - /// `Date(value)` + /// `Date.UTC()` /// - /// The newly-created `Date` object represents the value provided to the constructor. + /// The `Date.UTC()` method accepts parameters similar to the `Date` constructor, but treats them as UTC. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date-constructor - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date - pub(crate) fn make_date_single( - prototype: JsObject, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - let value = &args[0]; - let tv = match this_time_value(value) { - Ok(dt) => dt.0, - _ => match value.to_primitive(context, PreferredType::Default)? { - JsValue::String(ref str) => str - .to_std_string() - .ok() - .and_then(|s| chrono::DateTime::parse_from_rfc3339(s.as_str()).ok()) - .map(|dt| dt.naive_utc()), - tv => { - let tv = tv.to_number(context)?; - if tv.is_nan() { - None - } else { - let secs = (tv / 1_000f64) as i64; - let nano_secs = ((tv % 1_000f64) * 1_000_000f64) as u32; - NaiveDateTime::from_timestamp_opt(secs, nano_secs) - } - } - }, - }; + /// [spec]: https://tc39.es/ecma262/#sec-date.utc + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC + pub(crate) fn utc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + let t = some_or_nan!(Self::construct_date(args, context)?); - let tv = tv.filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some()); - Ok(JsObject::from_proto_and_data( - prototype, - ObjectData::date(Self(tv)), - )) + Ok(JsValue::from(t.timestamp_millis())) } - /// `Date(year, month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ])` + /// [`Date.prototype.getDate ( )`][local] and + /// [`Date.prototype.getUTCDate ( )`][utc]. /// - /// The newly-created `Date` object represents the date components provided to the constructor. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `getDate()` method returns the day of the month for the specified date. /// - /// [spec]: https://tc39.es/ecma262/#sec-date-constructor - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date - pub(crate) fn make_date_multiple( - prototype: JsObject, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - let mut year = args[0].to_number(context)?; - let month = args[1].to_number(context)?; - let day = args - .get(2) - .map_or(Ok(1f64), |value| value.to_number(context))?; - let hour = args - .get(3) - .map_or(Ok(0f64), |value| value.to_number(context))?; - let min = args - .get(4) - .map_or(Ok(0f64), |value| value.to_number(context))?; - let sec = args - .get(5) - .map_or(Ok(0f64), |value| value.to_number(context))?; - let milli = args - .get(6) - .map_or(Ok(0f64), |value| value.to_number(context))?; - - // If any of the args are infinity or NaN, return an invalid date. - if !check_normal_opt!(year, month, day, hour, min, sec, milli) { - return Ok(JsObject::from_proto_and_data( - prototype, - ObjectData::date(Self(None)), - )); - } - - if (0.0..=99.0).contains(&year) { - year += 1900.0; + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getdate + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcdate + pub(crate) fn get_date( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); } - let mut date = Self( - NaiveDateTime::from_timestamp_opt(0, 0) - .and_then(|local| ignore_ambiguity(Local.from_local_datetime(&local))) - .map(|local| local.naive_utc()) - .filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some()), - ); - - date.set_components( - false, - Some(year), - Some(month), - Some(day), - Some(hour), - Some(min), - Some(sec), - Some(milli), - ); - - Ok(JsObject::from_proto_and_data( - prototype, - ObjectData::date(date), - )) + // 3. Return DateFromTime(LocalTime(t)). + Ok(JsValue::new(t.day())) } - /// `Date.prototype[@@toPrimitive]` - /// - /// The [@@toPrimitive]() method converts a Date object to a primitive value. + /// [`Date.prototype.getDay ( )`][local] and + /// [`Date.prototype.getUTCDay ( )`][utc]. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `getDay()` method returns the day of the week for the specified date, where 0 represents + /// Sunday. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype-@@toprimitive - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/@@toPrimitive - #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_primitive( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getday + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcday + pub(crate) fn get_day( this: &JsValue, - args: &[JsValue], - context: &mut Context, + _args: &[JsValue], + _context: &mut Context, ) -> JsResult { - // 1. Let O be the this value. - // 2. If Type(O) is not Object, throw a TypeError exception. - let o = this.as_object().ok_or_else(|| { - JsNativeError::typ().with_message("Date.prototype[@@toPrimitive] called on non object") - })?; - - let hint = args.get_or_undefined(0); - - let try_first = match hint.as_string() { - // 3. If hint is "string" or "default", then - // a. Let tryFirst be string. - Some(string) if string == utf16!("string") || string == utf16!("default") => { - PreferredType::String - } - // 4. Else if hint is "number", then - // a. Let tryFirst be number. - Some(number) if number == utf16!("number") => PreferredType::Number, - // 5. Else, throw a TypeError exception. - _ => { - return Err(JsNativeError::typ() - .with_message("Date.prototype[@@toPrimitive] called with invalid hint") - .into()) - } - }; + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); + } - // 6. Return ? OrdinaryToPrimitive(O, tryFirst). - o.ordinary_to_primitive(context, try_first) + // 3. Return WeekDay(LocalTime(t)). + Ok(JsValue::new(t.weekday().num_days_from_sunday())) } - /// `Date.prototype.getDate()` + /// [`Date.prototype.getYear()`][spec]. /// - /// The `getDate()` method returns the day of the month for the specified date according to local time. + /// The `getYear()` method returns the year in the specified date according to local time. + /// Because `getYear()` does not return full years ("year 2000 problem"), it is no longer used + /// and has been replaced by the `getFullYear()` method. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getdate - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDate - pub fn get_date( + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getyear + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getYear + pub(crate) fn get_year( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - Ok(JsValue::new(local.day())) - } else { - Ok(JsValue::nan()) - } + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let t = some_or_nan!(this_time_value(this)?); + + // 3. Return YearFromTime(LocalTime(t)) - 1900๐”ฝ. + let local = Local.from_utc_datetime(&t); + Ok(JsValue::from(local.year() - 1900)) } - /// `Date.prototype.getDay()` + /// [`Date.prototype.getFullYear ( )`][local] and + /// [`Date.prototype.getUTCFullYear ( )`][utc]. /// - /// The `getDay()` method returns the day of the week for the specified date according to local time, where 0 - /// represents Sunday. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `getFullYear()` method returns the year of the specified date. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getday - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDay - pub fn get_day(this: &JsValue, _args: &[JsValue], _context: &mut Context) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - Ok(JsValue::new(local.weekday().num_days_from_sunday())) - } else { - Ok(JsValue::nan()) + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getfullyear + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcfullyear + pub(crate) fn get_full_year( + this: &JsValue, + _args: &[JsValue], + _context: &mut Context, + ) -> JsResult { + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); } + + // 3. Return YearFromTime(LocalTime(t)). + Ok(JsValue::new(t.year())) } - /// `Date.prototype.getFullYear()` + /// [`Date.prototype.getHours ( )`][local] and + /// [`Date.prototype.getUTCHours ( )`][utc]. /// - /// The `getFullYear()` method returns the year of the specified date according to local time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `getHours()` method returns the hour for the specified date. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getfullyear - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getFullYear - pub fn get_full_year( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.gethours + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutchours + pub(crate) fn get_hours( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - Ok(JsValue::new(local.year())) - } else { - Ok(JsValue::nan()) + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); } + + // 3. Return HourFromTime(LocalTime(t)). + Ok(JsValue::new(t.hour())) } - /// `Date.prototype.getHours()` + /// [`Date.prototype.getMilliseconds ( )`][local] and + /// [`Date.prototype.getUTCMilliseconds ( )`][utc]. /// - /// The `getHours()` method returns the hour for the specified date, according to local time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `getMilliseconds()` method returns the milliseconds in the specified date. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gethours - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getHours - pub fn get_hours( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getmilliseconds + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcmilliseconds + pub(crate) fn get_milliseconds( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - Ok(JsValue::new(local.hour())) - } else { - Ok(JsValue::nan()) + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); } + + // 3. Return msFromTime(LocalTime(t)). + Ok(JsValue::new(t.timestamp_subsec_millis())) } - /// `Date.prototype.getMilliseconds()` + /// [`Date.prototype.getMinutes ( )`][local] and + /// [`Date.prototype.getUTCMinutes ( )`][utc]. /// - /// The `getMilliseconds()` method returns the milliseconds in the specified date according to local time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `getMinutes()` method returns the minutes in the specified date. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getmilliseconds - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMilliseconds - pub fn get_milliseconds( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getminutes + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcminutes + pub(crate) fn get_minutes( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - Ok(JsValue::new(local.timestamp_subsec_millis())) - } else { - Ok(JsValue::nan()) + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); } + + // 3. Return MinFromTime(LocalTime(t)). + Ok(JsValue::new(t.minute())) } - /// `Date.prototype.getMinutes()` + /// [`Date.prototype.getMonth ( )`][local] and + /// [`Date.prototype.getUTCMonth ( )`][utc]. /// - /// The `getMinutes()` method returns the minutes in the specified date according to local time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `getMonth()` method returns the month in the specified date, as a zero-based value + /// (where zero indicates the first month of the year). /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getminutes - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMinutes - pub fn get_minutes( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getmonth + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcmonth + pub(crate) fn get_month( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - Ok(JsValue::new(local.minute())) - } else { - Ok(JsValue::nan()) + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); } + + // 3. Return MonthFromTime(LocalTime(t)). + Ok(JsValue::new(t.month0())) } - /// `Date.prototype.getMonth()` - /// - /// The `getMonth()` method returns the month in the specified date according to local time, as a zero-based value - /// (where zero indicates the first month of the year). + /// [`Date.prototype.getSeconds ( )`][local] and + /// [`Date.prototype.getUTCSeconds ( )`][utc]. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `getSeconds()` method returns the seconds in the specified date. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getmonth - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getMonth - pub fn get_month( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getseconds + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcseconds + pub(crate) fn get_seconds( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - Ok(JsValue::new(local.month0())) - } else { - Ok(JsValue::nan()) + // 1. Let t be ? thisTimeValue(this value). + // 2. If t is NaN, return NaN. + let mut t = some_or_nan!(this_time_value(this)?); + if LOCAL { + t = Local.from_utc_datetime(&t).naive_local(); } + + // 3. Return SecFromTime(LocalTime(t)) + Ok(JsValue::new(t.second())) } - /// `Date.prototype.getSeconds()` + /// `Date.prototype.getTime()`. /// - /// The `getSeconds()` method returns the seconds in the specified date according to local time. + /// The `getTime()` method returns the number of milliseconds since the Unix Epoch. /// /// More information: /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getseconds - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getSeconds - pub fn get_seconds( + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettime + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime + pub(crate) fn get_time( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - Ok(JsValue::new(local.second())) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.getYear()` - /// - /// The `getYear()` method returns the year in the specified date according to local time. - /// Because `getYear()` does not return full years ("year 2000 problem"), it is no longer used - /// and has been replaced by the `getFullYear()` method. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getyear - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getYear - pub fn get_year(this: &JsValue, _args: &[JsValue], context: &mut Context) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let local = Local::now().timezone().from_utc_datetime(&t); - let year = JsValue::Integer(local.year()); - year.sub(&JsValue::from(1900), context) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.getTime()` - /// - /// The `getTime()` method returns the number of milliseconds since the Unix Epoch. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettime - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime - pub fn get_time( - this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.timestamp_millis())) - } else { - Ok(JsValue::nan()) - } + // 1. Return ? thisTimeValue(this value). + let t = some_or_nan!(this_time_value(this)?); + Ok(JsValue::from(t.timestamp_millis())) } - /// Utility: Internal `get_time()` - fn int_get_time(&self) -> f64 { - self.to_utc() - .map_or(f64::NAN, |dt| dt.timestamp_millis() as f64) - } - - /// `Date.prototype.getTimeZoneOffset()` + /// `Date.prototype.getTimeZoneOffset()`. /// /// The `getTimezoneOffset()` method returns the time zone difference, in minutes, from current locale (host system /// settings) to UTC. @@ -776,997 +654,564 @@ impl Date { /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettimezoneoffset /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset #[inline] - pub fn get_timezone_offset( + pub(crate) fn get_timezone_offset( this: &JsValue, _: &[JsValue], _: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let t = this_time_value(this)?; - // 2. If t is NaN, return NaN. - if t.0.is_none() { - return Ok(JsValue::nan()); - } + some_or_nan!(this_time_value(this)?); // 3. Return (t - LocalTime(t)) / msPerMinute. - Ok(JsValue::new( - f64::from(-Local::now().offset().local_minus_utc()) / 60f64, - )) - } - - /// `Date.prototype.getUTCDate()` - /// - /// The `getUTCDate()` method returns the day (date) of the month in the specified date according to universal time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcdate - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDate - pub fn get_utc_date( - this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.day())) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.getUTCDay()` - /// - /// The `getUTCDay()` method returns the day of the week in the specified date according to universal time, where 0 - /// represents Sunday. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcday - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCDay - pub fn get_utc_day( - this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.weekday().num_days_from_sunday())) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.getUTCFullYear()` - /// - /// The `getUTCFullYear()` method returns the year in the specified date according to universal time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcfullyear - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCFullYear - pub fn get_utc_full_year( - this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.year())) - } else { - Ok(JsValue::nan()) - } + Ok(JsValue::from(-Local::now().offset().local_minus_utc() / 60)) } - /// `Date.prototype.getUTCHours()` - /// - /// The `getUTCHours()` method returns the hours in the specified date according to universal time. + /// [`Date.prototype.setDate ( date )`][local] and + /// [`Date.prototype.setUTCDate ( date )`][utc]. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `setDate()` method sets the day of the `Date` object relative to the beginning of the + /// currently set month. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutchours - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCHours - pub fn get_utc_hours( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setdate + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcdate + pub(crate) fn set_date( this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.hour())) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.getUTCMilliseconds()` - /// - /// The `getUTCMilliseconds()` method returns the milliseconds portion of the time object's value. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcmilliseconds - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMilliseconds - pub fn get_utc_milliseconds( - this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.timestamp_subsec_millis())) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.getUTCMinutes()` - /// - /// The `getUTCMinutes()` method returns the minutes in the specified date according to universal time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcminutes - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMinutes - pub fn get_utc_minutes( - this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.minute())) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.getUTCMonth()` - /// - /// The `getUTCMonth()` returns the month of the specified date according to universal time, as a zero-based value - /// (where zero indicates the first month of the year). - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcmonth - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCMonth - pub fn get_utc_month( - this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.month0())) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.getUTCSeconds()` - /// - /// The `getUTCSeconds()` method returns the seconds in the specified date according to universal time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getutcseconds - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getUTCSeconds - pub fn get_utc_seconds( - this: &JsValue, - _args: &[JsValue], - _context: &mut Context, + args: &[JsValue], + context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.second())) - } else { - Ok(JsValue::nan()) - } - } - - /// `Date.prototype.setDate()` - /// - /// The `setDate()` method sets the day of the `Date` object relative to the beginning of the currently set - /// month. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setdate - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setDate - pub fn set_date(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this)?; + get_mut_date!(let t = this); // 2. Let dt be ? ToNumber(date). - let dt = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 3. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)). - t.set_components(false, None, None, Some(dt), None, None, None, None); - - // 4. Let u be TimeClip(UTC(newDate)). - let u = t.int_get_time(); + let date = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 4. Set t to LocalTime(t). + // 5. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)). + // 6. Let u be TimeClip(UTC(newDate)). + let datetime = replace_params( + datetime, + DateParameters { + date: Some(date), + ..Default::default() + }, + LOCAL, + ); - // 5. Set the [[DateValue]] internal slot of this Date object to u. - this.set_data(ObjectData::date(t)); + // 7. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); - // 6. Return u. - Ok(u.into()) + // 8. Return u. + Ok(t.as_value()) } - /// `Date.prototype.setFullYear()` + /// [`Date.prototype.setFullYear ( year [ , month [ , date ] ] )`][local] and + /// [Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] )][utc]. /// - /// The `setFullYear()` method sets the full year for a specified date according to local time. Returns new + /// The `setFullYear()` method sets the full year for a specified date and returns the new /// timestamp. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setfullyear - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setFullYear - pub fn set_full_year( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setfullyear + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcfullyear + pub(crate) fn set_full_year( this: &JsValue, args: &[JsValue], context: &mut Context, ) -> JsResult { // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; + get_mut_date!(let t = this); // 2. If t is NaN, set t to +0๐”ฝ; otherwise, set t to LocalTime(t). - if t.0.is_none() { - t.0 = NaiveDateTime::from_timestamp_opt(0, 0) - .and_then(|local| ignore_ambiguity(Local.from_local_datetime(&local))) - .map(|local| local.naive_utc()) - .filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some()); - } + let datetime = match t.0 { + Some(dt) => dt, + None if LOCAL => { + let Some(datetime) = Local + .from_local_datetime(&NaiveDateTime::default()) + .earliest() + .as_ref() + .map(DateTime::naive_utc) else { + *t = Date(None); + return Ok(t.as_value()) + }; + datetime + } + None => NaiveDateTime::default(), + }; // 3. Let y be ? ToNumber(year). - let y = args.get_or_undefined(0).to_number(context)?; + let year = args.get_or_undefined(0).to_integer_or_nan(context)?; // 4. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month). - let m = args.get(1).map(|v| v.to_number(context)).transpose()?; + let month = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; // 5. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date). - let dt = args.get(2).map(|v| v.to_number(context)).transpose()?; + let date = args + .get(2) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; // 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)). - t.set_components(false, Some(y), m, dt, None, None, None, None); - // 7. Let u be TimeClip(UTC(newDate)). - let u = t.int_get_time(); - - // 8. Set the [[DateValue]] internal slot of this Date object to u. - this.set_data(ObjectData::date(t)); - - // 9. Return u. - Ok(u.into()) - } - - /// `Date.prototype.setHours()` - /// - /// The `setHours()` method sets the hours for a specified date according to local time, and returns the number - /// of milliseconds since January 1, 1970 00:00:00 UTC until the time represented by the updated `Date` - /// instance. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.sethours - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setHours - pub fn set_hours(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this)?; - - // 2. Let h be ? ToNumber(hour). - let h = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 3. If min is not present, let m be MinFromTime(t); otherwise, let m be ? ToNumber(min). - let m = args.get(1).map(|v| v.to_number(context)).transpose()?; - - // 4. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec). - let sec = args.get(2).map(|v| v.to_number(context)).transpose()?; - - // 5. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms). - let milli = args.get(3).map(|v| v.to_number(context)).transpose()?; - - // 6. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)). - t.set_components(false, None, None, None, Some(h), m, sec, milli); - - // 7. Let u be TimeClip(UTC(date)). - let u = t.int_get_time(); + let datetime = replace_params( + datetime, + DateParameters { + year: Some(year), + month, + date, + ..Default::default() + }, + LOCAL, + ); // 8. Set the [[DateValue]] internal slot of this Date object to u. - this.set_data(ObjectData::date(t)); + *t = Date(datetime); // 9. Return u. - Ok(u.into()) + Ok(t.as_value()) } - /// `Date.prototype.setMilliseconds()` - /// - /// The `setMilliseconds()` method sets the milliseconds for a specified date according to local time. + /// [`Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] )`][local] and + /// [`Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] )`][utc]. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `setHours()` method sets the hours for a specified date, and returns the number + /// of milliseconds since January 1, 1970 00:00:00 UTC until the time represented by the + /// updated `Date` instance. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMilliseconds - pub fn set_milliseconds( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.sethours + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutchours + pub(crate) fn set_hours( this: &JsValue, args: &[JsValue], context: &mut Context, ) -> JsResult { - // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this)?; + // 1. Let t be ? thisTimeValue(this value). + get_mut_date!(let t = this); - // 2. Set ms to ? ToNumber(ms). - let ms = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; + // 2. Let h be ? ToNumber(hour). + let hour = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If min is present, let m be ? ToNumber(min). + let minute = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; - // 3. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms). - t.set_components(false, None, None, None, None, None, None, Some(ms)); + // 4. If sec is present, let s be ? ToNumber(sec). + let second = args + .get(2) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; - // 4. Let u be TimeClip(UTC(MakeDate(Day(t), time))). - let u = t.int_get_time(); + // 5. If ms is present, let milli be ? ToNumber(ms). + let millisecond = args + .get(3) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 6. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 7. Set t to LocalTime(t). + // 8. If min is not present, let m be MinFromTime(t). + // 9. If sec is not present, let s be SecFromTime(t). + // 10. If ms is not present, let milli be msFromTime(t). + // 11. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)). + // 12. Let u be TimeClip(UTC(date)). + let datetime = replace_params( + datetime, + DateParameters { + hour: Some(hour), + minute, + second, + millisecond, + ..Default::default() + }, + LOCAL, + ); - // 5. Set the [[DateValue]] internal slot of this Date object to u. - this.set_data(ObjectData::date(t)); + // 13. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); - // 6. Return u. - Ok(u.into()) + // 14. Return u. + Ok(t.as_value()) } - /// `Date.prototype.setMinutes()` - /// - /// The `setMinutes()` method sets the minutes for a specified date according to local time. + /// [`Date.prototype.setMilliseconds ( ms )`[local] and + /// [`Date.prototype.setUTCMilliseconds ( ms )`][utc]. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `setMilliseconds()` method sets the milliseconds for a specified date according to local time. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setminutes - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMinutes - pub fn set_minutes( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcmilliseconds + pub(crate) fn set_milliseconds( this: &JsValue, args: &[JsValue], context: &mut Context, ) -> JsResult { + // 1. Let t be ?ย thisTimeValue(this value). // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this)?; - - // 2. Let m be ? ToNumber(min). - let m = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 3. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec). - let s = args.get(1).map(|v| v.to_number(context)).transpose()?; - - // 4. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms). - let milli = args.get(2).map(|v| v.to_number(context)).transpose()?; - - // 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)). - t.set_components(false, None, None, None, None, Some(m), s, milli); - - // 6. Let u be TimeClip(UTC(date)). - let u = t.int_get_time(); + get_mut_date!(let t = this); + + // 2. Set ms to ?ย ToNumber(ms). + let ms = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 4. Set t to LocalTime(t). + // 5. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms). + // 6. Let u be TimeClip(UTC(MakeDate(Day(t), time))). + let datetime = replace_params( + datetime, + DateParameters { + millisecond: Some(ms), + ..Default::default() + }, + LOCAL, + ); // 7. Set the [[DateValue]] internal slot of this Date object to u. - this.set_data(ObjectData::date(t)); + *t = Date(datetime); // 8. Return u. - Ok(u.into()) - } - - /// `Date.prototype.setMonth()` - /// - /// The `setMonth()` method sets the month for a specified date according to the currently set year. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setmonth - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth - pub fn set_month(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this)?; - - // 2. Let m be ? ToNumber(month). - let m = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 3. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date). - let dt = args.get(1).map(|v| v.to_number(context)).transpose()?; - - // 4. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)). - t.set_components(false, None, Some(m), dt, None, None, None, None); - - // 5. Let u be TimeClip(UTC(newDate)). - let u = t.int_get_time(); - - // 6. Set the [[DateValue]] internal slot of this Date object to u. - this.set_data(ObjectData::date(t)); - - // 7. Return u. - Ok(u.into()) + Ok(t.as_value()) } - /// `Date.prototype.setSeconds()` - /// - /// The `setSeconds()` method sets the seconds for a specified date according to local time. + /// [`Date.prototype.setMinutes ( min [ , sec [ , ms ] ] )`][local] and + /// [`Date.prototype.setUTCMinutes ( min [ , sec [ , ms ] ] )`][utc]. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `setMinutes()` method sets the minutes for a specified date. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setseconds - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setSeconds - pub fn set_seconds( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setminutes + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcminutes + pub(crate) fn set_minutes( this: &JsValue, args: &[JsValue], context: &mut Context, ) -> JsResult { - // 1. Let t be LocalTime(? thisTimeValue(this value)). - let mut t = this_time_value(this)?; + // 1. Let t be ?ย thisTimeValue(this value). + get_mut_date!(let t = this); - // 2. Let s be ? ToNumber(sec). - let s = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; + // 2. Let m be ?ย ToNumber(min). + let minute = args.get_or_undefined(0).to_integer_or_nan(context)?; - // 3. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms). - let milli = args.get(1).map(|v| v.to_number(context)).transpose()?; - - // 4. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)). - t.set_components(false, None, None, None, None, None, Some(s), milli); - - // 5. Let u be TimeClip(UTC(date)). - let u = t.int_get_time(); - - // 6. Set the [[DateValue]] internal slot of this Date object to u. - this.set_data(ObjectData::date(t)); - - // 7. Return u. - Ok(u.into()) - } - - /// `Date.prototype.setYear()` - /// - /// The `setYear()` method sets the year for a specified date according to local time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setyear - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setYear - pub fn set_year(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; - - // 2. If t is NaN, set t to +0๐”ฝ; otherwise, set t to LocalTime(t). - if t.0.is_none() { - t.0 = NaiveDateTime::from_timestamp_opt(0, 0) - .and_then(|local| ignore_ambiguity(Local.from_local_datetime(&local))) - .map(|local| local.naive_utc()) - .filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some()); - } - - // 3. Let y be ? ToNumber(year). - let mut y = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 4. If y is NaN, then - if y.is_nan() { - // a. Set the [[DateValue]] internal slot of this Date object to NaN. - this.set_data(ObjectData::date(Self(None))); - - // b. Return NaN. - return Ok(JsValue::nan()); - } - - // 5. Let yi be ! ToIntegerOrInfinity(y). - // 6. If 0 โ‰ค yi โ‰ค 99, let yyyy be 1900๐”ฝ + ๐”ฝ(yi). - // 7. Else, let yyyy be y. - if (0f64..=99f64).contains(&y) { - y += 1900f64; - } - - // 8. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)). - // 9. Let date be UTC(MakeDate(d, TimeWithinDay(t))). - t.set_components(false, Some(y), None, None, None, None, None, None); - - // 10. Set the [[DateValue]] internal slot of this Date object to TimeClip(date). - this.set_data(ObjectData::date(t)); - - // 11. Return the value of the [[DateValue]] internal slot of this Date object. - Ok(t.int_get_time().into()) - } - - /// `Date.prototype.setTime()` - /// - /// The `setTime()` method sets the Date object to the time represented by a number of milliseconds since - /// January 1, 1970, 00:00:00 UTC. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.settime - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime - pub fn set_time(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // 1. Perform ? thisTimeValue(this value). - this_time_value(this)?; - - // 2. Let t be ? ToNumber(time). - let t = args.get_or_undefined(0).to_number(context)?; - - // 3. Let v be TimeClip(t). - let v_int = Self::time_clip(t); - let v = v_int.map(|t| Local.timestamp_millis(t).naive_utc()); - - // 4. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(Date(v))); - - // 5. Return v. - Ok(v_int.map_or(JsValue::Rational(f64::NAN), JsValue::from)) - } - - /// `Date.prototype.setUTCDate()` - /// - /// The `setUTCDate()` method sets the day of the month for a specified date according to universal time. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcdate - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCDate - pub fn set_utc_date( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; - - // 2. Let dt be ? ToNumber(date). - let dt = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 3. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)). - t.set_components(true, None, None, Some(dt), None, None, None, None); - - // 4. Let v be TimeClip(newDate). - let v = Self::get_time(this, args, context)?; - - // 5. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(t)); - - // 6. Return v. - Ok(v) - } - - /// `Date.prototype.setFullYear()` - /// - /// The `setFullYear()` method sets the full year for a specified date according to local time. Returns new - /// timestamp. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcfullyear - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCFullYear - pub fn set_utc_full_year( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; - - // 2. If t is NaN, set t to +0๐”ฝ. - if t.0.is_none() { - t.0 = NaiveDateTime::from_timestamp_opt(0, 0) - .and_then(|local| ignore_ambiguity(Local.from_local_datetime(&local))) - .map(|local| local.naive_utc()) - .filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some()); - } - - // 3. Let y be ? ToNumber(year). - let y = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 4. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month). - let m = args.get(1).map(|v| v.to_number(context)).transpose()?; - - // 5. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date). - let dt = args.get(2).map(|v| v.to_number(context)).transpose()?; - - // 6. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)). - t.set_components(true, Some(y), m, dt, None, None, None, None); - - // 7. Let v be TimeClip(newDate). - let v = Self::get_time(this, args, context)?; - - // 8. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(t)); - - // 9. Return v. - Ok(v) - } - - /// `Date.prototype.setUTCHours()` - /// - /// The `setUTCHours()` method sets the hour for a specified date according to universal time, and returns the - /// number of milliseconds since January 1, 1970 00:00:00 UTC until the time represented by the updated `Date` - /// instance. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutchours - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCHours - pub fn set_utc_hours( - this: &JsValue, - args: &[JsValue], - context: &mut Context, - ) -> JsResult { - // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; - - // 2. Let h be ? ToNumber(hour). - let h = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 3. If min is not present, let m be MinFromTime(t); otherwise, let m be ? ToNumber(min). - let m = args.get(1).map(|v| v.to_number(context)).transpose()?; - - // 4. If sec is not present, let s be SecFromTime(t); otherwise, let s be ? ToNumber(sec). - let sec = args.get(2).map(|v| v.to_number(context)).transpose()?; - - // 5. If ms is not present, let milli be msFromTime(t); otherwise, let milli be ? ToNumber(ms). - let ms = args.get(3).map(|v| v.to_number(context)).transpose()?; - - // 6. Let newDate be MakeDate(Day(t), MakeTime(h, m, s, milli)). - t.set_components(true, None, None, None, Some(h), m, sec, ms); + // 3. If sec is present, let s be ?ย ToNumber(sec). + let second = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; - // 7. Let v be TimeClip(newDate). - let v = Self::get_time(this, args, context)?; + // 4. If ms is present, let milli be ?ย ToNumber(ms). + let millisecond = args + .get(2) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 5. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 6. Set t to LocalTime(t). + // 7. If sec is not present, let s be SecFromTime(t). + // 8. If ms is not present, let milli be msFromTime(t). + // 9. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)). + // 10. Let u be TimeClip(UTC(date)). + let datetime = replace_params( + datetime, + DateParameters { + minute: Some(minute), + second, + millisecond, + ..Default::default() + }, + LOCAL, + ); - // 8. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(t)); + // 11. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); - // 9. Return v. - Ok(v) + // 12. Return u. + Ok(t.as_value()) } - /// `Date.prototype.setUTCMilliseconds()` - /// - /// The `setUTCMilliseconds()` method sets the milliseconds for a specified date according to universal time. + /// [`Date.prototype.setMonth ( month [ , date ] )`][local] and + /// [`Date.prototype.setUTCMonth ( month [ , date ] )`][utc]. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `setMonth()` method sets the month for a specified date according to the currently set + /// year. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcmilliseconds - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMilliseconds - pub fn set_utc_milliseconds( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setmonth + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcmonth + pub(crate) fn set_month( this: &JsValue, args: &[JsValue], context: &mut Context, ) -> JsResult { - // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; - - // 2. Let milli be ? ToNumber(ms). - let ms = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; + // 1. Let t be ?ย thisTimeValue(this value). + get_mut_date!(let t = this); - // 3. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli). - t.set_components(true, None, None, None, None, None, None, Some(ms)); + // 2. Let m be ?ย ToNumber(month). + let month = args.get_or_undefined(0).to_integer_or_nan(context)?; - // 4. Let v be TimeClip(MakeDate(Day(t), time)). - let v = Self::get_time(this, args, context)?; + // 3. If date is present, let dt be ?ย ToNumber(date). + let date = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 4. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 5. Set t to LocalTime(t). + // 6. If date is not present, let dt be DateFromTime(t). + // 7. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)). + // 8. Let u be TimeClip(UTC(newDate)). + let datetime = replace_params( + datetime, + DateParameters { + month: Some(month), + date, + ..Default::default() + }, + LOCAL, + ); - // 5. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(t)); + // 9. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); - // 6. Return v. - Ok(v) + // 10. Return u. + Ok(t.as_value()) } - /// `Date.prototype.setUTCMinutes()` - /// - /// The `setUTCMinutes()` method sets the minutes for a specified date according to universal time. + /// [`Date.prototype.setSeconds ( sec [ , ms ] )`[local] and + /// [`Date.prototype.setUTCSeconds ( sec [ , ms ] )`][utc]. /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] + /// The `setSeconds()` method sets the seconds for a specified date. /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcminutes - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMinutes - pub fn set_utc_minutes( + /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setseconds + /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcseconds + pub(crate) fn set_seconds( this: &JsValue, args: &[JsValue], context: &mut Context, ) -> JsResult { - // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; - - // 2. Let m be ? ToNumber(min). - let m = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 3. If sec is not present, let s be SecFromTime(t). - // 4. Else, - // a. Let s be ? ToNumber(sec). - let s = args.get(1).map(|v| v.to_number(context)).transpose()?; + // 1. Let t be ?ย thisTimeValue(this value). + get_mut_date!(let t = this); - // 5. If ms is not present, let milli be msFromTime(t). - // 6. Else, - // a. Let milli be ? ToNumber(ms). - let milli = args.get(2).map(|v| v.to_number(context)).transpose()?; + // 2. Let s be ?ย ToNumber(sec). + let second = args.get_or_undefined(0).to_integer_or_nan(context)?; - // 7. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)). - t.set_components(true, None, None, None, None, Some(m), s, milli); - - // 8. Let v be TimeClip(date). - let v = Self::get_time(this, args, context)?; + // 3. If ms is present, let milli be ?ย ToNumber(ms). + let millisecond = args + .get(1) + .map(|v| v.to_integer_or_nan(context)) + .transpose()?; + + // 4. If t is NaN, return NaN. + let datetime = some_or_nan!(t.0); + + // 5. Set t to LocalTime(t). + // 6. If ms is not present, let milli be msFromTime(t). + // 7. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)). + // 8. Let u be TimeClip(UTC(date)). + let datetime = replace_params( + datetime, + DateParameters { + second: Some(second), + millisecond, + ..Default::default() + }, + LOCAL, + ); - // 9. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(t)); + // 9. Set the [[DateValue]] internal slot of this Date object to u. + *t = Date(datetime); - // 10. Return v. - Ok(v) + // 10. Return u. + Ok(t.as_value()) } - /// `Date.prototype.setUTCMonth()` + /// [`Date.prototype.setYear()`][spec]. + /// + /// The `setYear()` method sets the year for a specified date according to local time. /// - /// The `setUTCMonth()` method sets the month for a specified date according to universal time. + /// # Note + /// + /// The [`Self::set_full_year`] method is preferred for nearly all purposes, because it avoids + /// the โ€œyear 2000 problem.โ€ /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcmonth - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCMonth - pub fn set_utc_month( + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setYear + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setyear + pub(crate) fn set_year( this: &JsValue, args: &[JsValue], context: &mut Context, ) -> JsResult { - // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; + // 1. Let t be ?ย thisTimeValue(this value). + get_mut_date!(let t = this); + + // 2. Let y be ?ย ToNumber(year). + // 5. Let yi be !ย ToIntegerOrInfinity(y). + let year = args.get_or_undefined(0).to_integer_or_nan(context)?; + + // 3. If t is NaN, set t to +0๐”ฝ; otherwise, set t to LocalTime(t). + let Some(datetime) = t.0.or_else(|| { + Local + .from_local_datetime(&NaiveDateTime::default()) + .earliest() + .as_ref() + .map(DateTime::naive_utc) + }) else { + *t = Date(None); + return Ok(t.as_value()); + }; - // 2. Let m be ? ToNumber(month). - let m = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; + // 4. If y is NaN, then + let Some(mut year) = year.as_integer() else { + // a. Set the [[DateValue]] internal slot of this Date object to NaN. + *t = Date(None); - // 3. If date is not present, let dt be DateFromTime(t). - // 4. Else, - // a. Let dt be ? ToNumber(date). - let dt = args.get(1).map(|v| v.to_number(context)).transpose()?; + // b. Return NaN. + return Ok(t.as_value()); + }; - // 5. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)). - t.set_components(true, None, Some(m), dt, None, None, None, None); + // 6. If 0 โ‰ค yi โ‰ค 99, let yyyy be 1900๐”ฝ + ๐”ฝ(yi). + // 7. Else, let yyyy be y. + if (0..=99).contains(&year) { + year += 1900; + } - // 6. Let v be TimeClip(newDate). - let v = Self::get_time(this, args, context)?; + // 8. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)). + // 9. Let date be UTC(MakeDate(d, TimeWithinDay(t))). + let datetime = replace_params( + datetime, + DateParameters { + year: Some(IntegerOrNan::Integer(year)), + ..Default::default() + }, + true, + ); - // 7. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(t)); + // 10. Set the [[DateValue]] internal slot of this Date object to TimeClip(date). + *t = Date(datetime); - // 8. Return v. - Ok(v) + // 11. Return the value of the [[DateValue]] internal slot of this Date object. + Ok(t.as_value()) } - /// `Date.prototype.setUTCSeconds()` + /// [`Date.prototype.setTime()`][spec]. /// - /// The `setUTCSeconds()` method sets the seconds for a specified date according to universal time. + /// The `setTime()` method sets the Date object to the time represented by a number of milliseconds + /// since January 1, 1970, 00:00:00 UTC. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setutcseconds - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setUTCSeconds - pub fn set_utc_seconds( + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.settime + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime + pub(crate) fn set_time( this: &JsValue, args: &[JsValue], context: &mut Context, ) -> JsResult { - // 1. Let t be ? thisTimeValue(this value). - let mut t = this_time_value(this)?; - - // 2. Let s be ? ToNumber(sec). - let s = args - .get(0) - .cloned() - .unwrap_or_default() - .to_number(context)?; - - // 3. If ms is not present, let milli be msFromTime(t). - // 4. Else, - // a. Let milli be ? ToNumber(ms). - let milli = args.get(1).map(|v| v.to_number(context)).transpose()?; - - // 5. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)). - t.set_components(true, None, None, None, None, None, Some(s), milli); + // 1. Perform ? thisTimeValue(this value). + get_mut_date!(let t = this); - // 6. Let v be TimeClip(date). - let v = Self::get_time(this, args, context)?; + // 2. Let t be ? ToNumber(time). + // 3. Let v be TimeClip(t). + let timestamp = args + .get_or_undefined(0) + .to_integer_or_nan(context)? + .as_integer() + .and_then(time_clip) + .and_then(NaiveDateTime::from_timestamp_millis); - // 7. Set the [[DateValue]] internal slot of this Date object to v. - this.set_data(ObjectData::date(t)); + // 4. Set the [[DateValue]] internal slot of this Date object to v. + *t = Date(timestamp); - // 8. Return v. - Ok(v) + // 5. Return v. + Ok(t.as_value()) } - /// `Date.prototype.toDateString()` + /// [`Date.prototype.toDateString()`][spec]. /// /// The `toDateString()` method returns the date portion of a Date object in English. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.todatestring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toDateString - #[allow(clippy::wrong_self_convention)] - pub fn to_date_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + pub(crate) fn to_date_string( + this: &JsValue, + _: &[JsValue], + _: &mut Context, + ) -> JsResult { // 1. Let O be this Date object. // 2. Let tv be ? thisTimeValue(O). - let tv = this_time_value(this)?; + let Some(tv) = this_time_value(this)? else { + // 3. If tv is NaN, return "Invalid Date". + return Ok(js_string!("Invalid Date").into()); + }; - // 3. If tv is NaN, return "Invalid Date". // 4. Let t be LocalTime(tv). // 5. Return DateString(t). - if let Some(t) = tv.0 { - Ok(Local::now() - .timezone() - .from_utc_datetime(&t) - .format("%a %b %d %Y") - .to_string() - .into()) - } else { - Ok(js_string!("Invalid Date").into()) - } - } - - /// `Date.prototype.toLocaleDateString()` - /// - /// The `toLocaleDateString()` method returns the date portion of the given Date instance - /// according to language-specific conventions. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tolocaledatestring - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString - pub fn to_locale_date_string( - _this: &JsValue, - _args: &[JsValue], - _context: &mut Context, - ) -> JsResult { - Err(JsError::from_opaque(JsValue::new("Function Unimplemented"))) - } - - /// `Date.prototype.toGMTString()` - /// - /// The `toGMTString()` method converts a date to a string, using Internet Greenwich Mean Time (GMT) conventions. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.togmtstring - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toGMTString - pub fn to_gmt_string( - this: &JsValue, - _args: &[JsValue], - context: &mut Context, - ) -> JsResult { - Self::to_utc_string(this, &[], context) + Ok(Local::now() + .timezone() + .from_utc_datetime(&tv) + .format("%a %b %d %Y") + .to_string() + .into()) } - /// `Date.prototype.toISOString()` + /// [`Date.prototype.toISOString()`][spec]. /// - /// The `toISOString()` method returns a string in simplified extended ISO format (ISO 8601). + /// The `toISOString()` method returns a string in simplified extended ISO format + /// ([ISO 8601][iso8601]). /// /// More information: - /// - [ISO 8601][iso8601] - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [iso8601]: http://en.wikipedia.org/wiki/ISO_8601 /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toisostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString - #[allow(clippy::wrong_self_convention)] - pub fn to_iso_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(Utc::now() - .timezone() - .from_utc_datetime(&t) - .format("%Y-%m-%dT%H:%M:%S.%3fZ") - .to_string() - .into()) - } else { - Err(JsNativeError::range() - .with_message("Invalid time value") - .into()) - } + pub(crate) fn to_iso_string( + this: &JsValue, + _: &[JsValue], + _: &mut Context, + ) -> JsResult { + let t = this_time_value(this)? + .ok_or_else(|| JsNativeError::range().with_message("Invalid time value"))?; + Ok(Utc::now() + .timezone() + .from_utc_datetime(&t) + .format("%Y-%m-%dT%H:%M:%S.%3fZ") + .to_string() + .into()) } - /// `Date.prototype.toJSON()` + /// [`Date.prototype.toJSON()`][spec]. /// /// The `toJSON()` method returns a string representation of the `Date` object. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tojson /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON - #[allow(clippy::wrong_self_convention)] - pub fn to_json(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn to_json( + this: &JsValue, + _: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be ? ToObject(this value). let o = this.to_object(context)?; @@ -1785,45 +1230,34 @@ impl Date { context.call(&func, &o.into(), &[]) } - /// `Date.prototype.toString()` + /// [`Date.prototype.toLocaleDateString()`][spec]. /// - /// The `toString()` method returns a string representing the specified Date object. + /// The `toLocaleDateString()` method returns the date portion of the given Date instance according + /// to language-specific conventions. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tostring - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString - #[allow(clippy::wrong_self_convention)] - pub fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - // 1. Let tv be ? thisTimeValue(this value). - let tv = this_time_value(this)?; - - // 2. Return ToDateString(tv). - if let Some(t) = tv.0 { - Ok(Local::now() - .timezone() - .from_utc_datetime(&t) - .format("%a %b %d %Y %H:%M:%S GMT%z") - .to_string() - .into()) - } else { - Ok(js_string!("Invalid Date").into()) - } + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tolocaledatestring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString + pub(crate) fn to_locale_date_string( + _this: &JsValue, + _args: &[JsValue], + _context: &mut Context, + ) -> JsResult { + Err(JsError::from_opaque(JsValue::new("Function Unimplemented"))) } - /// `Date.prototype.toLocaleString()` + /// [`Date.prototype.toLocaleString()`][spec]. /// /// The `toLocaleString()` method returns a string representing the specified Date object. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tolocalestring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString - pub fn to_locale_string( + pub(crate) fn to_locale_string( _this: &JsValue, _: &[JsValue], _context: &mut Context, @@ -1833,50 +1267,17 @@ impl Date { ))) } - /// `Date.prototype.toTimeString()` - /// - /// The `toTimeString()` method returns the time portion of a Date object in human readable form in American - /// English. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.totimestring - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTimeString - #[allow(clippy::wrong_self_convention)] - pub fn to_time_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - // 1. Let O be this Date object. - // 2. Let tv be ? thisTimeValue(O). - let tv = this_time_value(this)?; - - // 3. If tv is NaN, return "Invalid Date". - // 4. Let t be LocalTime(tv). - // 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv). - if let Some(t) = tv.0 { - Ok(Local::now() - .timezone() - .from_utc_datetime(&t) - .format("%H:%M:%S GMT%z") - .to_string() - .into()) - } else { - Ok(js_string!("Invalid Date").into()) - } - } - - /// `Date.prototype.toLocaleTimeString()` + /// [`Date.prototype.toLocaleTimeString()`][spec]. /// - /// The `toLocaleTimeString()` method returns the time portion of a Date object in human readable form in American - /// English. + /// The `toLocaleTimeString()` method returns the time portion of a Date object in human readable + /// form in American English. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tolocaletimestring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString - pub fn to_locale_time_string( + pub(crate) fn to_locale_time_string( _this: &JsValue, _args: &[JsValue], _context: &mut Context, @@ -1886,185 +1287,180 @@ impl Date { ))) } - /// `Date.prototype.toUTCString()` + /// [`Date.prototype.toString()`][spec]. + /// + /// The `toString()` method returns a string representing the specified Date object. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tostring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString + pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + // 1. Let tv be ? thisTimeValue(this value). + // 2. Return ToDateString(tv). + let Some(t) = this_time_value(this)? else { + return Ok(js_string!("Invalid Date").into()); + }; + Ok(Local::now() + .timezone() + .from_utc_datetime(&t) + .format("%a %b %d %Y %H:%M:%S GMT%z") + .to_string() + .into()) + } + + /// [`Date.prototype.toTimeString()`][spec]. + /// + /// The `toTimeString()` method returns the time portion of a Date object in human readable form + /// in American English. + /// + /// More information: + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.totimestring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTimeString + pub(crate) fn to_time_string( + this: &JsValue, + _: &[JsValue], + _: &mut Context, + ) -> JsResult { + // 1. Let O be this Date object. + // 2. Let tv be ? thisTimeValue(O). + let Some(t) = this_time_value(this)? else { + // 3. If tv is NaN, return "Invalid Date". + return Ok(js_string!("Invalid Date").into()); + }; + + // 4. Let t be LocalTime(tv). + // 5. Return the string-concatenation of TimeString(t) and TimeZoneString(tv). + Ok(Local::now() + .timezone() + .from_utc_datetime(&t) + .format("%H:%M:%S GMT%z") + .to_string() + .into()) + } + + /// [`Date.prototype.toUTCString()`][spec]. /// /// The `toUTCString()` method returns a string representing the specified Date object. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toutcstring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toUTCString - pub fn to_utc_string( + pub(crate) fn to_utc_string( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - let utc_string = t.format("%a, %d %b %Y %H:%M:%S GMT").to_string(); - Ok(JsValue::new(utc_string)) - } else { - Ok(js_string!("Invalid Date").into()) - } + // 1. Let O be this Date object. + let Some(t) = this_time_value(this)? else { + // 3. If tv is NaN, return "Invalid Date". + return Ok(js_string!("Invalid Date").into()) + }; + + // 2. Let tv be ?ย thisTimeValue(O). + // 4. Let weekday be the Name of the entry in Table 60 with the Number WeekDay(tv). + // 5. Let month be the Name of the entry in Table 61 with the Number MonthFromTime(tv). + // 6. Let day be ToZeroPaddedDecimalString(โ„(DateFromTime(tv)), 2). + // 7. Let yv be YearFromTime(tv). + // 8. If yv is +0๐”ฝ or yv > +0๐”ฝ, let yearSign be the empty String; otherwise, let yearSign be "-". + // 9. Let paddedYear be ToZeroPaddedDecimalString(abs(โ„(yv)), 4). + // 10. Return the string-concatenation of weekday, ",", the code unit 0x0020 (SPACE), day, the + // code unit 0x0020 (SPACE), month, the code unit 0x0020 (SPACE), yearSign, paddedYear, the code + // unit 0x0020 (SPACE), and TimeString(tv) + let utc_string = t.format("%a, %d %b %Y %H:%M:%S GMT").to_string(); + Ok(JsValue::new(utc_string)) } - /// `Date.prototype.valueOf()` + /// [`Date.prototype.valueOf()`][spec]. /// /// The `valueOf()` method returns the primitive value of a `Date` object. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.valueof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/valueOf - pub fn value_of( + pub(crate) fn value_of( this: &JsValue, _args: &[JsValue], _context: &mut Context, ) -> JsResult { - if let Some(t) = this_time_value(this)?.0 { - Ok(JsValue::new(t.timestamp_millis())) - } else { - Ok(JsValue::nan()) - } + // 1. Return ?ย thisTimeValue(this value). + Ok(Date(this_time_value(this)?).as_value()) } - /// `Date.now()` + /// [`Date.prototype [ @@toPrimitive ] ( hint )`][spec]. /// - /// The static `Date.now()` method returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC. + /// The \[@@toPrimitive\]() method converts a Date object to a primitive value. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date.now - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now - #[allow(clippy::unnecessary_wraps)] - pub(crate) fn now(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Ok(JsValue::new(Utc::now().timestamp_millis() as f64)) - } + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype-@@toprimitive + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/@@toPrimitive + pub(crate) fn to_primitive( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { + // 1. Let O be the this value. + // 2. If Type(O) is not Object, throw a TypeError exception. + let o = this.as_object().ok_or_else(|| { + JsNativeError::typ().with_message("Date.prototype[@@toPrimitive] called on non object") + })?; - /// `Date.parse()` - /// - /// The `Date.parse()` method parses a string representation of a date, and returns the number of milliseconds since - /// January 1, 1970, 00:00:00 UTC or `NaN` if the string is unrecognized or, in some cases, contains illegal date - /// values. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#sec-date.parse - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse - pub(crate) fn parse(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - // This method is implementation-defined and discouraged, so we just require the same format as the string - // constructor. + let hint = args.get_or_undefined(0); - let Some(date) = args.get(0) else { - return Ok(JsValue::nan()); + let try_first = match hint.as_string() { + // 3. If hint is "string" or "default", then + // a. Let tryFirst be string. + Some(string) if string == utf16!("string") || string == utf16!("default") => { + PreferredType::String + } + // 4. Else if hint is "number", then + // a. Let tryFirst be number. + Some(number) if number == utf16!("number") => PreferredType::Number, + // 5. Else, throw a TypeError exception. + _ => { + return Err(JsNativeError::typ() + .with_message("Date.prototype[@@toPrimitive] called with invalid hint") + .into()) + } }; - let date = date.to_string(context)?; - - Ok(JsValue::new( - date.to_std_string() - .ok() - .and_then(|s| DateTime::parse_from_rfc3339(s.as_str()).ok()) - .map_or(f64::NAN, |v| v.naive_utc().timestamp_millis() as f64), - )) + // 6. Return ? OrdinaryToPrimitive(O, tryFirst). + o.ordinary_to_primitive(context, try_first) } - /// `Date.UTC()` + /// [`Date.prototype.toGMTString ( )`][spec]. /// - /// The `Date.UTC()` method accepts parameters similar to the `Date` constructor, but treats them as UTC. + /// The `toGMTString()` method converts a date to a string, using Internet Greenwich Mean Time + /// (GMT) conventions. /// /// More information: - /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-date.utc - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC - pub(crate) fn utc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { - let year = args - .get(0) - .map_or(Ok(f64::NAN), |value| value.to_number(context))?; - let month = args - .get(1) - .map_or(Ok(0f64), |value| value.to_number(context))?; - let day = args - .get(2) - .map_or(Ok(1f64), |value| value.to_number(context))?; - let hour = args - .get(3) - .map_or(Ok(0f64), |value| value.to_number(context))?; - let min = args - .get(4) - .map_or(Ok(0f64), |value| value.to_number(context))?; - let sec = args - .get(5) - .map_or(Ok(0f64), |value| value.to_number(context))?; - let milli = args - .get(6) - .map_or(Ok(0f64), |value| value.to_number(context))?; - - if !check_normal_opt!(year, month, day, hour, min, sec, milli) { - return Ok(JsValue::nan()); - } - - let year = year as i32; - let month = month as u32; - let day = day as u32; - let hour = hour as u32; - let min = min as u32; - let sec = sec as u32; - let milli = milli as u32; - - let year = if (0..=99).contains(&year) { - 1900 + year - } else { - year - }; - - NaiveDate::from_ymd_opt(year, month + 1, day) - .and_then(|f| f.and_hms_milli_opt(hour, min, sec, milli)) - .and_then(|f| Self::time_clip(f.timestamp_millis() as f64)) - .map_or(Ok(JsValue::nan()), |time| Ok(JsValue::new(time))) + /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.togmtstring + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toGMTString + pub(crate) fn to_gmt_string( + this: &JsValue, + _args: &[JsValue], + context: &mut Context, + ) -> JsResult { + // The initial value of the "toGMTString" property is %Date.prototype.toUTCString% + Self::to_utc_string(this, &[], context) } - /// Utility: Returns an `Object` representing `Date` from string in RFC3339 - pub(crate) fn create_obj(value: &JsValue, context: &mut Context) -> JsObject { - let prototype = context.intrinsics().constructors().date().prototype(); - let date_time = DateTime::parse_from_rfc3339(&value.to_string(context).expect( - "Utility: Date's string conversion used in limited(internal) area shouldn't fail", - ).to_std_string().expect("Utility: Conversion of confirmed JsString to std string")) - .expect("Utility: Parse RFC3339 is used in limited(internal) areas shouldn't fail"); - let internal_date = Date(Some(date_time.naive_local())); - JsObject::from_proto_and_data(prototype, ObjectData::date(internal_date)) + /// Converts the `Date` to a local `DateTime`. + /// + /// If the `Date` is invalid (i.e. NAN), this function will return `None`. + #[inline] + pub(crate) fn to_local(self) -> Option> { + self.0.map(|utc| Local.from_utc_datetime(&utc)) } } - -/// The abstract operation `thisTimeValue` takes argument value. -/// -/// In following descriptions of functions that are properties of the Date prototype object, the phrase โ€œthis -/// Date objectโ€ refers to the object that is the this value for the invocation of the function. If the `Type` of -/// the this value is not `Object`, a `TypeError` exception is thrown. The phrase โ€œthis time valueโ€ within the -/// specification of a method refers to the result returned by calling the abstract operation `thisTimeValue` with -/// the this value of the method invocation passed as the argument. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma262/#sec-thistimevalue -#[inline] -pub fn this_time_value(value: &JsValue) -> JsResult { - value - .as_object() - .and_then(|obj| obj.borrow().as_date().copied()) - .ok_or_else(|| { - JsNativeError::typ() - .with_message("'this' is not a Date") - .into() - }) -} diff --git a/boa_engine/src/builtins/date/tests.rs b/boa_engine/src/builtins/date/tests.rs index ec59ae5fca..b0050c7d69 100644 --- a/boa_engine/src/builtins/date/tests.rs +++ b/boa_engine/src/builtins/date/tests.rs @@ -1,30 +1,37 @@ -#![allow(clippy::zero_prefixed_literal)] - use crate::{forward, forward_val, Context, JsValue}; -use chrono::prelude::*; +use chrono::{prelude::*, Duration}; // NOTE: Javascript Uses 0-based months, where chrono uses 1-based months. Many of the assertions look wrong because of // this. -fn forward_dt_utc(context: &mut Context, src: &str) -> Option { +fn datetime_from_local( + year: i32, + month: u32, + date: u32, + hour: u32, + minute: u32, + second: u32, + millisecond: u32, +) -> NaiveDateTime { + Local + .from_local_datetime( + &NaiveDate::from_ymd_opt(year, month, date) + .unwrap() + .and_hms_milli_opt(hour, minute, second, millisecond) + .unwrap(), + ) + .earliest() + .unwrap() + .naive_utc() +} + +fn forward_dt(context: &mut Context, src: &str) -> Option { let date_time = forward_val(context, src).unwrap(); let date_time = date_time.as_object().unwrap(); let date_time = date_time.borrow(); date_time.as_date().unwrap().0 } -fn forward_dt_local(context: &mut Context, src: &str) -> Option { - let date_time = forward_dt_utc(context, src); - - // The timestamp is converted to UTC for internal representation - date_time.map(|utc| { - Local::now() - .timezone() - .from_utc_datetime(&utc) - .naive_local() - }) -} - #[test] fn date_display() { let dt = super::Date(None); @@ -53,28 +60,15 @@ fn date_this_time_value() { assert_eq!("\'this\' is not a Date", error.message()); } -#[test] -fn date_call() { - let mut context = Context::default(); - - let dt1 = forward(&mut context, "Date()"); - - std::thread::sleep(std::time::Duration::from_millis(1)); - - let dt2 = forward(&mut context, "Date()"); - - assert_ne!(dt1, dt2); -} - #[test] fn date_ctor_call() { let mut context = Context::default(); - let dt1 = forward_dt_local(&mut context, "new Date()"); + let dt1 = forward_dt(&mut context, "new Date()"); std::thread::sleep(std::time::Duration::from_millis(1)); - let dt2 = forward_dt_local(&mut context, "new Date()"); + let dt2 = forward_dt(&mut context, "new Date()"); assert_ne!(dt1, dt2); } @@ -83,11 +77,16 @@ fn date_ctor_call() { fn date_ctor_call_string() { let mut context = Context::default(); - let date_time = forward_dt_utc(&mut context, "new Date('2020-06-08T09:16:15.779-06:30')"); + let date_time = forward_dt(&mut context, "new Date('2020-06-08T09:16:15.779-06:30')"); // Internal date is expressed as UTC assert_eq!( - Some(NaiveDate::from_ymd(2020, 06, 08).and_hms_milli(15, 46, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 6, 8) + .unwrap() + .and_hms_milli_opt(15, 46, 15, 779) + .unwrap() + ), date_time ); } @@ -96,7 +95,7 @@ fn date_ctor_call_string() { fn date_ctor_call_string_invalid() { let mut context = Context::default(); - let date_time = forward_dt_local(&mut context, "new Date('nope')"); + let date_time = forward_dt(&mut context, "new Date('nope')"); assert_eq!(None, date_time); } @@ -104,9 +103,14 @@ fn date_ctor_call_string_invalid() { fn date_ctor_call_number() { let mut context = Context::default(); - let date_time = forward_dt_utc(&mut context, "new Date(1594199775779)"); + let date_time = forward_dt(&mut context, "new Date(1594199775779)"); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), date_time ); } @@ -115,10 +119,15 @@ fn date_ctor_call_number() { fn date_ctor_call_date() { let mut context = Context::default(); - let date_time = forward_dt_utc(&mut context, "new Date(new Date(1594199775779))"); + let date_time = forward_dt(&mut context, "new Date(new Date(1594199775779))"); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), date_time ); } @@ -127,10 +136,10 @@ fn date_ctor_call_date() { fn date_ctor_call_multiple() { let mut context = Context::default(); - let date_time = forward_dt_local(&mut context, "new Date(2020, 06, 08, 09, 16, 15, 779)"); + let date_time = forward_dt(&mut context, "new Date(2020, 6, 8, 9, 16, 15, 779)"); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2020, 7, 8, 9, 16, 15, 779)), date_time ); } @@ -139,10 +148,10 @@ fn date_ctor_call_multiple() { fn date_ctor_call_multiple_90s() { let mut context = Context::default(); - let date_time = forward_dt_local(&mut context, "new Date(99, 06, 08, 09, 16, 15, 779)"); + let date_time = forward_dt(&mut context, "new Date(99, 6, 8, 9, 16, 15, 779)"); assert_eq!( - Some(NaiveDate::from_ymd(1999, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(1999, 7, 8, 9, 16, 15, 779)), date_time ); } @@ -151,17 +160,17 @@ fn date_ctor_call_multiple_90s() { fn date_ctor_call_multiple_nan() { fn check(src: &str) { let mut context = Context::default(); - let date_time = forward_dt_local(&mut context, src); + let date_time = forward_dt(&mut context, src); assert_eq!(None, date_time); } - check("new Date(1/0, 06, 08, 09, 16, 15, 779)"); - check("new Date(2020, 1/0, 08, 09, 16, 15, 779)"); - check("new Date(2020, 06, 1/0, 09, 16, 15, 779)"); - check("new Date(2020, 06, 08, 1/0, 16, 15, 779)"); - check("new Date(2020, 06, 08, 09, 1/0, 15, 779)"); - check("new Date(2020, 06, 08, 09, 16, 1/0, 779)"); - check("new Date(2020, 06, 08, 09, 16, 15, 1/0)"); + check("new Date(1/0, 6, 8, 9, 16, 15, 779)"); + check("new Date(2020, 1/0, 8, 9, 16, 15, 779)"); + check("new Date(2020, 6, 1/0, 9, 16, 15, 779)"); + check("new Date(2020, 6, 8, 1/0, 16, 15, 779)"); + check("new Date(2020, 6, 8, 9, 1/0, 15, 779)"); + check("new Date(2020, 6, 8, 9, 16, 1/0, 779)"); + check("new Date(2020, 6, 8, 9, 16, 15, 1/0)"); } #[test] @@ -185,16 +194,16 @@ fn date_ctor_parse_call() { let date_time = forward_val(&mut context, "Date.parse('2020-06-08T09:16:15.779-07:30')"); - assert_eq!(JsValue::new(1591634775779f64), date_time.unwrap()); + assert_eq!(JsValue::new(1591634775779i64), date_time.unwrap()); } #[test] fn date_ctor_utc_call() { let mut context = Context::default(); - let date_time = forward_val(&mut context, "Date.UTC(2020, 06, 08, 09, 16, 15, 779)"); + let date_time = forward_val(&mut context, "Date.UTC(2020, 6, 8, 9, 16, 15, 779)"); - assert_eq!(JsValue::new(1594199775779f64), date_time.unwrap()); + assert_eq!(JsValue::new(1594199775779i64), date_time.unwrap()); } #[test] @@ -205,13 +214,13 @@ fn date_ctor_utc_call_nan() { assert_eq!(JsValue::nan(), date_time); } - check("Date.UTC(1/0, 06, 08, 09, 16, 15, 779)"); - check("Date.UTC(2020, 1/0, 08, 09, 16, 15, 779)"); - check("Date.UTC(2020, 06, 1/0, 09, 16, 15, 779)"); - check("Date.UTC(2020, 06, 08, 1/0, 16, 15, 779)"); - check("Date.UTC(2020, 06, 08, 09, 1/0, 15, 779)"); - check("Date.UTC(2020, 06, 08, 09, 16, 1/0, 779)"); - check("Date.UTC(2020, 06, 08, 09, 16, 15, 1/0)"); + check("Date.UTC(1/0, 6, 8, 9, 16, 15, 779)"); + check("Date.UTC(2020, 1/0, 8, 9, 16, 15, 779)"); + check("Date.UTC(2020, 6, 1/0, 9, 16, 15, 779)"); + check("Date.UTC(2020, 6, 8, 1/0, 16, 15, 779)"); + check("Date.UTC(2020, 6, 8, 9, 1/0, 15, 779)"); + check("Date.UTC(2020, 6, 8, 9, 16, 1/0, 779)"); + check("Date.UTC(2020, 6, 8, 9, 16, 15, 1/0)"); } #[test] @@ -220,9 +229,9 @@ fn date_proto_get_date_call() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getDate()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getDate()", ); - assert_eq!(JsValue::new(08f64), actual.unwrap()); + assert_eq!(JsValue::new(8), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getDate()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -234,9 +243,9 @@ fn date_proto_get_day_call() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getDay()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getDay()", ); - assert_eq!(JsValue::new(3f64), actual.unwrap()); + assert_eq!(JsValue::new(3), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getDay()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -248,9 +257,9 @@ fn date_proto_get_full_year_call() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getFullYear()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getFullYear()", ); - assert_eq!(JsValue::new(2020f64), actual.unwrap()); + assert_eq!(JsValue::new(2020), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getFullYear()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -262,9 +271,9 @@ fn date_proto_get_hours_call() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getHours()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getHours()", ); - assert_eq!(JsValue::new(09f64), actual.unwrap()); + assert_eq!(JsValue::new(9), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getHours()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -276,9 +285,9 @@ fn date_proto_get_milliseconds_call() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getMilliseconds()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getMilliseconds()", ); - assert_eq!(JsValue::new(779f64), actual.unwrap()); + assert_eq!(JsValue::new(779), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getMilliseconds()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -290,9 +299,9 @@ fn date_proto_get_minutes_call() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getMinutes()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getMinutes()", ); - assert_eq!(JsValue::new(16f64), actual.unwrap()); + assert_eq!(JsValue::new(16), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getMinutes()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -304,9 +313,9 @@ fn date_proto_get_month() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getMonth()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getMonth()", ); - assert_eq!(JsValue::new(06f64), actual.unwrap()); + assert_eq!(JsValue::new(6), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getMonth()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -318,9 +327,9 @@ fn date_proto_get_seconds() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getSeconds()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getSeconds()", ); - assert_eq!(JsValue::new(15f64), actual.unwrap()); + assert_eq!(JsValue::new(15), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getSeconds()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -332,14 +341,12 @@ fn date_proto_get_time() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getTime()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getTime()", ); - let ts = Local - .ymd(2020, 07, 08) - .and_hms_milli(09, 16, 15, 779) - .timestamp_millis() as f64; - assert_eq!(JsValue::new(ts), actual.unwrap()); + let ts = Local.with_ymd_and_hms(2020, 7, 8, 9, 16, 15).unwrap() + Duration::milliseconds(779); + + assert_eq!(JsValue::new(ts.timestamp_millis()), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getTime()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -351,9 +358,9 @@ fn date_proto_get_year() { let actual = forward_val( &mut context, - "new Date(2020, 06, 08, 09, 16, 15, 779).getYear()", + "new Date(2020, 6, 8, 9, 16, 15, 779).getYear()", ); - assert_eq!(JsValue::new(120f64), actual.unwrap()); + assert_eq!(JsValue::new(120), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getYear()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -377,8 +384,8 @@ fn date_proto_get_timezone_offset() { ); // The value of now().offset() depends on the host machine, so we have to replicate the method code here. - let offset_seconds = f64::from(chrono::Local::now().offset().local_minus_utc()); - let offset_minutes = -offset_seconds / 60f64; + let offset_seconds = chrono::Local::now().offset().local_minus_utc(); + let offset_minutes = -offset_seconds / 60; assert_eq!(JsValue::new(offset_minutes), actual.unwrap()); let actual = forward_val( @@ -394,9 +401,9 @@ fn date_proto_get_utc_date_call() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCDate()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCDate()", ); - assert_eq!(JsValue::new(08f64), actual.unwrap()); + assert_eq!(JsValue::new(8), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCDate()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -408,9 +415,9 @@ fn date_proto_get_utc_day_call() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCDay()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCDay()", ); - assert_eq!(JsValue::new(3f64), actual.unwrap()); + assert_eq!(JsValue::new(3), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCDay()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -422,9 +429,9 @@ fn date_proto_get_utc_full_year_call() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCFullYear()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCFullYear()", ); - assert_eq!(JsValue::new(2020f64), actual.unwrap()); + assert_eq!(JsValue::new(2020), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCFullYear()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -436,9 +443,9 @@ fn date_proto_get_utc_hours_call() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCHours()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCHours()", ); - assert_eq!(JsValue::new(09f64), actual.unwrap()); + assert_eq!(JsValue::new(9), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCHours()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -450,9 +457,9 @@ fn date_proto_get_utc_milliseconds_call() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCMilliseconds()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMilliseconds()", ); - assert_eq!(JsValue::new(779f64), actual.unwrap()); + assert_eq!(JsValue::new(779), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCMilliseconds()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -464,9 +471,9 @@ fn date_proto_get_utc_minutes_call() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCMinutes()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMinutes()", ); - assert_eq!(JsValue::new(16f64), actual.unwrap()); + assert_eq!(JsValue::new(16), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCMinutes()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -478,9 +485,9 @@ fn date_proto_get_utc_month() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCMonth()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMonth()", ); - assert_eq!(JsValue::new(06f64), actual.unwrap()); + assert_eq!(JsValue::new(6), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCMonth()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -492,9 +499,9 @@ fn date_proto_get_utc_seconds() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).getUTCSeconds()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCSeconds()", ); - assert_eq!(JsValue::new(15f64), actual.unwrap()); + assert_eq!(JsValue::new(15), actual.unwrap()); let actual = forward_val(&mut context, "new Date(1/0).getUTCSeconds()"); assert_eq!(JsValue::nan(), actual.unwrap()); @@ -504,28 +511,28 @@ fn date_proto_get_utc_seconds() { fn date_proto_set_date() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setDate(21); dt", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setDate(21); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 21).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2020, 7, 21, 9, 16, 15, 779)), actual ); // Date wraps to previous month for 0. - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setDate(0); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setDate(0); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 06, 30).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2020, 6, 30, 9, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setDate(1/0); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setDate(1/0); dt", ); assert_eq!(None, actual); } @@ -534,68 +541,68 @@ fn date_proto_set_date() { fn date_proto_set_full_year() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setFullYear(2012); dt", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setFullYear(2012); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2012, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2012, 7, 8, 9, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setFullYear(2012, 8); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setFullYear(2012, 8); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2012, 09, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2012, 9, 8, 9, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setFullYear(2012, 8, 10); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setFullYear(2012, 8, 10); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2012, 09, 10).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2012, 9, 10, 9, 16, 15, 779)), actual ); // Out-of-bounds - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 07, 08, 09, 16, 15, 779); dt.setFullYear(2012, 35); dt", + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setFullYear(2012, 35); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2014, 12, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2014, 12, 8, 9, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 07, 08, 09, 16, 15, 779); dt.setFullYear(2012, -35); dt", + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setFullYear(2012, -35); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2009, 02, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2009, 2, 8, 9, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 07, 08, 09, 16, 15, 779); dt.setFullYear(2012, 9, 950); dt", + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setFullYear(2012, 9, 950); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2015, 05, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2015, 5, 8, 9, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 07, 08, 09, 16, 15, 779); dt.setFullYear(2012, 9, -950); dt", + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setFullYear(2012, 9, -950); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2010, 02, 23).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2010, 2, 23, 9, 16, 15, 779)), actual ); } @@ -604,50 +611,50 @@ fn date_proto_set_full_year() { fn date_proto_set_hours() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setHours(11); dt", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(11); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(11, 16, 15, 779)), + Some(datetime_from_local(2020, 7, 8, 11, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setHours(11, 35); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(11, 35); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(11, 35, 15, 779)), + Some(datetime_from_local(2020, 7, 8, 11, 35, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setHours(11, 35, 23); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(11, 35, 23); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(11, 35, 23, 779)), + Some(datetime_from_local(2020, 7, 8, 11, 35, 23, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setHours(11, 35, 23, 537); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(11, 35, 23, 537); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(11, 35, 23, 537)), + Some(datetime_from_local(2020, 7, 8, 11, 35, 23, 537)), actual ); // Out-of-bounds - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setHours(10000, 20000, 30000, 40123); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setHours(10000, 20000, 30000, 40123); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2021, 09, 11).and_hms_milli(21, 40, 40, 123)), + Some(datetime_from_local(2021, 9, 11, 21, 40, 40, 123)), actual ); } @@ -656,24 +663,24 @@ fn date_proto_set_hours() { fn date_proto_set_milliseconds() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setMilliseconds(597); dt", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMilliseconds(597); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 15, 597)), + Some(datetime_from_local(2020, 7, 8, 9, 16, 15, 597)), actual ); // Out-of-bounds // Thorough tests are done by setHours - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setMilliseconds(40123); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMilliseconds(40123); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 55, 123)), + Some(datetime_from_local(2020, 7, 8, 9, 16, 55, 123)), actual ); } @@ -682,42 +689,42 @@ fn date_proto_set_milliseconds() { fn date_proto_set_minutes() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setMinutes(11); dt", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMinutes(11); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 11, 15, 779)), + Some(datetime_from_local(2020, 7, 8, 9, 11, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setMinutes(11, 35); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMinutes(11, 35); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 11, 35, 779)), + Some(datetime_from_local(2020, 7, 8, 9, 11, 35, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setMinutes(11, 35, 537); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMinutes(11, 35, 537); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 11, 35, 537)), + Some(datetime_from_local(2020, 7, 8, 9, 11, 35, 537)), actual ); // Out-of-bounds // Thorough tests are done by setHours - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setMinutes(600000, 30000, 40123); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMinutes(600000, 30000, 40123); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2021, 08, 29).and_hms_milli(09, 20, 40, 123)), + Some(datetime_from_local(2021, 8, 29, 9, 20, 40, 123)), actual ); } @@ -726,33 +733,33 @@ fn date_proto_set_minutes() { fn date_proto_set_month() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setMonth(11); dt", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMonth(11); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 12, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2020, 12, 8, 9, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setMonth(11, 16); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setMonth(11, 16); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 12, 16).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2020, 12, 16, 9, 16, 15, 779)), actual ); // Out-of-bounds // Thorough tests are done by setFullYear - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 07, 08, 09, 16, 15, 779); dt.setMonth(40, 83); dt", + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setMonth(40, 83); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2023, 07, 22).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2023, 7, 22, 9, 16, 15, 779)), actual ); } @@ -761,33 +768,33 @@ fn date_proto_set_month() { fn date_proto_set_seconds() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setSeconds(11); dt", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setSeconds(11); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 11, 779)), + Some(datetime_from_local(2020, 7, 8, 9, 16, 11, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setSeconds(11, 487); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setSeconds(11, 487); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 11, 487)), + Some(datetime_from_local(2020, 7, 8, 9, 16, 11, 487)), actual ); // Out-of-bounds // Thorough tests are done by setHour - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 07, 08, 09, 16, 15, 779); dt.setSeconds(40000000, 40123); dt", + "dt = new Date(2020, 7, 8, 9, 16, 15, 779); dt.setSeconds(40000000, 40123); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2021, 11, 14).and_hms_milli(08, 23, 20, 123)), + Some(datetime_from_local(2021, 11, 14, 8, 23, 20, 123)), actual ); } @@ -796,21 +803,21 @@ fn date_proto_set_seconds() { fn set_year() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setYear(98); dt", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setYear(98); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(1998, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(1998, 7, 8, 9, 16, 15, 779)), actual ); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.setYear(2001); dt", + "dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.setYear(2001); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2001, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2001, 7, 8, 9, 16, 15, 779)), actual ); } @@ -819,12 +826,12 @@ fn set_year() { fn date_proto_set_time() { let mut context = Context::default(); - let actual = forward_dt_local( + let actual = forward_dt( &mut context, - "let dt = new Date(); dt.setTime(new Date(2020, 06, 08, 09, 16, 15, 779).getTime()); dt", + "let dt = new Date(); dt.setTime(new Date(2020, 6, 8, 9, 16, 15, 779).getTime()); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some(datetime_from_local(2020, 7, 8, 9, 16, 15, 779)), actual ); } @@ -833,28 +840,38 @@ fn date_proto_set_time() { fn date_proto_set_utc_date() { let mut context = Context::default(); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCDate(21); dt", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCDate(21); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 21).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 21) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); // Date wraps to previous month for 0. - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCDate(0); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCDate(0); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 06, 30).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 6, 30) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCDate(1/0); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCDate(1/0); dt", ); assert_eq!(None, actual); } @@ -863,68 +880,103 @@ fn date_proto_set_utc_date() { fn date_proto_set_utc_full_year() { let mut context = Context::default(); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCFullYear(2012); dt", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2012, 07, 08).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2012, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCFullYear(2012, 8); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 8); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2012, 09, 08).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2012, 9, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCFullYear(2012, 8, 10); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 8, 10); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2012, 09, 10).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2012, 9, 10) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); // Out-of-bounds - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 07, 08, 09, 16, 15, 779)); dt.setUTCFullYear(2012, 35); dt", + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 35); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2014, 12, 08).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2014, 12, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 07, 08, 09, 16, 15, 779)); dt.setUTCFullYear(2012, -35); dt", + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, -35); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2009, 02, 08).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2009, 2, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 07, 08, 09, 16, 15, 779)); dt.setUTCFullYear(2012, 9, 950); dt", + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 9, 950); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2015, 05, 08).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2015, 5, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 07, 08, 09, 16, 15, 779)); dt.setUTCFullYear(2012, 9, -950); dt", + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCFullYear(2012, 9, -950); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2010, 02, 23).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2010, 2, 23) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); } @@ -933,50 +985,75 @@ fn date_proto_set_utc_full_year() { fn date_proto_set_utc_hours() { let mut context = Context::default(); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCHours(11); dt", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(11); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(11, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(11, 16, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCHours(11, 35); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(11, 35); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(11, 35, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(11, 35, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCHours(11, 35, 23); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(11, 35, 23); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(11, 35, 23, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(11, 35, 23, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCHours(11, 35, 23, 537); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(11, 35, 23, 537); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(11, 35, 23, 537)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(11, 35, 23, 537) + .unwrap() + ), actual ); // Out-of-bounds - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCHours(10000, 20000, 30000, 40123); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCHours(10000, 20000, 30000, 40123); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2021, 09, 11).and_hms_milli(21, 40, 40, 123)), + Some( + NaiveDate::from_ymd_opt(2021, 9, 11) + .unwrap() + .and_hms_milli_opt(21, 40, 40, 123) + .unwrap() + ), actual ); } @@ -985,24 +1062,34 @@ fn date_proto_set_utc_hours() { fn date_proto_set_utc_milliseconds() { let mut context = Context::default(); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCMilliseconds(597); dt", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMilliseconds(597); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 15, 597)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 597) + .unwrap() + ), actual ); // Out-of-bounds // Thorough tests are done by setHours - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCMilliseconds(40123); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMilliseconds(40123); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 55, 123)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 55, 123) + .unwrap() + ), actual ); } @@ -1011,42 +1098,62 @@ fn date_proto_set_utc_milliseconds() { fn date_proto_set_utc_minutes() { let mut context = Context::default(); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCMinutes(11); dt", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMinutes(11); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 11, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 11, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCMinutes(11, 35); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMinutes(11, 35); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 11, 35, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 11, 35, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCMinutes(11, 35, 537); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMinutes(11, 35, 537); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 11, 35, 537)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 11, 35, 537) + .unwrap() + ), actual ); // Out-of-bounds // Thorough tests are done by setHours - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCMinutes(600000, 30000, 40123); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMinutes(600000, 30000, 40123); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2021, 08, 29).and_hms_milli(09, 20, 40, 123)), + Some( + NaiveDate::from_ymd_opt(2021, 8, 29) + .unwrap() + .and_hms_milli_opt(9, 20, 40, 123) + .unwrap() + ), actual ); } @@ -1055,33 +1162,48 @@ fn date_proto_set_utc_minutes() { fn date_proto_set_utc_month() { let mut context = Context::default(); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCMonth(11); dt", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMonth(11); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 12, 08).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 12, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCMonth(11, 16); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCMonth(11, 16); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 12, 16).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 12, 16) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); // Out-of-bounds // Thorough tests are done by setFullYear - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 07, 08, 09, 16, 15, 779)); dt.setUTCMonth(40, 83); dt", + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCMonth(40, 83); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2023, 07, 22).and_hms_milli(09, 16, 15, 779)), + Some( + NaiveDate::from_ymd_opt(2023, 7, 22) + .unwrap() + .and_hms_milli_opt(9, 16, 15, 779) + .unwrap() + ), actual ); } @@ -1090,33 +1212,48 @@ fn date_proto_set_utc_month() { fn date_proto_set_utc_seconds() { let mut context = Context::default(); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCSeconds(11); dt", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCSeconds(11); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 11, 779)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 11, 779) + .unwrap() + ), actual ); - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.setUTCSeconds(11, 487); dt", + "dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.setUTCSeconds(11, 487); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2020, 07, 08).and_hms_milli(09, 16, 11, 487)), + Some( + NaiveDate::from_ymd_opt(2020, 7, 8) + .unwrap() + .and_hms_milli_opt(9, 16, 11, 487) + .unwrap() + ), actual ); // Out-of-bounds // Thorough tests are done by setHour - let actual = forward_dt_utc( + let actual = forward_dt( &mut context, - "dt = new Date(Date.UTC(2020, 07, 08, 09, 16, 15, 779)); dt.setUTCSeconds(40000000, 40123); dt", + "dt = new Date(Date.UTC(2020, 7, 8, 9, 16, 15, 779)); dt.setUTCSeconds(40000000, 40123); dt", ); assert_eq!( - Some(NaiveDate::from_ymd(2021, 11, 14).and_hms_milli(08, 23, 20, 123)), + Some( + NaiveDate::from_ymd_opt(2021, 11, 14) + .unwrap() + .and_hms_milli_opt(8, 23, 20, 123) + .unwrap() + ), actual ); } @@ -1127,7 +1264,7 @@ fn date_proto_to_date_string() { let actual = forward_val( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.toDateString()", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.toDateString()", ) .unwrap(); assert_eq!(JsValue::new("Wed Jul 08 2020"), actual); @@ -1139,7 +1276,7 @@ fn date_proto_to_gmt_string() { let actual = forward_val( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.toGMTString()", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.toGMTString()", ) .unwrap(); assert_eq!(JsValue::new("Wed, 08 Jul 2020 09:16:15 GMT"), actual); @@ -1151,7 +1288,7 @@ fn date_proto_to_iso_string() { let actual = forward_val( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.toISOString()", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.toISOString()", ) .unwrap(); assert_eq!(JsValue::new("2020-07-08T09:16:15.779Z"), actual); @@ -1163,7 +1300,7 @@ fn date_proto_to_json() { let actual = forward_val( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.toJSON()", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.toJSON()", ) .unwrap(); assert_eq!(JsValue::new("2020-07-08T09:16:15.779Z"), actual); @@ -1175,7 +1312,7 @@ fn date_proto_to_string() { let actual = forward_val( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.toString()", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.toString()", ) .ok(); @@ -1183,8 +1320,8 @@ fn date_proto_to_string() { Some(JsValue::new( Local .from_local_datetime(&NaiveDateTime::new( - NaiveDate::from_ymd(2020, 7, 8), - NaiveTime::from_hms_milli(9, 16, 15, 779) + NaiveDate::from_ymd_opt(2020, 7, 8).unwrap(), + NaiveTime::from_hms_milli_opt(9, 16, 15, 779).unwrap() )) .earliest() .unwrap() @@ -1201,7 +1338,7 @@ fn date_proto_to_time_string() { let actual = forward_val( &mut context, - "let dt = new Date(2020, 06, 08, 09, 16, 15, 779); dt.toTimeString()", + "let dt = new Date(2020, 6, 8, 9, 16, 15, 779); dt.toTimeString()", ) .ok(); @@ -1209,8 +1346,8 @@ fn date_proto_to_time_string() { Some(JsValue::new( Local .from_local_datetime(&NaiveDateTime::new( - NaiveDate::from_ymd(2020, 7, 8), - NaiveTime::from_hms_milli(9, 16, 15, 779) + NaiveDate::from_ymd_opt(2020, 7, 8).unwrap(), + NaiveTime::from_hms_milli_opt(9, 16, 15, 779).unwrap() )) .earliest() .unwrap() @@ -1227,7 +1364,7 @@ fn date_proto_to_utc_string() { let actual = forward_val( &mut context, - "let dt = new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)); dt.toUTCString()", + "let dt = new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)); dt.toUTCString()", ) .unwrap(); assert_eq!(JsValue::new("Wed, 08 Jul 2020 09:16:15 GMT"), actual); @@ -1239,10 +1376,10 @@ fn date_proto_value_of() { let actual = forward_val( &mut context, - "new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)).valueOf()", + "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).valueOf()", ) .unwrap(); - assert_eq!(JsValue::new(1594199775779f64), actual); + assert_eq!(JsValue::new(1594199775779i64), actual); } #[test] @@ -1251,10 +1388,10 @@ fn date_neg() { let actual = forward_val( &mut context, - "-new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779))", + "-new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779))", ) .unwrap(); - assert_eq!(JsValue::new(-1594199775779f64), actual); + assert_eq!(JsValue::new(-1594199775779i64), actual); } #[test] @@ -1263,7 +1400,7 @@ fn date_json() { let actual = forward_val( &mut context, - "JSON.stringify({ date: new Date(Date.UTC(2020, 06, 08, 09, 16, 15, 779)) })", + "JSON.stringify({ date: new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)) })", ) .unwrap(); assert_eq!( diff --git a/boa_engine/src/builtins/date/utils.rs b/boa_engine/src/builtins/date/utils.rs new file mode 100644 index 0000000000..8c1ae9a174 --- /dev/null +++ b/boa_engine/src/builtins/date/utils.rs @@ -0,0 +1,200 @@ +use chrono::{Datelike, Local, NaiveDateTime, TimeZone, Timelike}; + +use crate::value::IntegerOrNan; + +/// The absolute maximum value of a timestamp +pub(super) const MAX_TIMESTAMP: i64 = 864 * 10i64.pow(13); +/// The number of milliseconds in a second. +pub(super) const MILLIS_PER_SECOND: i64 = 1000; +/// The number of milliseconds in a minute. +pub(super) const MILLIS_PER_MINUTE: i64 = MILLIS_PER_SECOND * 60; +/// The number of milliseconds in an hour. +pub(super) const MILLIS_PER_HOUR: i64 = MILLIS_PER_MINUTE * 60; +/// The number of milliseconds in a day. +pub(super) const MILLIS_PER_DAY: i64 = MILLIS_PER_HOUR * 24; + +// https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-time-values-and-time-range +// +// The smaller range supported by a time value as specified in this section is approximately -273,790 to 273,790 +// years relative to 1970. +pub(super) const MIN_YEAR: i64 = -300_000; +pub(super) const MAX_YEAR: i64 = -MIN_YEAR; +pub(super) const MIN_MONTH: i64 = MIN_YEAR * 12; +pub(super) const MAX_MONTH: i64 = MAX_YEAR * 12; + +/// Calculates the absolute day number from the year number. +pub(super) const fn day_from_year(year: i64) -> i64 { + // Taken from https://chromium.googlesource.com/v8/v8/+/refs/heads/main/src/date/date.cc#496 + // Useful to avoid negative divisions and overflows on 32-bit platforms (if we plan to support them). + const YEAR_DELTA: i64 = 399_999; + const fn day(year: i64) -> i64 { + let year = year + YEAR_DELTA; + 365 * year + year / 4 - year / 100 + year / 400 + } + + assert!(MIN_YEAR <= year && year <= MAX_YEAR); + day(year) - day(1970) +} + +/// Abstract operation [`MakeTime`][spec]. +/// +/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-maketime +pub(super) fn make_time(hour: i64, min: i64, sec: i64, ms: i64) -> Option { + // 1. If hour is not finite or min is not finite or sec is not finite or ms is not finite, return NaN. + // 2. Let h be ๐”ฝ(! ToIntegerOrInfinity(hour)). + // 3. Let m be ๐”ฝ(! ToIntegerOrInfinity(min)). + // 4. Let s be ๐”ฝ(! ToIntegerOrInfinity(sec)). + // 5. Let milli be ๐”ฝ(! ToIntegerOrInfinity(ms)). + + // 6. Let t be ((h * msPerHour + m * msPerMinute) + s * msPerSecond) + milli, performing the arithmetic according to IEEE 754-2019 rules (that is, as if using the ECMAScript operators * and +). + // 7. Return t. + + let h_ms = hour.checked_mul(MILLIS_PER_HOUR)?; + let m_ms = min.checked_mul(MILLIS_PER_MINUTE)?; + let s_ms = sec.checked_mul(MILLIS_PER_SECOND)?; + + h_ms.checked_add(m_ms)?.checked_add(s_ms)?.checked_add(ms) +} + +/// Abstract operation [`MakeDay`][spec]. +/// +/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makeday +pub(super) fn make_day(mut year: i64, mut month: i64, date: i64) -> Option { + // 1. If year is not finite or month is not finite or date is not finite, return NaN. + // 2. Let y be ๐”ฝ(! ToIntegerOrInfinity(year)). + // 3. Let m be ๐”ฝ(! ToIntegerOrInfinity(month)). + // 4. Let dt be ๐”ฝ(! ToIntegerOrInfinity(date)). + if !(MIN_YEAR..=MAX_YEAR).contains(&year) || !(MIN_MONTH..=MAX_MONTH).contains(&month) { + return None; + } + + // At this point, we've already asserted that year and month are much less than its theoretical + // maximum and minimum values (i64::MAX/MIN), so we don't need to do checked operations. + + // 5. Let ym be y + ๐”ฝ(floor(โ„(m) / 12)). + // 6. If ym is not finite, return NaN. + year += month / 12; + // 7. Let mn be ๐”ฝ(โ„(m) modulo 12). + month %= 12; + if month < 0 { + month += 12; + year -= 1; + } + + // 8. Find a finite time value t such that YearFromTime(t) is ym and MonthFromTime(t) is mn and DateFromTime(t) is + // 1๐”ฝ; but if this is not possible (because some argument is out of range), return NaN. + let month = usize::try_from(month).expect("month must be between 0 and 11 at this point"); + + let mut day = day_from_year(year); + + // Consider leap years when calculating the cumulative days added to the year from the input month + if (year % 4 != 0) || (year % 100 == 0 && year % 400 != 0) { + day += [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334][month]; + } else { + day += [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335][month]; + } + + // 9. Return Day(t) + dt - 1๐”ฝ. + (day - 1).checked_add(date) +} + +/// Abstract operation [`MakeDate`][spec]. +/// +/// [spec]: https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-makedate +pub(super) fn make_date(day: i64, time: i64) -> Option { + // 1. If day is not finite or time is not finite, return NaN. + // 2. Let tv be day ร— msPerDay + time. + // 3. If tv is not finite, return NaN. + // 4. Return tv. + day.checked_mul(MILLIS_PER_DAY)?.checked_add(time) +} + +/// Abstract operation [`TimeClip`][spec] +/// Returns the timestamp (number of milliseconds) if it is in the expected range. +/// Otherwise, returns `None`. +/// +/// [spec]: https://tc39.es/ecma262/#sec-timeclip +#[inline] +pub(super) fn time_clip(time: i64) -> Option { + // 1. If time is not finite, return NaN. + // 2. If abs(โ„(time)) > 8.64 ร— 10^15, return NaN. + // 3. Return ๐”ฝ(! ToIntegerOrInfinity(time)). + (time.checked_abs()? <= MAX_TIMESTAMP).then_some(time) +} + +#[derive(Default, Debug, Clone, Copy)] +pub(super) struct DateParameters { + pub(super) year: Option, + pub(super) month: Option, + pub(super) date: Option, + pub(super) hour: Option, + pub(super) minute: Option, + pub(super) second: Option, + pub(super) millisecond: Option, +} + +/// Replaces some (or all) parameters of `date` with the specified parameters +pub(super) fn replace_params( + datetime: NaiveDateTime, + params: DateParameters, + local: bool, +) -> Option { + let DateParameters { + year, + month, + date, + hour, + minute, + second, + millisecond, + } = params; + + let datetime = if local { + Local.from_utc_datetime(&datetime).naive_local() + } else { + datetime + }; + + let year = match year { + Some(i) => i.as_integer()?, + None => i64::from(datetime.year()), + }; + let month = match month { + Some(i) => i.as_integer()?, + None => i64::from(datetime.month() - 1), + }; + let date = match date { + Some(i) => i.as_integer()?, + None => i64::from(datetime.day()), + }; + let hour = match hour { + Some(i) => i.as_integer()?, + None => i64::from(datetime.hour()), + }; + let minute = match minute { + Some(i) => i.as_integer()?, + None => i64::from(datetime.minute()), + }; + let second = match second { + Some(i) => i.as_integer()?, + None => i64::from(datetime.second()), + }; + let millisecond = match millisecond { + Some(i) => i.as_integer()?, + None => i64::from(datetime.timestamp_subsec_millis()), + }; + + let new_day = make_day(year, month, date)?; + let new_time = make_time(hour, minute, second, millisecond)?; + let mut ts = make_date(new_day, new_time)?; + + if local { + ts = Local + .from_local_datetime(&NaiveDateTime::from_timestamp_millis(ts)?) + .earliest()? + .naive_utc() + .timestamp_millis(); + } + + NaiveDateTime::from_timestamp_millis(time_clip(ts)?) +} diff --git a/boa_engine/src/builtins/set/mod.rs b/boa_engine/src/builtins/set/mod.rs index 6291fbcb9f..997225dedd 100644 --- a/boa_engine/src/builtins/set/mod.rs +++ b/boa_engine/src/builtins/set/mod.rs @@ -251,20 +251,18 @@ impl Set { /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.clear /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear pub(crate) fn clear(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - if let Some(object) = this.as_object() { - if object.borrow().is_set() { - this.set_data(ObjectData::set(OrderedSet::new())); - Ok(JsValue::undefined()) - } else { - Err(JsNativeError::typ() - .with_message("'this' is not a Set") - .into()) - } - } else { - Err(JsNativeError::typ() - .with_message("'this' is not a Set") - .into()) - } + let mut object = this + .as_object() + .map(JsObject::borrow_mut) + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?; + + let set = object + .as_set_mut() + .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?; + + set.clear(); + + Ok(JsValue::undefined()) } /// `Set.prototype.delete( value )` diff --git a/boa_engine/src/builtins/set/ordered_set.rs b/boa_engine/src/builtins/set/ordered_set.rs index 758bb9c503..6d8753fbdd 100644 --- a/boa_engine/src/builtins/set/ordered_set.rs +++ b/boa_engine/src/builtins/set/ordered_set.rs @@ -90,6 +90,12 @@ where self.inner.shift_remove(value) } + /// Removes all elements in the set, while preserving its capacity. + #[inline] + pub fn clear(&mut self) { + self.inner.clear(); + } + /// Checks if a given value is present in the set /// /// Return `true` if `value` is present in set, false otherwise. diff --git a/boa_engine/src/builtins/weak/weak_ref.rs b/boa_engine/src/builtins/weak/weak_ref.rs index a111b5cd0f..29f8cf6ef7 100644 --- a/boa_engine/src/builtins/weak/weak_ref.rs +++ b/boa_engine/src/builtins/weak/weak_ref.rs @@ -161,6 +161,6 @@ mod tests { ) .unwrap(), JsValue::undefined() - ) + ); } } diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 0fc3ea736e..1e5df8736b 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -641,6 +641,7 @@ impl ContextBuilder { /// /// This function is only available if the `fuzz` feature is enabled. #[cfg(feature = "fuzz")] + #[must_use] pub fn instructions_remaining(mut self, instructions_remaining: usize) -> Self { self.instructions_remaining = instructions_remaining; self diff --git a/boa_engine/src/object/builtins/jsdate.rs b/boa_engine/src/object/builtins/jsdate.rs index ed0298a1fd..f84bb832de 100644 --- a/boa_engine/src/object/builtins/jsdate.rs +++ b/boa_engine/src/object/builtins/jsdate.rs @@ -1,11 +1,12 @@ use std::ops::Deref; use boa_gc::{Finalize, Trace}; +use chrono::DateTime; use crate::{ builtins::Date, - object::{JsObject, JsObjectType}, - Context, JsResult, JsValue, + object::{JsObject, JsObjectType, ObjectData}, + Context, JsNativeError, JsResult, JsValue, }; /// `JsDate` is a wrapper for JavaScript `JsDate` builtin object @@ -39,7 +40,8 @@ impl JsDate { /// Create a new `Date` object with universal time. #[inline] pub fn new(context: &mut Context) -> Self { - let inner = Date::date_create(None, context); + let prototype = context.intrinsics().constructors().date().prototype(); + let inner = JsObject::from_proto_and_data(prototype, ObjectData::date(Date::default())); Self { inner } } @@ -79,7 +81,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getDate()`. #[inline] pub fn get_date(&self, context: &mut Context) -> JsResult { - Date::get_date(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_date::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the day of the week (0โ€“6) for the specified date @@ -88,7 +90,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getDay()`. #[inline] pub fn get_day(&self, context: &mut Context) -> JsResult { - Date::get_day(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_day::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the year (4 digits for 4-digit years) of the specified date @@ -97,7 +99,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getFullYear()`. #[inline] pub fn get_full_year(&self, context: &mut Context) -> JsResult { - Date::get_full_year(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_full_year::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the hour (0โ€“23) in the specified date according to local time. @@ -105,7 +107,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getHours()`. #[inline] pub fn get_hours(&self, context: &mut Context) -> JsResult { - Date::get_hours(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_hours::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the milliseconds (0โ€“999) in the specified date according @@ -114,7 +116,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMilliseconds()`. #[inline] pub fn get_milliseconds(&self, context: &mut Context) -> JsResult { - Date::get_milliseconds(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the minutes (0โ€“59) in the specified date according to local time. @@ -122,7 +124,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMinutes()`. #[inline] pub fn get_minutes(&self, context: &mut Context) -> JsResult { - Date::get_minutes(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_minutes::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the month (0โ€“11) in the specified date according to local time. @@ -130,7 +132,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getMonth()`. #[inline] pub fn get_month(&self, context: &mut Context) -> JsResult { - Date::get_month(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_month::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the seconds (0โ€“59) in the specified date according to local time. @@ -138,7 +140,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getSeconds()`. #[inline] pub fn get_seconds(&self, context: &mut Context) -> JsResult { - Date::get_seconds(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_seconds::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the numeric value of the specified date as the number @@ -165,7 +167,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCDate()`. #[inline] pub fn get_utc_date(&self, context: &mut Context) -> JsResult { - Date::get_utc_date(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_date::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the day of the week (0โ€“6) in the specified @@ -174,7 +176,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCDay()`. #[inline] pub fn get_utc_day(&self, context: &mut Context) -> JsResult { - Date::get_utc_day(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_day::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the year (4 digits for 4-digit years) in the specified @@ -183,7 +185,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCFullYear()`. #[inline] pub fn get_utc_full_year(&self, context: &mut Context) -> JsResult { - Date::get_utc_full_year(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_full_year::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the hours (0โ€“23) in the specified date according @@ -192,7 +194,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCHours()`. #[inline] pub fn get_utc_hours(&self, context: &mut Context) -> JsResult { - Date::get_utc_hours(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_hours::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the milliseconds (0โ€“999) in the specified date @@ -201,7 +203,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMilliseconds()`. #[inline] pub fn get_utc_milliseconds(&self, context: &mut Context) -> JsResult { - Date::get_utc_milliseconds(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_milliseconds::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the minutes (0โ€“59) in the specified date according @@ -210,7 +212,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMinutes()`. #[inline] pub fn get_utc_minutes(&self, context: &mut Context) -> JsResult { - Date::get_utc_minutes(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_minutes::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the month (0โ€“11) in the specified date according @@ -219,7 +221,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCMonth()`. #[inline] pub fn get_utc_month(&self, context: &mut Context) -> JsResult { - Date::get_utc_month(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_month::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Returns the seconds (0โ€“59) in the specified date according @@ -228,19 +230,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.getUTCSeconds()`. #[inline] pub fn get_utc_seconds(&self, context: &mut Context) -> JsResult { - Date::get_utc_seconds(&self.inner.clone().into(), &[JsValue::null()], context) - } - - /// DEPRECATED: This feature is no longer recommended. - /// USE: `get_full_year()` instead. - /// Returns the year (usually 2โ€“3 digits) in the specified date - /// according to local time. - /// - /// Same as JavaScript's `Date.prototype.getYear()`. - #[deprecated] - #[inline] - pub fn get_year(&self, context: &mut Context) -> JsResult { - Date::get_year(&self.inner.clone().into(), &[JsValue::null()], context) + Date::get_seconds::(&self.inner.clone().into(), &[JsValue::null()], context) } /// Sets the day of the month for a specified date according @@ -255,7 +245,7 @@ impl JsDate { where T: Into, { - Date::set_date(&self.inner.clone().into(), &[value.into()], context) + Date::set_date::(&self.inner.clone().into(), &[value.into()], context) } /// Sets the full year (e.g. 4 digits for 4-digit years) for a @@ -267,7 +257,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setFullYear()`. #[inline] pub fn set_full_year(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_full_year(&self.inner.clone().into(), values, context) + Date::set_full_year::(&self.inner.clone().into(), values, context) } /// Sets the hours for a specified date according to local time. @@ -278,7 +268,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setHours()`. #[inline] pub fn set_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_hours(&self.inner.clone().into(), values, context) + Date::set_hours::(&self.inner.clone().into(), values, context) } /// Sets the milliseconds for a specified date according to local time. @@ -292,7 +282,7 @@ impl JsDate { where T: Into, { - Date::set_milliseconds(&self.inner.clone().into(), &[value.into()], context) + Date::set_milliseconds::(&self.inner.clone().into(), &[value.into()], context) } /// Sets the minutes for a specified date according to local time. @@ -303,7 +293,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setMinutes()`. #[inline] pub fn set_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_minutes(&self.inner.clone().into(), values, context) + Date::set_minutes::(&self.inner.clone().into(), values, context) } /// Sets the month for a specified date according to local time. @@ -314,7 +304,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setMonth()`. #[inline] pub fn set_month(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_month(&self.inner.clone().into(), values, context) + Date::set_month::(&self.inner.clone().into(), values, context) } /// Sets the seconds for a specified date according to local time. @@ -325,7 +315,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setSeconds()`. #[inline] pub fn set_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_seconds(&self.inner.clone().into(), values, context) + Date::set_seconds::(&self.inner.clone().into(), values, context) } /// Sets the Date object to the time represented by a number @@ -356,7 +346,7 @@ impl JsDate { where T: Into, { - Date::set_utc_date(&self.inner.clone().into(), &[value.into()], context) + Date::set_date::(&self.inner.clone().into(), &[value.into()], context) } /// Sets the full year (e.g. 4 digits for 4-digit years) for a @@ -372,7 +362,7 @@ impl JsDate { values: &[JsValue], context: &mut Context, ) -> JsResult { - Date::set_utc_full_year(&self.inner.clone().into(), values, context) + Date::set_full_year::(&self.inner.clone().into(), values, context) } /// Sets the hours for a specified date according to universal time. @@ -383,7 +373,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setUTCHours()`. #[inline] pub fn set_utc_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_utc_hours(&self.inner.clone().into(), values, context) + Date::set_hours::(&self.inner.clone().into(), values, context) } /// Sets the milliseconds for a specified date according to universal time. @@ -397,7 +387,7 @@ impl JsDate { where T: Into, { - Date::set_utc_milliseconds(&self.inner.clone().into(), &[value.into()], context) + Date::set_milliseconds::(&self.inner.clone().into(), &[value.into()], context) } /// Sets the minutes for a specified date according to universal time. @@ -408,7 +398,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setUTCMinutes()`. #[inline] pub fn set_utc_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_utc_minutes(&self.inner.clone().into(), values, context) + Date::set_minutes::(&self.inner.clone().into(), values, context) } /// Sets the month for a specified date according to universal time. @@ -419,7 +409,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setUTCMonth()`. #[inline] pub fn set_utc_month(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_month(&self.inner.clone().into(), values, context) + Date::set_month::(&self.inner.clone().into(), values, context) } /// Sets the seconds for a specified date according to universal time. @@ -430,23 +420,7 @@ impl JsDate { /// Same as JavaScript's `Date.prototype.setUTCSeconds()`. #[inline] pub fn set_utc_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult { - Date::set_utc_seconds(&self.inner.clone().into(), values, context) - } - - /// DEPRECATED: This feature is no longer recommended. - /// USE: `set_full_year()` instead. - /// Sets the year for a specified date according to local time. - /// Return a `Number` representing the milliseconds elapsed since - /// the UNIX epoch. - /// - /// Same as JavaScript's legacy `Date.prototype.setYear()`. - #[deprecated] - #[inline] - pub fn set_year(&self, value: T, context: &mut Context) -> JsResult - where - T: Into, - { - Date::set_year(&self.inner.clone().into(), &[value.into()], context) + Date::set_seconds::(&self.inner.clone().into(), values, context) } /// Returns the "date" portion of the Date as a human-readable string. @@ -554,9 +528,19 @@ impl JsDate { /// Utility create a `Date` object from RFC3339 string #[inline] - pub fn new_from_parse(value: &JsValue, context: &mut Context) -> Self { - let inner = Date::create_obj(value, context); - Self { inner } + pub fn new_from_parse(value: &JsValue, context: &mut Context) -> JsResult { + let prototype = context.intrinsics().constructors().date().prototype(); + let string = value + .to_string(context)? + .to_std_string() + .map_err(|_| JsNativeError::typ().with_message("unpaired surrogate on date string"))?; + let date_time = DateTime::parse_from_rfc3339(&string) + .map_err(|err| JsNativeError::typ().with_message(err.to_string()))?; + let date_time = Date::new(Some(date_time.naive_local())); + + Ok(Self { + inner: JsObject::from_proto_and_data(prototype, ObjectData::date(date_time)), + }) } } diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 64a06e4d0a..61a3d8970a 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -1188,6 +1188,17 @@ impl Object { } } + #[inline] + pub fn as_date_mut(&mut self) -> Option<&mut Date> { + match self.data { + ObjectData { + kind: ObjectKind::Date(ref mut date), + .. + } => Some(date), + _ => None, + } + } + /// Checks if it a `RegExp` object. #[inline] pub fn is_regexp(&self) -> bool { diff --git a/boa_engine/src/value/integer.rs b/boa_engine/src/value/integer.rs index 2cebeeadb3..64b757dd85 100644 --- a/boa_engine/src/value/integer.rs +++ b/boa_engine/src/value/integer.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -/// Represents the result of `ToIntegerOrInfinity` operation +/// Represents the result of the `ToIntegerOrInfinity` operation #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum IntegerOrInfinity { PositiveInfinity, @@ -90,3 +90,27 @@ impl PartialOrd for i64 { } } } + +/// Represents the result of the `to_integer_or_nan` method. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) enum IntegerOrNan { + Integer(i64), + Nan, +} + +impl IntegerOrNan { + /// Gets the wrapped `i64` if the variant is an `Integer`. + pub(crate) fn as_integer(self) -> Option { + match self { + Self::Integer(i) => Some(i), + Self::Nan => None, + } + } +} + +impl From for IntegerOrNan { + fn from(ior: IntegerOrInfinity) -> Self { + ior.as_integer() + .map_or(IntegerOrNan::Nan, IntegerOrNan::Integer) + } +} diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index 473a1029d5..b40acc5482 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -44,6 +44,8 @@ pub use integer::IntegerOrInfinity; pub use operations::*; pub use r#type::Type; +pub(crate) use self::integer::IntegerOrNan; + static TWO_E_64: Lazy = Lazy::new(|| { const TWO_E_64: u128 = 2u128.pow(64); BigInt::from(TWO_E_64) @@ -333,14 +335,6 @@ impl JsValue { } } - /// Set the kind of an object. - #[inline] - pub fn set_data(&self, data: ObjectData) { - if let Self::Object(ref obj) = *self { - obj.borrow_mut().data = data; - } - } - /// The abstract operation `ToPrimitive` takes an input argument and an optional argumen`PreferredType`pe. /// /// @@ -869,6 +863,22 @@ impl JsValue { Ok(IntegerOrInfinity::from(number)) } + /// Modified abstract operation `ToIntegerOrInfinity ( argument )`. + /// + /// This function is almost the same as [`Self::to_integer_or_infinity`], but with the exception + /// that this will return `Nan` if [`Self::to_number`] returns a non-finite number. + pub(crate) fn to_integer_or_nan(&self, context: &mut Context) -> JsResult { + // 1. Let number be ? ToNumber(argument). + let number = self.to_number(context)?; + + if number.is_nan() { + return Ok(IntegerOrNan::Nan); + } + + // Continues on `IntegerOrInfinity::from::` + Ok(IntegerOrInfinity::from(number).into()) + } + /// Converts a value to a double precision floating point. /// /// This function is equivalent to the unary `+` operator (`+value`) in JavaScript diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 7f75db94aa..25bd0dd5d6 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -182,9 +182,10 @@ impl Context { while self.vm.frame().pc < self.vm.frame().code.code.len() { #[cfg(feature = "fuzz")] - if self.instructions_remaining == 0 { - return Err(JsError::from_native(JsNativeError::no_instructions_remain())); - } else { + { + if self.instructions_remaining == 0 { + return Err(JsError::from_native(JsNativeError::no_instructions_remain())); + } self.instructions_remaining -= 1; }