mirror of https://github.com/boa-dev/boa.git
Browse Source
* Remove `Temporal.Calendar` and `Temporal.TimeZone` * bump test262 * add new test262 features * fix bugpull/3895/head
José Julián Espina
4 months ago
committed by
GitHub
22 changed files with 169 additions and 2785 deletions
File diff suppressed because it is too large
Load Diff
@ -1,860 +0,0 @@
|
||||
//! Boa's implementation of a user-defined Anonymous Calendar.
|
||||
|
||||
use crate::{ |
||||
builtins::{ |
||||
iterable::IteratorHint, |
||||
temporal::{ |
||||
fields::object_to_temporal_fields, plain_date, plain_date_time, plain_month_day, |
||||
plain_year_month, |
||||
}, |
||||
Array, |
||||
}, |
||||
property::PropertyKey, |
||||
Context, JsObject, JsString, JsValue, |
||||
}; |
||||
|
||||
use boa_macros::js_str; |
||||
use num_traits::ToPrimitive; |
||||
use plain_date::PlainDate; |
||||
use plain_date_time::PlainDateTime; |
||||
use plain_month_day::PlainMonthDay; |
||||
use plain_year_month::PlainYearMonth; |
||||
use temporal_rs::{ |
||||
components::{ |
||||
calendar::{CalendarDateLike, CalendarProtocol}, |
||||
Date, Duration, MonthDay, YearMonth, |
||||
}, |
||||
options::ArithmeticOverflow, |
||||
TemporalError, TemporalFields, TemporalResult, TinyAsciiStr, |
||||
}; |
||||
|
||||
impl CalendarProtocol for JsObject { |
||||
type Date = JsObject<PlainDate>; |
||||
type DateTime = JsObject<PlainDateTime>; |
||||
type YearMonth = JsObject<PlainYearMonth>; |
||||
type MonthDay = JsObject<PlainMonthDay>; |
||||
type Context = Context; |
||||
fn date_from_fields( |
||||
&self, |
||||
fields: &mut TemporalFields, |
||||
overflow: ArithmeticOverflow, |
||||
context: &mut Context, |
||||
) -> TemporalResult<Date<Self>> { |
||||
let method = self |
||||
.get(js_str!("dateFromFields"), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let fields = JsObject::from_temporal_fields(fields, context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let overflow_obj = JsObject::with_null_proto(); |
||||
|
||||
overflow_obj |
||||
.create_data_property_or_throw( |
||||
js_str!("overflow"), |
||||
JsString::from(overflow.to_string()), |
||||
context, |
||||
) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let value = method |
||||
.as_callable() |
||||
.ok_or_else(|| { |
||||
TemporalError::general("dateFromFields must be implemented as a callable method.") |
||||
})? |
||||
.call( |
||||
&self.clone().into(), |
||||
&[fields.into(), overflow_obj.into()], |
||||
context, |
||||
) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let obj = value.as_object().map(JsObject::borrow).ok_or_else(|| { |
||||
TemporalError::r#type() |
||||
.with_message("datefromFields must return a valid PlainDate object.") |
||||
})?; |
||||
|
||||
let pd = obj.downcast_ref::<PlainDate>().ok_or_else(|| { |
||||
TemporalError::r#type().with_message("Object returned was not a PlainDate") |
||||
})?; |
||||
|
||||
Ok(pd.inner.clone()) |
||||
} |
||||
|
||||
fn year_month_from_fields( |
||||
&self, |
||||
fields: &mut TemporalFields, |
||||
overflow: ArithmeticOverflow, |
||||
context: &mut Context, |
||||
) -> TemporalResult<YearMonth<JsObject>> { |
||||
let method = self |
||||
.get(js_str!("yearMonthFromFields"), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let fields = JsObject::from_temporal_fields(fields, context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let overflow_obj = JsObject::with_null_proto(); |
||||
|
||||
overflow_obj |
||||
.create_data_property_or_throw( |
||||
js_str!("overflow"), |
||||
JsString::from(overflow.to_string()), |
||||
context, |
||||
) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let value = method |
||||
.as_callable() |
||||
.ok_or_else(|| { |
||||
TemporalError::general( |
||||
"yearMonthFromFields must be implemented as a callable method.", |
||||
) |
||||
})? |
||||
.call( |
||||
&self.clone().into(), |
||||
&[fields.into(), overflow_obj.into()], |
||||
context, |
||||
) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let obj = value.as_object().map(JsObject::borrow).ok_or_else(|| { |
||||
TemporalError::r#type() |
||||
.with_message("yearMonthFromFields must return a valid PlainYearMonth object.") |
||||
})?; |
||||
|
||||
let ym = obj.downcast_ref::<PlainYearMonth>().ok_or_else(|| { |
||||
TemporalError::r#type().with_message("Object returned was not a PlainDate") |
||||
})?; |
||||
|
||||
Ok(ym.inner.clone()) |
||||
} |
||||
|
||||
fn month_day_from_fields( |
||||
&self, |
||||
fields: &mut TemporalFields, |
||||
overflow: ArithmeticOverflow, |
||||
context: &mut Context, |
||||
) -> TemporalResult<MonthDay<JsObject>> { |
||||
let method = self |
||||
.get(js_str!("yearMonthFromFields"), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let fields = JsObject::from_temporal_fields(fields, context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let overflow_obj = JsObject::with_null_proto(); |
||||
|
||||
overflow_obj |
||||
.create_data_property_or_throw( |
||||
js_str!("overflow"), |
||||
JsString::from(overflow.to_string()), |
||||
context, |
||||
) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let value = method |
||||
.as_callable() |
||||
.ok_or_else(|| { |
||||
TemporalError::general( |
||||
"yearMonthFromFields must be implemented as a callable method.", |
||||
) |
||||
})? |
||||
.call( |
||||
&self.clone().into(), |
||||
&[fields.into(), overflow_obj.into()], |
||||
context, |
||||
) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let obj = value.as_object().map(JsObject::borrow).ok_or_else(|| { |
||||
TemporalError::r#type() |
||||
.with_message("yearMonthFromFields must return a valid PlainYearMonth object.") |
||||
})?; |
||||
|
||||
let md = obj.downcast_ref::<PlainMonthDay>().ok_or_else(|| { |
||||
TemporalError::r#type().with_message("Object returned was not a PlainDate") |
||||
})?; |
||||
|
||||
Ok(md.inner.clone()) |
||||
} |
||||
|
||||
fn date_add( |
||||
&self, |
||||
_date: &Date<JsObject>, |
||||
_duration: &Duration, |
||||
_overflow: ArithmeticOverflow, |
||||
_context: &mut Context, |
||||
) -> TemporalResult<Date<JsObject>> { |
||||
// TODO
|
||||
Err(TemporalError::general("Not yet implemented.")) |
||||
} |
||||
|
||||
fn date_until( |
||||
&self, |
||||
_one: &Date<JsObject>, |
||||
_two: &Date<JsObject>, |
||||
_largest_unit: temporal_rs::options::TemporalUnit, |
||||
_context: &mut Context, |
||||
) -> TemporalResult<Duration> { |
||||
// TODO
|
||||
Err(TemporalError::general("Not yet implemented.")) |
||||
} |
||||
|
||||
fn era( |
||||
&self, |
||||
_: &CalendarDateLike<JsObject>, |
||||
_: &mut Context, |
||||
) -> TemporalResult<Option<TinyAsciiStr<16>>> { |
||||
// Return undefined as custom calendars do not implement -> Currently.
|
||||
Ok(None) |
||||
} |
||||
|
||||
fn era_year( |
||||
&self, |
||||
_: &CalendarDateLike<JsObject>, |
||||
_: &mut Context, |
||||
) -> TemporalResult<Option<i32>> { |
||||
// Return undefined as custom calendars do not implement -> Currently.
|
||||
Ok(None) |
||||
} |
||||
|
||||
fn year( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<i32> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("year")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("year must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("year return must be integral.")); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err(TemporalError::r#type().with_message("year return must be larger than 1.")); |
||||
} |
||||
|
||||
let result = number |
||||
.to_i32() |
||||
.ok_or_else(|| TemporalError::range().with_message("year exceeded a valid range."))?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn month( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u8> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("month")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("month must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("month return must be integral.")); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err(TemporalError::r#type().with_message("month return must be larger than 1.")); |
||||
} |
||||
|
||||
let result = number |
||||
.to_u8() |
||||
.ok_or_else(|| TemporalError::range().with_message("month exceeded a valid range."))?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn month_code( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<TinyAsciiStr<4>> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("monthCode")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
let JsValue::String(result) = val else { |
||||
return Err(TemporalError::r#type().with_message("monthCode return must be a String.")); |
||||
}; |
||||
|
||||
let result = TinyAsciiStr::<4>::from_str(&result.to_std_string_escaped()) |
||||
.map_err(|_| TemporalError::general("Unexpected monthCode value."))?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn day( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u8> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("day")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("day must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("day return must be integral.")); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err(TemporalError::r#type().with_message("day return must be larger than 1.")); |
||||
} |
||||
|
||||
let result = number |
||||
.to_u8() |
||||
.ok_or_else(|| TemporalError::range().with_message("day exceeded a valid range."))?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn day_of_week( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u16> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("dayOfWeek")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("DayOfWeek must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("DayOfWeek return must be integral.")); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("DayOfWeek return must be larger than 1.") |
||||
); |
||||
} |
||||
|
||||
let result = number.to_u16().ok_or_else(|| { |
||||
TemporalError::range().with_message("DayOfWeek exceeded valid range.") |
||||
})?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn day_of_year( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u16> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("dayOfYear")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("dayOfYear must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("dayOfYear return must be integral.")); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("dayOfYear return must be larger than 1.") |
||||
); |
||||
} |
||||
|
||||
let result = number.to_u16().ok_or_else(|| { |
||||
TemporalError::range().with_message("dayOfYear exceeded valid range.") |
||||
})?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn week_of_year( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u16> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("weekOfYear")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("weekOfYear must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("weekOfYear return must be integral.")); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("weekOfYear return must be larger than 1.") |
||||
); |
||||
} |
||||
|
||||
let result = number.to_u16().ok_or_else(|| { |
||||
TemporalError::range().with_message("weekOfYear exceeded valid range.") |
||||
})?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn year_of_week( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<i32> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("yearOfWeek")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("yearOfWeek must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("yearOfWeek return must be integral.")); |
||||
} |
||||
|
||||
let result = number.to_i32().ok_or_else(|| { |
||||
TemporalError::range().with_message("yearOfWeek exceeded valid range.") |
||||
})?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn days_in_week( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u16> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("daysInWeek")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("daysInWeek must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("daysInWeek return must be integral.")); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("daysInWeek return must be larger than 1.") |
||||
); |
||||
} |
||||
|
||||
let result = number.to_u16().ok_or_else(|| { |
||||
TemporalError::range().with_message("daysInWeek exceeded valid range.") |
||||
})?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn days_in_month( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u16> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("daysInMonth")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("daysInMonth must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("daysInMonth return must be integral.") |
||||
); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("daysInMonth return must be larger than 1.") |
||||
); |
||||
} |
||||
|
||||
let result = number.to_u16().ok_or_else(|| { |
||||
TemporalError::range().with_message("daysInMonth exceeded valid range.") |
||||
})?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn days_in_year( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u16> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("daysInYear")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("daysInYear must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err(TemporalError::r#type().with_message("daysInYear return must be integral.")); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("daysInYear return must be larger than 1.") |
||||
); |
||||
} |
||||
|
||||
let result = number.to_u16().ok_or_else(|| { |
||||
TemporalError::range().with_message("monthsInYear exceeded valid range.") |
||||
})?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn months_in_year( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<u16> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("monthsInYear")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
// Validate the return value.
|
||||
// 3. If Type(result) is not Number, throw a TypeError exception.
|
||||
// 4. If IsIntegralNumber(result) is false, throw a RangeError exception.
|
||||
// 5. If result < 1𝔽, throw a RangeError exception.
|
||||
// 6. Return ℝ(result).
|
||||
|
||||
let Some(number) = val.as_number() else { |
||||
return Err(TemporalError::r#type().with_message("monthsInYear must return a number.")); |
||||
}; |
||||
|
||||
if !number.is_finite() || number.fract() != 0.0 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("monthsInYear return must be integral.") |
||||
); |
||||
} |
||||
|
||||
if number < 1f64 { |
||||
return Err( |
||||
TemporalError::r#type().with_message("monthsInYear return must be larger than 1.") |
||||
); |
||||
} |
||||
|
||||
let result = number.to_u16().ok_or_else(|| { |
||||
TemporalError::range().with_message("monthsInYear exceeded valid range.") |
||||
})?; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn in_leap_year( |
||||
&self, |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<bool> { |
||||
let date_like = date_like_to_object(date_like, context)?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("inLeapYear")), context) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let val = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[date_like], context) |
||||
.map_err(|err| TemporalError::general(err.to_string()))?; |
||||
|
||||
let JsValue::Boolean(result) = val else { |
||||
return Err( |
||||
TemporalError::r#type().with_message("inLeapYear must return a valid boolean.") |
||||
); |
||||
}; |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn fields(&self, fields: Vec<String>, context: &mut Context) -> TemporalResult<Vec<String>> { |
||||
let fields_js = Array::create_array_from_list( |
||||
fields.iter().map(|s| JsString::from(s.clone()).into()), |
||||
context, |
||||
); |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("fields")), context) |
||||
.expect("method must exist on an object that implements the CalendarProtocol."); |
||||
|
||||
let result = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&self.clone().into(), &[fields_js.into()], context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
// validate result and map to a `Vec<String>`
|
||||
let mut iterator = result |
||||
.get_iterator(context, Some(IteratorHint::Sync), None) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let mut result = Vec::default(); |
||||
while iterator |
||||
.step(context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))? |
||||
{ |
||||
let next_value = iterator |
||||
.value(context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let JsValue::String(s) = next_value else { |
||||
return Err(TemporalError::r#type() |
||||
.with_message("Invalid return type in fields method implementation.")); |
||||
}; |
||||
|
||||
result.push(s.to_std_string_escaped()); |
||||
} |
||||
|
||||
Ok(result) |
||||
} |
||||
|
||||
fn merge_fields( |
||||
&self, |
||||
fields: &TemporalFields, |
||||
additional_fields: &TemporalFields, |
||||
context: &mut Context, |
||||
) -> TemporalResult<TemporalFields> { |
||||
let fields = JsObject::from_temporal_fields(fields, context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
let add_fields = JsObject::from_temporal_fields(additional_fields, context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let method = self |
||||
.get(PropertyKey::from(js_str!("mergeFields")), context) |
||||
.expect("method must exist on an object that implements the CalendarProtocol."); |
||||
|
||||
let value = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call( |
||||
&self.clone().into(), |
||||
&[fields.into(), add_fields.into()], |
||||
context, |
||||
) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
let JsValue::Object(o) = value else { |
||||
return Err( |
||||
TemporalError::r#type().with_message("mergeFields did not return an object.") |
||||
); |
||||
}; |
||||
|
||||
object_to_temporal_fields(&o, context).map_err(|e| TemporalError::general(e.to_string())) |
||||
} |
||||
|
||||
fn identifier(&self, context: &mut Context) -> TemporalResult<String> { |
||||
let identifier = self |
||||
.__get__( |
||||
&PropertyKey::from(js_str!("id")), |
||||
self.clone().into(), |
||||
&mut context.into(), |
||||
) |
||||
.expect("method must exist on a object that implements the CalendarProtocol."); |
||||
|
||||
let JsValue::String(s) = identifier else { |
||||
return Err(TemporalError::range().with_message("Identifier was not a string")); |
||||
}; |
||||
|
||||
Ok(s.to_std_string_escaped()) |
||||
} |
||||
} |
||||
|
||||
/// Utility function for converting `Temporal`'s `CalendarDateLike` to it's `Boa` specific `JsObject`.
|
||||
pub(crate) fn date_like_to_object( |
||||
date_like: &CalendarDateLike<JsObject>, |
||||
context: &mut Context, |
||||
) -> TemporalResult<JsValue> { |
||||
match date_like { |
||||
CalendarDateLike::Date(d) => plain_date::create_temporal_date(d.clone(), None, context) |
||||
.map_err(|e| TemporalError::general(e.to_string())) |
||||
.map(Into::into), |
||||
CalendarDateLike::DateTime(dt) => { |
||||
plain_date_time::create_temporal_datetime(dt.clone(), None, context) |
||||
.map_err(|e| TemporalError::general(e.to_string())) |
||||
.map(Into::into) |
||||
} |
||||
CalendarDateLike::CustomMonthDay(md) => Ok(md.clone().upcast().into()), |
||||
CalendarDateLike::CustomYearMonth(ym) => Ok(ym.clone().upcast().into()), |
||||
CalendarDateLike::CustomDate(pd) => Ok(pd.clone().upcast().into()), |
||||
CalendarDateLike::CustomDateTime(pdt) => Ok(pdt.clone().upcast().into()), |
||||
} |
||||
} |
@ -1,61 +0,0 @@
|
||||
use crate::{js_string, run_test_actions, TestAction}; |
||||
|
||||
#[test] |
||||
fn calendar_constructor() { |
||||
// TODO: Add other BuiltinCalendars
|
||||
run_test_actions([TestAction::assert_eq( |
||||
"new Temporal.Calendar('iso8601').id", |
||||
js_string!("iso8601"), |
||||
)]); |
||||
} |
||||
|
||||
#[test] |
||||
fn calendar_methods() { |
||||
run_test_actions([ |
||||
TestAction::run("let iso = new Temporal.Calendar('iso8601');"), |
||||
TestAction::assert_eq("iso.inLeapYear('2020-11-20')", true), |
||||
TestAction::assert_eq("iso.daysInYear('2020-11-20')", 366), |
||||
TestAction::assert_eq("iso.daysInYear('2021-11-20')", 365), |
||||
TestAction::assert_eq("iso.monthsInYear('2021-11-20')", 12), |
||||
TestAction::assert_eq("iso.daysInWeek('2021-11-20')", 7), |
||||
]); |
||||
} |
||||
|
||||
#[test] |
||||
fn run_custom_calendar() { |
||||
run_test_actions([ |
||||
TestAction::run( |
||||
r#"const custom = { |
||||
dateAdd() {}, |
||||
dateFromFields() {}, |
||||
dateUntil() {}, |
||||
day() {}, |
||||
dayOfWeek() {}, |
||||
dayOfYear() {}, |
||||
daysInMonth() { return 14 }, |
||||
daysInWeek() {return 6}, |
||||
daysInYear() {return 360}, |
||||
fields() {}, |
||||
id: "custom-calendar", |
||||
inLeapYear() {}, |
||||
mergeFields() {}, |
||||
month() {}, |
||||
monthCode() {}, |
||||
monthDayFromFields() {}, |
||||
monthsInYear() {}, |
||||
weekOfYear() {}, |
||||
year() {}, |
||||
yearMonthFromFields() {}, |
||||
yearOfWeek() {}, |
||||
}; |
||||
|
||||
let cal = Temporal.Calendar.from(custom); |
||||
let date = "1972-05-01"; |
||||
"#, |
||||
), |
||||
TestAction::assert_eq("cal.id", js_string!("custom-calendar")), |
||||
TestAction::assert_eq("cal.daysInMonth(date)", 14), |
||||
TestAction::assert_eq("cal.daysInWeek(date)", 6), |
||||
TestAction::assert_eq("cal.daysInYear(date)", 360), |
||||
]); |
||||
} |
@ -1,250 +0,0 @@
|
||||
//! A Rust native implementation of the `fields` object used in `Temporal`.
|
||||
|
||||
use std::str::FromStr; |
||||
|
||||
use crate::{ |
||||
js_string, object::internal_methods::InternalMethodContext, property::PropertyKey, |
||||
value::PreferredType, Context, JsNativeError, JsObject, JsResult, JsString, JsValue, |
||||
}; |
||||
|
||||
use rustc_hash::FxHashSet; |
||||
|
||||
use temporal_rs::fields::{FieldConversion, FieldValue, TemporalFields}; |
||||
|
||||
use super::{to_integer_with_truncation, to_positive_integer_with_trunc}; |
||||
|
||||
// TODO: Move extended and required fields into the temporal library?
|
||||
/// `PrepareTemporalFeilds`
|
||||
pub(crate) fn prepare_temporal_fields( |
||||
fields: &JsObject, |
||||
field_names: &mut Vec<JsString>, |
||||
required_fields: &mut Vec<JsString>, |
||||
extended_fields: Option<Vec<(String, bool)>>, |
||||
partial: bool, |
||||
dup_behaviour: Option<JsString>, |
||||
context: &mut Context, |
||||
) -> JsResult<TemporalFields> { |
||||
// 1. If duplicateBehaviour is not present, set duplicateBehaviour to throw.
|
||||
let dup_option = dup_behaviour.unwrap_or_else(|| js_string!("throw")); |
||||
|
||||
// 2. Let result be OrdinaryObjectCreate(null).
|
||||
let mut result = TemporalFields::default(); |
||||
|
||||
// 3. Let any be false.
|
||||
let mut any = false; |
||||
// 4. If extraFieldDescriptors is present, then
|
||||
if let Some(extra_fields) = extended_fields { |
||||
for (field_name, required) in extra_fields { |
||||
// a. For each Calendar Field Descriptor Record desc of extraFieldDescriptors, do
|
||||
// i. Assert: fieldNames does not contain desc.[[Property]].
|
||||
// ii. Append desc.[[Property]] to fieldNames.
|
||||
field_names.push(JsString::from(field_name.clone())); |
||||
|
||||
// iii. If desc.[[Required]] is true and requiredFields is a List, then
|
||||
if required && !partial { |
||||
// 1. Append desc.[[Property]] to requiredFields.
|
||||
required_fields.push(JsString::from(field_name)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 5. Let sortedFieldNames be SortStringListByCodeUnit(fieldNames).
|
||||
// 6. Let previousProperty be undefined.
|
||||
let mut dups_map = FxHashSet::default(); |
||||
|
||||
// 7. For each property name property of sortedFieldNames, do
|
||||
for field in &*field_names { |
||||
// a. If property is one of "constructor" or "__proto__", then
|
||||
if field.to_std_string_escaped().as_str() == "constructor" |
||||
|| field.to_std_string_escaped().as_str() == "__proto__" |
||||
{ |
||||
// i. Throw a RangeError exception.
|
||||
return Err(JsNativeError::range() |
||||
.with_message("constructor or proto is out of field range.") |
||||
.into()); |
||||
} |
||||
|
||||
let new_value = dups_map.insert(field); |
||||
|
||||
// b. If property is not equal to previousProperty, then
|
||||
if new_value { |
||||
// i. Let value be ? Get(fields, property).
|
||||
let value = fields.get(PropertyKey::from(field.clone()), context)?; |
||||
// ii. If value is not undefined, then
|
||||
if !value.is_undefined() { |
||||
// 1. Set any to true.
|
||||
any = true; |
||||
|
||||
// 2. If property is in the Property column of Table 17 and there is a Conversion value in the same row, then
|
||||
// a. Let Conversion be the Conversion value of the same row.
|
||||
|
||||
// TODO: Conversion from TemporalError -> JsError
|
||||
let conversion = FieldConversion::from_str(field.to_std_string_escaped().as_str()) |
||||
.map_err(|_| JsNativeError::range().with_message("wrong field value"))?; |
||||
// b. If Conversion is ToIntegerWithTruncation, then
|
||||
let converted_value = match conversion { |
||||
FieldConversion::ToIntegerWithTruncation => { |
||||
// i. Set value to ? ToIntegerWithTruncation(value).
|
||||
let v = to_integer_with_truncation(&value, context)?; |
||||
// ii. Set value to 𝔽(value).
|
||||
FieldValue::Integer(v) |
||||
} |
||||
// c. Else if Conversion is ToPositiveIntegerWithTruncation, then
|
||||
FieldConversion::ToPositiveIntegerWithTruncation => { |
||||
// i. Set value to ? ToPositiveIntegerWithTruncation(value).
|
||||
let v = to_positive_integer_with_trunc(&value, context)?; |
||||
// ii. Set value to 𝔽(value).
|
||||
FieldValue::Integer(v) |
||||
} |
||||
// d. Else,
|
||||
// i. Assert: Conversion is ToPrimitiveAndRequireString.
|
||||
FieldConversion::ToPrimativeAndRequireString => { |
||||
// ii. NOTE: Non-primitive values are supported here for consistency with other fields, but such values must coerce to Strings.
|
||||
// iii. Set value to ? ToPrimitive(value, string).
|
||||
let primitive = value.to_primitive(context, PreferredType::String)?; |
||||
// iv. If value is not a String, throw a TypeError exception.
|
||||
FieldValue::String(primitive.to_string(context)?.to_std_string_escaped()) |
||||
} |
||||
FieldConversion::None => { |
||||
unreachable!("todo need to implement conversion handling for tz.") |
||||
} |
||||
}; |
||||
|
||||
// 3. Perform ! CreateDataPropertyOrThrow(result, property, value).
|
||||
result |
||||
.set_field_value(&field.to_std_string_escaped(), &converted_value) |
||||
.expect("FieldConversion enforces the appropriate type"); |
||||
// iii. Else if requiredFields is a List, then
|
||||
} else if !partial { |
||||
// 1. If requiredFields contains property, then
|
||||
if required_fields.contains(field) { |
||||
// a. Throw a TypeError exception.
|
||||
return Err(JsNativeError::typ() |
||||
.with_message("A required TemporalField was not provided.") |
||||
.into()); |
||||
} |
||||
|
||||
// NOTE: flag that the value is active and the default should be used.
|
||||
// 2. If property is in the Property column of Table 17, then
|
||||
// a. Set value to the corresponding Default value of the same row.
|
||||
// 3. Perform ! CreateDataPropertyOrThrow(result, property, value).
|
||||
result.require_field(&field.to_std_string_escaped()); |
||||
} |
||||
// c. Else if duplicateBehaviour is throw, then
|
||||
} else if dup_option.to_std_string_escaped() == "throw" { |
||||
// i. Throw a RangeError exception.
|
||||
return Err(JsNativeError::range() |
||||
.with_message("Cannot have a duplicate field") |
||||
.into()); |
||||
} |
||||
// d. Set previousProperty to property.
|
||||
} |
||||
|
||||
// 8. If requiredFields is partial and any is false, then
|
||||
if partial && !any { |
||||
// a. Throw a TypeError exception.
|
||||
return Err(JsNativeError::range() |
||||
.with_message("requiredFields cannot be partial when any is false") |
||||
.into()); |
||||
} |
||||
|
||||
// 9. Return result.
|
||||
Ok(result) |
||||
} |
||||
|
||||
// NOTE(nekevss): The below serves as a replacement for `Snapshot` on `Calendar.prototype.mergeFields`.
|
||||
//
|
||||
// Some potential issues here: `Calendar.prototype.mergeFields` appears to allow extra fields that
|
||||
// are not part of a `TemporalFields` record; however, the specification only calls `mergeFields` on an
|
||||
// object returned by `PrepareTemporalFields`, so the translation should be fine sound.
|
||||
//
|
||||
// The restriction/trade-off would occur if someone wanted to include non-normative calendar fields (i.e. something
|
||||
// not accounted for in the specification) in a Custom Calendar or use `Calendar.prototype.mergeFields` in
|
||||
// general as a way to merge two objects.
|
||||
pub(crate) fn object_to_temporal_fields( |
||||
source: &JsObject, |
||||
context: &mut Context, |
||||
) -> JsResult<TemporalFields> { |
||||
// Adapted from `CopyDataProperties` with ExcludedKeys -> << >> && ExcludedValues -> << Undefined >>
|
||||
const VALID_FIELDS: [&str; 14] = [ |
||||
"year", |
||||
"month", |
||||
"monthCode", |
||||
"day", |
||||
"hour", |
||||
"minute", |
||||
"second", |
||||
"millisecond", |
||||
"microsecond", |
||||
"nanosecond", |
||||
"offset", |
||||
"timeZone", |
||||
"era", |
||||
"eraYear", |
||||
]; |
||||
let mut copy = TemporalFields::default(); |
||||
|
||||
let keys = source.__own_property_keys__(context)?; |
||||
|
||||
for key in &keys { |
||||
let desc = source.__get_own_property__(key, &mut InternalMethodContext::new(context))?; |
||||
match desc { |
||||
// Enforce that the `PropertyKey` is a valid field here.
|
||||
Some(desc) |
||||
if desc.expect_enumerable() && VALID_FIELDS.contains(&key.to_string().as_str()) => |
||||
{ |
||||
let value = source.get(key.clone(), context)?; |
||||
// Below is repurposed from `PrepareTemporalFields`.
|
||||
if !value.is_undefined() { |
||||
let conversion = FieldConversion::from_str(&key.to_string())?; |
||||
let converted_value = match conversion { |
||||
FieldConversion::ToIntegerWithTruncation => { |
||||
let v = to_integer_with_truncation(&value, context)?; |
||||
FieldValue::Integer(v) |
||||
} |
||||
FieldConversion::ToPositiveIntegerWithTruncation => { |
||||
let v = to_positive_integer_with_trunc(&value, context)?; |
||||
FieldValue::Integer(v) |
||||
} |
||||
FieldConversion::ToPrimativeAndRequireString => { |
||||
let primitive = value.to_primitive(context, PreferredType::String)?; |
||||
FieldValue::String( |
||||
primitive.to_string(context)?.to_std_string_escaped(), |
||||
) |
||||
} |
||||
FieldConversion::None => { |
||||
unreachable!("todo need to implement conversion handling for tz.") |
||||
} |
||||
}; |
||||
// TODO: Test the below further and potentially expand handling.
|
||||
copy.set_field_value(&key.to_string(), &converted_value) |
||||
.expect("FieldConversion enforces the appropriate type"); |
||||
} |
||||
} |
||||
_ => {} |
||||
}; |
||||
} |
||||
|
||||
Ok(copy) |
||||
} |
||||
|
||||
impl JsObject { |
||||
pub(crate) fn from_temporal_fields( |
||||
fields: &TemporalFields, |
||||
context: &mut Context, |
||||
) -> JsResult<Self> { |
||||
let obj = JsObject::with_null_proto(); |
||||
|
||||
for (key, value) in fields.active_kvs() { |
||||
let js_value = match value { |
||||
FieldValue::Undefined => JsValue::undefined(), |
||||
FieldValue::Integer(x) => JsValue::Integer(x), |
||||
FieldValue::String(s) => JsValue::String(s.into()), |
||||
}; |
||||
|
||||
obj.create_data_property_or_throw(JsString::from(key), js_value, context)?; |
||||
} |
||||
|
||||
Ok(obj) |
||||
} |
||||
} |
@ -1,63 +0,0 @@
|
||||
//! A custom `TimeZone` object.
|
||||
use crate::{property::PropertyKey, Context, JsObject, JsValue}; |
||||
|
||||
use boa_gc::{Finalize, Trace}; |
||||
use boa_macros::js_str; |
||||
use num_bigint::BigInt; |
||||
use temporal_rs::{ |
||||
components::{tz::TzProtocol, Instant}, |
||||
TemporalError, TemporalResult, |
||||
}; |
||||
|
||||
#[derive(Debug, Clone, Trace, Finalize)] |
||||
pub(crate) struct JsCustomTimeZone { |
||||
tz: JsObject, |
||||
} |
||||
|
||||
impl TzProtocol for JsCustomTimeZone { |
||||
type Context = Context; |
||||
fn get_offset_nanos_for(&self, context: &mut Context) -> TemporalResult<BigInt> { |
||||
let method = self |
||||
.tz |
||||
.get(js_str!("getOffsetNanosFor"), context) |
||||
.expect("Method must exist for the custom calendar to be valid."); |
||||
|
||||
let result = method |
||||
.as_callable() |
||||
.expect("is method") |
||||
.call(&method, &[], context) |
||||
.map_err(|e| TemporalError::general(e.to_string()))?; |
||||
|
||||
// TODO (nekevss): Validate that the below conversion is fine vs. matching to JsValue::BigInt()
|
||||
let Some(bigint) = result.as_bigint() else { |
||||
return Err(TemporalError::r#type() |
||||
.with_message("Expected BigInt return from getOffsetNanosFor")); |
||||
}; |
||||
|
||||
Ok(bigint.as_inner().clone()) |
||||
} |
||||
|
||||
fn get_possible_instant_for(&self, _context: &mut Context) -> TemporalResult<Vec<Instant>> { |
||||
// TODO: Implement once Instant has been migrated to `boa_temporal`'s Instant.
|
||||
Err(TemporalError::range().with_message("Not yet implemented.")) |
||||
} |
||||
|
||||
fn id(&self, context: &mut Context) -> TemporalResult<String> { |
||||
let ident = self |
||||
.tz |
||||
.__get__( |
||||
&PropertyKey::from(js_str!("id")), |
||||
JsValue::undefined(), |
||||
&mut context.into(), |
||||
) |
||||
.expect("Method must exist for the custom calendar to be valid."); |
||||
|
||||
let JsValue::String(id) = ident else { |
||||
return Err( |
||||
TemporalError::r#type().with_message("Invalid custom Time Zone identifier type.") |
||||
); |
||||
}; |
||||
|
||||
Ok(id.to_std_string_escaped()) |
||||
} |
||||
} |
Loading…
Reference in new issue