Browse Source

Safe wrapper for `JsDate` (#2181)

This PR adds a safe wrapper around JavaScript `JsDate` from `builtins::date`, and is being tracked at #2098.

#### Implements following methods 
- [x] `new Date()`
- [x] `Date.prototype.getDate()`
- [x] `Date.prototype.getDay()`
- [x] `Date.prototype.getFullYear()`
- [x] `Date.prototype.getHours()`
- [x] `Date.prototype.getMilliseconds()`
- [x] `Date.prototype.getMinutes()`
- [x] `Date.prototype.getMonth()`
- [x] `Date.prototype.getSeconds()`
- [x] `Date.prototype.getTime()`
- [x] `Date.prototype.getTimezoneOffset()`
- [x] `Date.prototype.getUTCDate()`
- [x] `Date.prototype.getUTCDay()`
- [x] `Date.prototype.getUTCFullYear()`
- [x] `Date.prototype.getUTCHours()`
- [x] `Date.prototype.getUTCMilliseconds()`
- [x] `Date.prototype.getUTCMinutes()`
- [x] `Date.prototype.getUTCMonth()`
- [x] `Date.prototype.getUTCSeconds()`
- [x] `Date.prototype.getYear()`
- [x] `Date.now()`
- [ ] `Date.parse()` Issue 4
- [x] `Date.prototype.setDate()`
- [x] `Date.prototype.setFullYear()`
- [ ] `Date.prototype.setHours()` Issue 3
- [x] `Date.prototype.setMilliseconds()`
- [ ] `Date.prototype.setMinutes()` Issue 3
- [x] `Date.prototype.setMonth()`
- [x] `Date.prototype.setSeconds()`
- [x] `Date.prototype.setTime()`
- [x] `Date.prototype.setUTCDate()`
- [x] `Date.prototype.setUTCFullYear()`
- [x] `Date.prototype.setUTCHours()`
- [x] `Date.prototype.setUTCMilliseconds()`
- [x] `Date.prototype.setUTCMinutes()`
- [x] `Date.prototype.setUTCMonth()`
- [x] `Date.prototype.setUTCSeconds()`
- [x] `Date.prototype.setYear()`
- [ ] `Date.prototype.toDateString()` Issue 5
- [ ] `Date.prototype.toGMTString()` Issue 5
- [ ] `Date.prototype.toISOString()` Issue 5
- [ ] `Date.prototype.toJSON()` Issue 5
- [ ] `Date.prototype.toLocaleDateString()` Issue 5 and 6
- [ ] `Date.prototype.toLocaleString()` Issue 5 and 6
- [ ] `Date.prototype.toLocaleTimeString()` Issue 5 and 6
- [ ] `Date.prototype.toString()` Issue 5
- [ ] `Date.prototype.toTimeString()` Issue 5
- [ ] `Date.prototype.toUTCString()` Issue 5
- [x] `Date.UTC()`
- [x] `Date.prototype.valueOf()`

### Issues
1. ~~`get_*()` and some other methods - They take `&self` as input internally, and internal struct shouldn't be used in a wrapper API. Therefore, these would require input to be `this: &JsValue, args: &[JsValue], context: &mut Context` like others and use `this_time_value()`?~~ Fixed using `this_time_value()`
2. ~~`to_string()`- how can I use `Date::to_string()` rather than `alloc::string::ToString`.~~ My bad it compiles, just `rust-analyzer` was showing it as an issue.
3. `set_hours()` and `set_minutes()` - they subtract local timezones when setting the value, e.g.
    - On further look:
    ```rust
       // both function call `builtins:📅:mod.rs#L1038
      this.set_data(ObjectData::date(t));
      // `ObjectData::date` creates a new `Date` object `object::mods.rs#L423
      //                 | this date is chrono::Date<Tz(TimezoneOffset)> and Tz default is being used here which is GMT+0
      pub fn date(date: Date) -> Self {
        Self {
            kind: ObjectKind::Date(date),
            internal_methods: &ORDINARY_INTERNAL_METHODS,
        }
    }
     ```
     - BTW, in `object::mod.rs`'s `enum ObjectKind` there is `Date(chrono::Date)` and it requires 
     the generic argument, how is it being bypassed here?
     - Also in `set_minutes()` step 6, `LocalTime` should be used.
```rust
// reference date = 2000-01-01T06:26:53.984
date.set_hours(&[23.into(), 23.into(), 23.into(), 23.into()], context)?;
// would add tiemzone(+5:30) to it
// Is                 2000-01-01T17:53:23.023
// Should be    2000-01-01T23:23:23.023
```
4. `parse()` - it uses `chrono::parse_from_rfc3339` internally, while es6 spec recommends ISO8601. And it can also parse other formats like from [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse) `04 Dec 1995 00:12:00 GMT` which fails. So what should be done about it.
5. `to_*()` - This is more general, as the internal date object uses `chrono::NaiveDateTime` which doesn't have timezone. It doesn't account for `+4:00` in example below.
```rust
// Creates new `Date` object from given rfc3339 string.
let date = JsDate::new_from_parse(&JsValue::new("2018-01-26T18:30:09.453+04:00"), context);
println!("to_string: {:?}", date2.to_string(context)?); 
// IS:          Sat Jan 27 2018 00:00:09 GMT+0530
// Should:  Fri Jan 26 2018 20:00:09 GMT+0530
```
6. `to_locale_*()` - requires [`ToDateTimeOptions`](https://402.ecma-international.org/9.0/#sec-todatetimeoptions) and localization would require chrono's `unstable-locales` feature, which is available for `DateTime` and not for `NaiveDateTime`. 
    - I should have looked properly, `to_date_time_options` is already implemented in `builtins::intl`. Anyway, I would still need some tips on how to use it. What would function signature be like in wrapper API, how would `options` be passed to the said API. 
    - So `to_date_time_options()` takes `options: &JsValue` as an argument and build an object from it and fetch properties through `Object.get()`. If I want `options` to be `{ weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }` what would `JsValue` look like to make it all work. 
    ```rust 
    date.to_locale_date_string(&[JsValue::new("en_EN"), OPTIONS], context)?;
    // OPTIONS need to be a JsValue which when converted into an object 
    // have these properties { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
    ```


### Possible improvements 
1. Right now, `object::jsdate::set_full_year()` and alike (input is a slice) are like below, `into()` doesn't feel ergonomic.
    ```rust
    #[inline]
    pub fn set_full_year(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
        Date::set_full_year(&self.inner.clone().into(), values, context)
    }
    // Usage 
    date.set_full_year(&[2000.into(), 0.into(), 1.into()], context)?;
    // How can something like this be made to work
    #[inline]
    pub fn set_full_year<T>(&self, values: &[T], context: &mut Context) -> JsResult<JsValue>
    where
        T: Into<JsValue>,
    {
                                                          | expected reference `&[value::JsValue]` 
                                                          | found reference `&[T]`        
        Date::set_full_year(&self.inner.clone().into(), values, context)
    }
    ```
2. Any other suggestion?
pull/2427/head
Anuvrat 2 years ago
parent
commit
70f73b45d8
  1. 484
      boa_engine/src/builtins/date/mod.rs
  2. 586
      boa_engine/src/object/builtins/jsdate.rs
  3. 2
      boa_engine/src/object/builtins/mod.rs
  4. 78
      boa_examples/src/bin/jsdate.rs

484
boa_engine/src/builtins/date/mod.rs

@ -13,7 +13,7 @@ use crate::{
string::utf16,
symbol::WellKnownSymbols,
value::{JsValue, PreferredType},
Context, JsResult,
Context, JsError, JsResult,
};
use boa_profiler::Profiler;
use chrono::{prelude::*, Duration, LocalResult};
@ -48,29 +48,15 @@ fn ignore_ambiguity<T>(result: LocalResult<T>) -> Option<T> {
}
}
macro_rules! getter_method {
($name:ident) => {{
fn get_value(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
Ok(JsValue::new(this_time_value(this)?.$name()))
}
get_value
}};
(Self::$name:ident) => {{
fn get_value(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
Ok(JsValue::new(Date::$name()))
}
get_value
}};
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Date(Option<NaiveDateTime>);
impl Display for Date {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.to_local() {
Some(v) => write!(f, "{}", v.format("%a %b %d %Y %H:%M:%S GMT%:z")),
_ => write!(f, "Invalid Date"),
if let Some(v) = self.to_local() {
write!(f, "{}", v.format("%a %b %d %Y %H:%M:%S GMT%:z"))
} else {
write!(f, "Invalid Date")
}
}
}
@ -94,29 +80,27 @@ impl BuiltIn for Date {
)
.name(Self::NAME)
.length(Self::LENGTH)
.method(getter_method!(get_date), "getDate", 0)
.method(getter_method!(get_day), "getDay", 0)
.method(getter_method!(get_full_year), "getFullYear", 0)
.method(getter_method!(get_hours), "getHours", 0)
.method(getter_method!(get_milliseconds), "getMilliseconds", 0)
.method(getter_method!(get_minutes), "getMinutes", 0)
.method(getter_method!(get_month), "getMonth", 0)
.method(getter_method!(get_seconds), "getSeconds", 0)
.method(getter_method!(get_time), "getTime", 0)
.method(getter_method!(get_year), "getYear", 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(getter_method!(get_utc_date), "getUTCDate", 0)
.method(getter_method!(get_utc_day), "getUTCDay", 0)
.method(getter_method!(get_utc_full_year), "getUTCFullYear", 0)
.method(getter_method!(get_utc_hours), "getUTCHours", 0)
.method(
getter_method!(get_utc_milliseconds),
"getUTCMilliseconds",
0,
)
.method(getter_method!(get_utc_minutes), "getUTCMinutes", 0)
.method(getter_method!(get_utc_month), "getUTCMonth", 0)
.method(getter_method!(get_utc_seconds), "getUTCSeconds", 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_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)
@ -124,7 +108,6 @@ impl BuiltIn for Date {
.method(Self::set_minutes, "setMinutes", 3)
.method(Self::set_month, "setMonth", 2)
.method(Self::set_seconds, "setSeconds", 2)
.method(Self::set_year, "setYear", 1)
.method(Self::set_time, "setTime", 1)
.method(Self::set_utc_date, "setUTCDate", 1)
.method(Self::set_utc_full_year, "setUTCFullYear", 3)
@ -133,23 +116,24 @@ impl BuiltIn for Date {
.method(Self::set_utc_minutes, "setUTCMinutes", 3)
.method(Self::set_utc_month, "setUTCMonth", 2)
.method(Self::set_utc_seconds, "setUTCSeconds", 2)
.method(Self::set_year, "setYear", 1)
.method(Self::to_date_string, "toDateString", 0)
.method(getter_method!(to_gmt_string), "toGMTString", 0)
.method(Self::to_gmt_string, "toGMTString", 0)
.method(Self::to_iso_string, "toISOString", 0)
.method(Self::to_json, "toJSON", 1)
// Locale strings
.method(Self::to_locale_date_string, "toLocaleDateString", 2)
.method(Self::to_locale_string, "toLocaleString", 2)
.method(Self::to_locale_time_string, "toLocaleTimeString", 2)
.method(Self::to_string, "toString", 0)
.method(Self::to_time_string, "toTimeString", 0)
.method(getter_method!(to_utc_string), "toUTCString", 0)
.method(getter_method!(value_of), "valueOf", 0)
.method(Self::to_utc_string, "toUTCString", 0)
.static_method(Self::utc, "UTC", 7)
.method(Self::value_of, "valueOf", 0)
.method(
Self::to_primitive,
(WellKnownSymbols::to_primitive(), "[Symbol.toPrimitive]"),
1,
)
.static_method(Self::now, "now", 0)
.static_method(Self::parse, "parse", 1)
.static_method(Self::utc, "UTC", 7)
.build()
.conv::<JsValue>()
.pipe(Some)
@ -349,6 +333,14 @@ impl Date {
}
}
/// Utility: Returns `Date` object with current datetime
pub(crate) fn date_create(prototype: Option<JsObject>, context: &mut Context) -> JsObject {
let prototype =
prototype.unwrap_or_else(|| context.intrinsics().constructors().date().prototype());
Self::make_date_now(prototype)
}
/// `Date()`
///
/// The `Date()` function is used to create a string that represent the current date and time.
@ -545,8 +537,17 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_local().map_or(f64::NAN, |dt| f64::from(dt.day()))
pub fn get_date(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
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())
}
}
/// `Date.prototype.getDay()`
@ -560,12 +561,13 @@ impl 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(&self) -> f64 {
self.to_local().map_or(f64::NAN, |dt| {
let weekday = dt.weekday() as u32;
let weekday = (weekday + 1) % 7; // 0 represents Monday in Chrono
f64::from(weekday)
})
pub fn get_day(this: &JsValue, _args: &[JsValue], _context: &mut Context) -> JsResult<JsValue> {
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())
}
}
/// `Date.prototype.getFullYear()`
@ -578,8 +580,17 @@ impl 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(&self) -> f64 {
self.to_local().map_or(f64::NAN, |dt| f64::from(dt.year()))
pub fn get_full_year(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
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())
}
}
/// `Date.prototype.getHours()`
@ -592,8 +603,17 @@ impl 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(&self) -> f64 {
self.to_local().map_or(f64::NAN, |dt| f64::from(dt.hour()))
pub fn get_hours(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
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())
}
}
/// `Date.prototype.getMilliseconds()`
@ -606,10 +626,17 @@ impl 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(&self) -> f64 {
self.to_local().map_or(f64::NAN, |dt| {
f64::from(dt.nanosecond()) / NANOS_PER_MS as f64
})
pub fn get_milliseconds(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
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())
}
}
/// `Date.prototype.getMinutes()`
@ -622,9 +649,17 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_local()
.map_or(f64::NAN, |dt| f64::from(dt.minute()))
pub fn get_minutes(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
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())
}
}
/// `Date.prototype.getMonth()`
@ -638,9 +673,17 @@ impl 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(&self) -> f64 {
self.to_local()
.map_or(f64::NAN, |dt| f64::from(dt.month0()))
pub fn get_month(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
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())
}
}
/// `Date.prototype.getSeconds()`
@ -653,9 +696,17 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_local()
.map_or(f64::NAN, |dt| f64::from(dt.second()))
pub fn get_seconds(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
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()`
@ -670,9 +721,14 @@ impl 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 fn get_year(&self) -> f64 {
self.to_local()
.map_or(f64::NAN, |dt| f64::from(dt.year()) - 1900f64)
pub fn get_year(this: &JsValue, _args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
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()`
@ -685,7 +741,20 @@ impl Date {
///
/// [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(&self) -> f64 {
pub fn get_time(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.timestamp_millis()))
} else {
Ok(JsValue::nan())
}
}
/// Utility: Internal `get_time()`
fn int_get_time(&self) -> f64 {
self.to_utc()
.map_or(f64::NAN, |dt| dt.timestamp_millis() as f64)
}
@ -731,8 +800,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_utc().map_or(f64::NAN, |dt| f64::from(dt.day()))
pub fn get_utc_date(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.day()))
} else {
Ok(JsValue::nan())
}
}
/// `Date.prototype.getUTCDay()`
@ -746,12 +823,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_utc().map_or(f64::NAN, |dt| {
let weekday = dt.weekday() as u32;
let weekday = (weekday + 1) % 7; // 0 represents Monday in Chrono
f64::from(weekday)
})
pub fn get_utc_day(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
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()`
@ -764,8 +845,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_utc().map_or(f64::NAN, |dt| f64::from(dt.year()))
pub fn get_utc_full_year(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.year()))
} else {
Ok(JsValue::nan())
}
}
/// `Date.prototype.getUTCHours()`
@ -778,8 +867,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_utc().map_or(f64::NAN, |dt| f64::from(dt.hour()))
pub fn get_utc_hours(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.hour()))
} else {
Ok(JsValue::nan())
}
}
/// `Date.prototype.getUTCMilliseconds()`
@ -792,10 +889,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_utc().map_or(f64::NAN, |dt| {
f64::from(dt.nanosecond()) / NANOS_PER_MS as f64
})
pub fn get_utc_milliseconds(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.timestamp_subsec_millis()))
} else {
Ok(JsValue::nan())
}
}
/// `Date.prototype.getUTCMinutes()`
@ -808,8 +911,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_utc().map_or(f64::NAN, |dt| f64::from(dt.minute()))
pub fn get_utc_minutes(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.minute()))
} else {
Ok(JsValue::nan())
}
}
/// `Date.prototype.getUTCMonth()`
@ -823,8 +934,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_utc().map_or(f64::NAN, |dt| f64::from(dt.month0()))
pub fn get_utc_month(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.month0()))
} else {
Ok(JsValue::nan())
}
}
/// `Date.prototype.getUTCSeconds()`
@ -837,8 +956,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.to_utc().map_or(f64::NAN, |dt| f64::from(dt.second()))
pub fn get_utc_seconds(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.second()))
} else {
Ok(JsValue::nan())
}
}
/// `Date.prototype.setDate()`
@ -867,7 +994,7 @@ impl Date {
t.set_components(false, None, None, Some(dt), None, None, None, None);
// 4. Let u be TimeClip(UTC(newDate)).
let u = t.get_time();
let u = t.int_get_time();
// 5. Set the [[DateValue]] internal slot of this Date object to u.
this.set_data(ObjectData::date(t));
@ -916,7 +1043,7 @@ impl Date {
t.set_components(false, Some(y), m, dt, None, None, None, None);
// 7. Let u be TimeClip(UTC(newDate)).
let u = t.get_time();
let u = t.int_get_time();
// 8. Set the [[DateValue]] internal slot of this Date object to u.
this.set_data(ObjectData::date(t));
@ -961,7 +1088,7 @@ impl Date {
t.set_components(false, None, None, None, Some(h), m, sec, milli);
// 7. Let u be TimeClip(UTC(date)).
let u = t.get_time();
let u = t.int_get_time();
// 8. Set the [[DateValue]] internal slot of this Date object to u.
this.set_data(ObjectData::date(t));
@ -999,7 +1126,7 @@ impl Date {
t.set_components(false, None, None, None, None, None, None, Some(ms));
// 4. Let u be TimeClip(UTC(MakeDate(Day(t), time))).
let u = t.get_time();
let u = t.int_get_time();
// 5. Set the [[DateValue]] internal slot of this Date object to u.
this.set_data(ObjectData::date(t));
@ -1043,7 +1170,7 @@ impl Date {
t.set_components(false, None, None, None, None, Some(m), s, milli);
// 6. Let u be TimeClip(UTC(date)).
let u = t.get_time();
let u = t.int_get_time();
// 7. Set the [[DateValue]] internal slot of this Date object to u.
this.set_data(ObjectData::date(t));
@ -1080,7 +1207,7 @@ impl Date {
t.set_components(false, None, Some(m), dt, None, None, None, None);
// 5. Let u be TimeClip(UTC(newDate)).
let u = t.get_time();
let u = t.int_get_time();
// 6. Set the [[DateValue]] internal slot of this Date object to u.
this.set_data(ObjectData::date(t));
@ -1121,7 +1248,7 @@ impl Date {
t.set_components(false, None, None, None, None, None, Some(s), milli);
// 5. Let u be TimeClip(UTC(date)).
let u = t.get_time();
let u = t.int_get_time();
// 6. Set the [[DateValue]] internal slot of this Date object to u.
this.set_data(ObjectData::date(t));
@ -1183,7 +1310,7 @@ impl Date {
this.set_data(ObjectData::date(t));
// 11. Return the value of the [[DateValue]] internal slot of this Date object.
Ok(t.get_time().into())
Ok(t.int_get_time().into())
}
/// `Date.prototype.setTime()`
@ -1215,13 +1342,13 @@ impl Date {
};
// 3. Let v be TimeClip(t).
let v = t.get_time();
let v = Self::get_time(this, args, context)?;
// 4. Set the [[DateValue]] internal slot of this Date object to v.
this.set_data(ObjectData::date(t));
// 5. Return v.
Ok(v.into())
Ok(v)
}
/// `Date.prototype.setUTCDate()`
@ -1253,13 +1380,13 @@ impl Date {
t.set_components(true, None, None, Some(dt), None, None, None, None);
// 4. Let v be TimeClip(newDate).
let v = t.get_time();
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.into())
Ok(v)
}
/// `Date.prototype.setFullYear()`
@ -1306,13 +1433,13 @@ impl Date {
t.set_components(true, Some(y), m, dt, None, None, None, None);
// 7. Let v be TimeClip(newDate).
let v = t.get_time();
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.into())
Ok(v)
}
/// `Date.prototype.setUTCHours()`
@ -1355,13 +1482,13 @@ impl Date {
t.set_components(true, None, None, None, Some(h), m, sec, ms);
// 7. Let v be TimeClip(newDate).
let v = t.get_time();
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.into())
Ok(v)
}
/// `Date.prototype.setUTCMilliseconds()`
@ -1393,13 +1520,13 @@ impl Date {
t.set_components(true, None, None, None, None, None, None, Some(ms));
// 4. Let v be TimeClip(MakeDate(Day(t), time)).
let v = t.get_time();
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.into())
Ok(v)
}
/// `Date.prototype.setUTCMinutes()`
@ -1441,13 +1568,13 @@ impl Date {
t.set_components(true, None, None, None, None, Some(m), s, milli);
// 8. Let v be TimeClip(date).
let v = t.get_time();
let v = Self::get_time(this, args, context)?;
// 9. Set the [[DateValue]] internal slot of this Date object to v.
this.set_data(ObjectData::date(t));
// 10. Return v.
Ok(v.into())
Ok(v)
}
/// `Date.prototype.setUTCMonth()`
@ -1484,13 +1611,13 @@ impl Date {
t.set_components(true, None, Some(m), dt, None, None, None, None);
// 6. Let v be TimeClip(newDate).
let v = t.get_time();
let v = Self::get_time(this, args, context)?;
// 7. Set the [[DateValue]] internal slot of this Date object to v.
this.set_data(ObjectData::date(t));
// 8. Return v.
Ok(v.into())
Ok(v)
}
/// `Date.prototype.setUTCSeconds()`
@ -1527,13 +1654,13 @@ impl Date {
t.set_components(true, None, None, None, None, None, Some(s), milli);
// 6. Let v be TimeClip(date).
let v = t.get_time();
let v = Self::get_time(this, args, context)?;
// 7. Set the [[DateValue]] internal slot of this Date object to v.
this.set_data(ObjectData::date(t));
// 8. Return v.
Ok(v.into())
Ok(v)
}
/// `Date.prototype.toDateString()`
@ -1567,6 +1694,25 @@ impl Date {
}
}
/// `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<JsValue> {
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.
@ -1577,8 +1723,12 @@ impl Date {
///
/// [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(self) -> String {
self.to_utc_string()
pub fn to_gmt_string(
this: &JsValue,
_args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
Self::to_utc_string(this, &[JsValue::Null], context)
}
/// `Date.prototype.toISOString()`
@ -1641,7 +1791,7 @@ impl Date {
/// `Date.prototype.toString()`
///
/// The toString() method returns a string representing the specified Date object.
/// The `toString()` method returns a string representing the specified Date object.
///
/// More information:
/// - [ECMAScript reference][spec]
@ -1667,6 +1817,26 @@ impl Date {
}
}
/// `Date.prototype.toLocaleString()`
///
/// 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(
_this: &JsValue,
_: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
Err(JsError::from_opaque(JsValue::new(
"Function Unimplemented]",
)))
}
/// `Date.prototype.toTimeString()`
///
/// The `toTimeString()` method returns the time portion of a Date object in human readable form in American
@ -1699,6 +1869,27 @@ impl Date {
}
}
/// `Date.prototype.toLocaleTimeString()`
///
/// 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(
_this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
Err(JsError::from_opaque(JsValue::new(
"Function Unimplemented]",
)))
}
/// `Date.prototype.toUTCString()`
///
/// The `toUTCString()` method returns a string representing the specified Date object.
@ -1709,11 +1900,17 @@ impl Date {
///
/// [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(self) -> String {
self.to_utc().map_or_else(
|| "Invalid Date".to_string(),
|date_time| date_time.format("%a, %d %b %Y %H:%M:%S GMT").to_string(),
)
pub fn to_utc_string(
this: &JsValue,
_args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
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(context.construct_error("Invalid time value"))
}
}
/// `Date.prototype.valueOf()`
@ -1726,8 +1923,16 @@ impl Date {
///
/// [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(&self) -> f64 {
self.get_time()
pub fn value_of(
this: &JsValue,
_args: &[JsValue],
_context: &mut Context,
) -> JsResult<JsValue> {
if let Some(t) = this_time_value(this)?.0 {
Ok(JsValue::new(t.timestamp_millis()))
} else {
Ok(JsValue::nan())
}
}
/// `Date.now()`
@ -1831,6 +2036,17 @@ impl Date {
.and_then(|f| Self::time_clip(f.timestamp_millis() as f64))
.map_or(Ok(JsValue::nan()), |time| Ok(JsValue::new(time)))
}
/// 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))
}
}
/// The abstract operation `thisTimeValue` takes argument value.

586
boa_engine/src/object/builtins/jsdate.rs

@ -0,0 +1,586 @@
use std::ops::Deref;
use boa_gc::{Finalize, Trace};
use crate::{
builtins::Date,
object::{JsObject, JsObjectType},
Context, JsResult, JsValue,
};
/// `JsDate` is a wrapper for JavaScript `JsDate` builtin object
///
/// # Example
///
/// Create a `JsDate` object and set date to December 4 1995
///
/// ```
/// use boa_engine::{object::builtins::JsDate, Context, JsValue, JsResult};
///
/// fn main() -> JsResult<()> {
/// // JS mutable Context
/// let context = &mut Context::default();
///
/// let date = JsDate::new(context);
///
/// date.set_full_year(&[1995.into(), 11.into(), 4.into()], context)?;
///
/// assert_eq!(date.to_date_string(context)?, JsValue::from("Mon Dec 04 1995"));
///
/// Ok(())
/// }
/// ```
#[derive(Debug, Clone, Trace, Finalize)]
pub struct JsDate {
inner: JsObject,
}
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);
Self { inner }
}
/// Return a `Number` representing the milliseconds elapsed since the UNIX epoch.
///
/// Same as JavaScript's `Date.now()`
#[inline]
pub fn now(context: &mut Context) -> JsResult<JsValue> {
Date::now(&JsValue::Null, &[JsValue::Null], context)
}
// DEBUG: Uses RFC3339 internally therefore could match es6 spec of ISO8601 <========
/// Parse a `String` representation of date.
/// String should be ISO 8601 format.
/// Returns the `Number` of milliseconds since UNIX epoch if `String`
/// is valid, else return a `NaN`.
///
/// Same as JavaScript's `Date.parse(value)`.
#[inline]
pub fn parse(value: JsValue, context: &mut Context) -> JsResult<JsValue> {
Date::parse(&JsValue::Null, &[value], context)
}
/// Takes a [year, month, day, hour, minute, second, millisecond]
/// Return a `Number` representing the milliseconds elapsed since the UNIX epoch.
///
/// Same as JavaScript's `Date.UTC()`
#[inline]
pub fn utc(values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::utc(&JsValue::Null, values, context)
}
/// Returns the day of the month(1-31) for the specified date
/// according to local time.
///
/// Same as JavaScript's `Date.prototype.getDate()`.
#[inline]
pub fn get_date(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_date(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the day of the week (0–6) for the specified date
/// according to local time.
///
/// Same as JavaScript's `Date.prototype.getDay()`.
#[inline]
pub fn get_day(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_day(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the year (4 digits for 4-digit years) of the specified date
/// according to local time.
///
/// Same as JavaScript's `Date.prototype.getFullYear()`.
#[inline]
pub fn get_full_year(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_full_year(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the hour (0–23) in the specified date according to local time.
///
/// Same as JavaScript's `Date.prototype.getHours()`.
#[inline]
pub fn get_hours(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_hours(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the milliseconds (0–999) in the specified date according
/// to local time.
///
/// Same as JavaScript's `Date.prototype.getMilliseconds()`.
#[inline]
pub fn get_milliseconds(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_milliseconds(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the minutes (0–59) in the specified date according to local time.
///
/// Same as JavaScript's `Date.prototype.getMinutes()`.
#[inline]
pub fn get_minutes(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_minutes(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the month (0–11) in the specified date according to local time.
///
/// Same as JavaScript's `Date.prototype.getMonth()`.
#[inline]
pub fn get_month(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_month(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the seconds (0–59) in the specified date according to local time.
///
/// Same as JavaScript's `Date.prototype.getSeconds()`.
#[inline]
pub fn get_seconds(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_seconds(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the numeric value of the specified date as the number
/// of milliseconds since UNIX epoch.
/// Negative values are returned for prior times.
///
/// Same as JavaScript's `Date.prototype.getTime()`.
#[inline]
pub fn get_time(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_time(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the time-zone offset in minutes for the current locale.
///
/// Same as JavaScript's `Date.prototype.getTimezoneOffset()`.
#[inline]
pub fn get_timezone_offset(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_timezone_offset(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// Returns the day (date) of the month (1–31) in the specified
/// date according to universal time.
///
/// Same as JavaScript's `Date.prototype.getUTCDate()`.
#[inline]
pub fn get_utc_date(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_date(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the day of the week (0–6) in the specified
/// date according to universal time.
///
/// Same as JavaScript's `Date.prototype.getUTCDay()`.
#[inline]
pub fn get_utc_day(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_day(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the year (4 digits for 4-digit years) in the specified
/// date according to universal time.
///
/// Same as JavaScript's `Date.prototype.getUTCFullYear()`.
#[inline]
pub fn get_utc_full_year(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_full_year(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the hours (0–23) in the specified date according
/// to universal time.
///
/// Same as JavaScript's `Date.prototype.getUTCHours()`.
#[inline]
pub fn get_utc_hours(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_hours(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the milliseconds (0–999) in the specified date
/// according to universal time.
///
/// Same as JavaScript's `Date.prototype.getUTCMilliseconds()`.
#[inline]
pub fn get_utc_milliseconds(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_milliseconds(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the minutes (0–59) in the specified date according
/// to universal time.
///
/// Same as JavaScript's `Date.prototype.getUTCMinutes()`.
#[inline]
pub fn get_utc_minutes(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_minutes(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the month (0–11) in the specified date according
/// to universal time.
///
/// Same as JavaScript's `Date.prototype.getUTCMonth()`.
#[inline]
pub fn get_utc_month(&self, context: &mut Context) -> JsResult<JsValue> {
Date::get_utc_month(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Returns the seconds (0–59) in the specified date according
/// to universal time.
///
/// Same as JavaScript's `Date.prototype.getUTCSeconds()`.
#[inline]
pub fn get_utc_seconds(&self, context: &mut Context) -> JsResult<JsValue> {
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<JsValue> {
Date::get_year(&self.inner.clone().into(), &[JsValue::null()], context)
}
/// Sets the day of the month for a specified date according
/// to local time.
/// Takes a `month_value`.
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the given date.
///
/// Same as JavaScript's `Date.prototype.setDate()`.
#[inline]
pub fn set_date<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Date::set_date(&self.inner.clone().into(), &[value.into()], context)
}
/// Sets the full year (e.g. 4 digits for 4-digit years) for a
/// specified date according to local time.
/// Takes [`year_value`, `month_value`, `date_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and updated date.
///
/// Same as JavaScript's `Date.prototype.setFullYear()`.
#[inline]
pub fn set_full_year(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_full_year(&self.inner.clone().into(), values, context)
}
/// Sets the hours for a specified date according to local time.
/// Takes [`hours_value`, `minutes_value`, `seconds_value`, `ms_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setHours()`.
#[inline]
pub fn set_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_hours(&self.inner.clone().into(), values, context)
}
/// Sets the milliseconds for a specified date according to local time.
/// Takes a `milliseconds_value`
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and updated date.
///
/// Same as JavaScript's `Date.prototype.setMilliseconds()`.
#[inline]
pub fn set_milliseconds<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Date::set_milliseconds(&self.inner.clone().into(), &[value.into()], context)
}
/// Sets the minutes for a specified date according to local time.
/// Takes [`minutes_value`, `seconds_value`, `ms_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setMinutes()`.
#[inline]
pub fn set_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_minutes(&self.inner.clone().into(), values, context)
}
/// Sets the month for a specified date according to local time.
/// Takes [`month_value`, `day_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setMonth()`.
#[inline]
pub fn set_month(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_month(&self.inner.clone().into(), values, context)
}
/// Sets the seconds for a specified date according to local time.
/// Takes [`seconds_value`, `ms_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setSeconds()`.
#[inline]
pub fn set_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_seconds(&self.inner.clone().into(), values, context)
}
/// Sets the Date object to the time represented by a number
/// of milliseconds since UNIX epoch.
/// Takes number of milliseconds since UNIX epoch.
/// Use negative numbers for times prior.
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setTime()`.
#[inline]
pub fn set_time<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Date::set_time(&self.inner.clone().into(), &[value.into()], context)
}
/// Sets the day of the month for a specified date according
/// to universal time.
/// Takes a `month_value`.
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setUTCDate()`.
#[inline]
pub fn set_utc_date<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Date::set_utc_date(&self.inner.clone().into(), &[value.into()], context)
}
/// Sets the full year (e.g. 4 digits for 4-digit years) for a
/// specified date according to universal time.
/// Takes [`year_value`, `month_value`, `date_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setUTCFullYear()`.
#[inline]
pub fn set_utc_full_year(
&self,
values: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
Date::set_utc_full_year(&self.inner.clone().into(), values, context)
}
/// Sets the hours for a specified date according to universal time.
/// Takes [`hours_value`, `minutes_value`, `seconds_value`, `ms_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated dated.
///
/// Same as JavaScript's `Date.prototype.setUTCHours()`.
#[inline]
pub fn set_utc_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_utc_hours(&self.inner.clone().into(), values, context)
}
/// Sets the milliseconds for a specified date according to universal time.
/// Takes a `milliseconds_value`
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setUTCMilliseconds()`.
#[inline]
pub fn set_utc_milliseconds<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Date::set_utc_milliseconds(&self.inner.clone().into(), &[value.into()], context)
}
/// Sets the minutes for a specified date according to universal time.
/// Takes [`minutes_value`, `seconds_value`, `ms_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setUTCMinutes()`.
#[inline]
pub fn set_utc_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_utc_minutes(&self.inner.clone().into(), values, context)
}
/// Sets the month for a specified date according to universal time.
/// Takes [`month_value`, `day_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setUTCMonth()`.
#[inline]
pub fn set_utc_month(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::set_month(&self.inner.clone().into(), values, context)
}
/// Sets the seconds for a specified date according to universal time.
/// Takes [`seconds_value`, `ms_value`]
/// Return a `Number` representing the milliseconds elapsed between
/// the UNIX epoch and the updated date.
///
/// Same as JavaScript's `Date.prototype.setUTCSeconds()`.
#[inline]
pub fn set_utc_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
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<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
Date::set_year(&self.inner.clone().into(), &[value.into()], context)
}
/// Returns the "date" portion of the Date as a human-readable string.
///
/// Same as JavaScript's `Date.prototype.toDateString()`.
#[inline]
pub fn to_date_string(&self, context: &mut Context) -> JsResult<JsValue> {
Date::to_date_string(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// DEPRECATED: This feature is no longer recommended.
/// USE: `to_utc_string()` instead.
/// Returns a string representing the Date based on the GMT timezone.
///
/// Same as JavaScript's legacy `Date.prototype.toGMTString()`
#[deprecated]
#[inline]
pub fn to_gmt_string(&self, context: &mut Context) -> JsResult<JsValue> {
Date::to_gmt_string(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// Returns the given date in the ISO 8601 format according to universal
/// time.
///
/// Same as JavaScript's `Date.prototype.toISOString()`.
#[inline]
pub fn to_iso_string(&self, context: &mut Context) -> JsResult<JsValue> {
Date::to_iso_string(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// Returns a string representing the Date using `to_iso_string()`.
///
/// Same as JavaScript's `Date.prototype.toJSON()`.
#[inline]
pub fn to_json(&self, context: &mut Context) -> JsResult<JsValue> {
Date::to_json(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// Returns a string representing the date portion of the given Date instance
/// according to language-specific conventions.
/// Takes [locales, options]
///
/// Same as JavaScript's `Date.prototype.toLocaleDateString()`.
#[inline]
pub fn to_local_date_string(
&self,
values: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
Date::to_locale_date_string(&self.inner.clone().into(), values, context)
}
/// Returns a string representing the given date according to language-specific conventions.
/// Takes [locales, options]
///
/// Same as JavaScript's `Date.prototype.toLocaleDateString()`.
#[inline]
pub fn to_locale_string(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Date::to_locale_string(&self.inner.clone().into(), values, context)
}
/// Returns the "time" portion of the Date as human-readable string.
///
/// Same as JavaScript's `Date.prototype.toTimeString()`.
#[inline]
pub fn to_locale_time_string(
&self,
values: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
Date::to_locale_time_string(&self.inner.clone().into(), values, context)
}
/// Returns a string representing the specified Date object.
///
/// Same as JavaScript's `Date.prototype.toString()`.
#[inline]
pub fn to_string(&self, context: &mut Context) -> JsResult<JsValue> {
Date::to_string(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// Returns the "time" portion of the Date as human-readable string.
///
/// Same as JavaScript's `Date.prototype.toTimeString()`.
#[inline]
pub fn to_time_string(&self, context: &mut Context) -> JsResult<JsValue> {
Date::to_time_string(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// Returns a string representing the given date using the UTC time zone.
///
/// Same as JavaScript's `Date.prototype.toUTCString()`.
#[inline]
pub fn to_utc_string(&self, context: &mut Context) -> JsResult<JsValue> {
Date::to_utc_string(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// Returns the primitive value pf Date object.
///
/// Same as JavaScript's `Date.prototype.valueOf()`.
#[inline]
pub fn value_of(&self, context: &mut Context) -> JsResult<JsValue> {
Date::value_of(&self.inner.clone().into(), &[JsValue::Null], context)
}
/// 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 }
}
}
impl From<JsDate> for JsObject {
#[inline]
fn from(o: JsDate) -> Self {
o.inner.clone()
}
}
impl From<JsDate> for JsValue {
#[inline]
fn from(o: JsDate) -> Self {
o.inner.clone().into()
}
}
impl Deref for JsDate {
type Target = JsObject;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl JsObjectType for JsDate {}

2
boa_engine/src/object/builtins/mod.rs

@ -3,6 +3,7 @@
mod jsarray;
mod jsarraybuffer;
mod jsdataview;
mod jsdate;
mod jsfunction;
mod jsgenerator;
mod jsmap;
@ -16,6 +17,7 @@ mod jstypedarray;
pub use jsarray::*;
pub use jsarraybuffer::*;
pub use jsdataview::*;
pub use jsdate::*;
pub use jsfunction::*;
pub use jsgenerator::*;
pub use jsmap::*;

78
boa_examples/src/bin/jsdate.rs

@ -0,0 +1,78 @@
use boa_engine::{object::builtins::JsDate, Context, JsResult, JsValue};
fn main() -> JsResult<()> {
let context = &mut Context::default();
let date = JsDate::new(context);
// 823230245000.0
JsDate::utc(
&[
JsValue::new(96),
JsValue::new(1),
JsValue::new(2),
JsValue::new(3),
JsValue::new(4),
JsValue::new(5),
],
context,
)?;
// reference date: 2022-07-16T06:27:32.087241439
// sets day of the month to 24
date.set_date(24, context)?;
// 2022-07-24T06:27:11.567
// sets date to 1st of January 2000
date.set_full_year(&[2000.into(), 0.into(), 1.into()], context)?;
// 2000-01-01T06:26:53.984
// sets time to 10H:10M:10S:10mS
date.set_hours(&[23.into(), 23.into(), 23.into(), 23.into()], context)?;
// Is 2000-01-01T17:53:23.023
// Should be 2000-01-01T23:23:23.023
// sets milliseconds to 999
date.set_milliseconds(999, context)?;
// 2000-01-01T17:40:10.999
// sets time to 12M:12S:12ms
date.set_minutes(&[12.into(), 12.into(), 12.into()], context)?;
// Is 2000-01-01T17:42:12.012
// Should be 2000-01-01T17:12:12:012
// sets month to 9 and day to 9
date.set_month(&[9.into(), 9.into()], context)?;
// 2000-10-09T04:42:12.012
// set seconds to 59 and ms to 59
date.set_seconds(&[59.into(), 59.into()], context)?;
// 2000-10-09T04:42:59.059
assert_eq!(
date.to_json(context)?,
JsValue::from("2000-10-09T17:42:59.059Z")
);
assert_eq!(
date.to_date_string(context)?,
JsValue::from("Mon Oct 09 2000")
);
assert_eq!(
date.to_iso_string(context)?,
JsValue::from("2000-10-09T17:42:59.059Z")
);
assert_eq!(
date.to_time_string(context)?,
JsValue::from("23:12:59 GMT+0530")
);
assert_eq!(
date.to_string(context)?,
JsValue::from("Mon Oct 09 2000 23:12:59 GMT+0530")
);
Ok(())
}
Loading…
Cancel
Save