mod calendar;
mod duration;
mod error;
mod instant;
mod now;
mod options;
mod plain_date;
mod plain_date_time;
mod plain_month_day;
mod plain_time;
mod plain_year_month;
mod time_zone;
mod zoned_date_time;
#[cfg(test)]
mod tests;
pub use self::{
duration::*, instant::*, now::*, plain_date::*, plain_date_time::*, plain_month_day::*,
plain_time::*, plain_year_month::*, zoned_date_time::*,
};
use crate::{
builtins::{iterable::IteratorRecord, BuiltInBuilder, BuiltInObject, IntrinsicObject},
context::intrinsics::Intrinsics,
js_string,
property::Attribute,
realm::Realm,
string::StaticJsStrings,
value::Type,
Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_profiler::Profiler;
use temporal_rs::{PlainDate as TemporalDate, ZonedDateTime as TemporalZonedDateTime, NS_PER_DAY};
pub(crate) fn ns_max_instant() -> JsBigInt {
JsBigInt::from(i128::from(NS_PER_DAY) * 100_000_000_i128)
}
pub(crate) fn ns_min_instant() -> JsBigInt {
JsBigInt::from(i128::from(NS_PER_DAY) * -100_000_000_i128)
}
#[allow(unused)]
pub(crate) enum DateTimeValues {
Year,
Month,
MonthCode,
Week,
Day,
Hour,
Minute,
Second,
Millisecond,
Microsecond,
Nanosecond,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Temporal;
impl BuiltInObject for Temporal {
const NAME: JsString = StaticJsStrings::TEMPORAL;
}
impl IntrinsicObject for Temporal {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event(std::any::type_name::<Self>(), "init");
BuiltInBuilder::with_intrinsic::<Self>(realm)
.static_property(
JsSymbol::to_string_tag(),
Self::NAME,
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("Now"),
realm.intrinsics().objects().now(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("Calendar"),
realm.intrinsics().constructors().calendar().constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("Duration"),
realm.intrinsics().constructors().duration().constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("Instant"),
realm.intrinsics().constructors().instant().constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("PlainDate"),
realm.intrinsics().constructors().plain_date().constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("PlainDateTime"),
realm
.intrinsics()
.constructors()
.plain_date_time()
.constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("PlainMonthDay"),
realm
.intrinsics()
.constructors()
.plain_month_day()
.constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("PlainTime"),
realm.intrinsics().constructors().plain_time().constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("PlainYearMonth"),
realm
.intrinsics()
.constructors()
.plain_year_month()
.constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("TimeZone"),
realm.intrinsics().constructors().time_zone().constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.static_property(
js_string!("ZonedDateTime"),
realm
.intrinsics()
.constructors()
.zoned_date_time()
.constructor(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
intrinsics.objects().temporal()
}
}
fn to_zero_padded_decimal_string(n: u64, min_length: usize) -> String {
format!("{n:0min_length$}")
}
pub(crate) fn _iterator_to_list_of_types(
iterator: &mut IteratorRecord,
element_types: &[Type],
context: &mut Context,
) -> JsResult<Vec<JsValue>> {
let mut values = Vec::new();
while let Some(next) = iterator.step_value(context)? {
if element_types.contains(&next.get_type()) {
let completion = JsNativeError::typ()
.with_message("IteratorNext is not within allowed type values.");
let _never = iterator.close(Err(completion.into()), context)?;
}
values.push(next);
}
Ok(values)
}
type RelativeTemporalObjectResult = JsResult<(Option<TemporalDate>, Option<TemporalZonedDateTime>)>;
pub(crate) fn to_relative_temporal_object(
options: &JsObject,
context: &mut Context,
) -> RelativeTemporalObjectResult {
let relative_to = options.get(js_string!("relativeTo"), context)?;
let plain_date = match relative_to {
JsValue::String(relative_to_str) => JsValue::from(relative_to_str),
JsValue::Object(relative_to_obj) => JsValue::from(relative_to_obj),
JsValue::Undefined => return Ok((None, None)),
_ => {
return Err(JsNativeError::typ()
.with_message("Invalid type for converting to relativeTo object")
.into())
}
};
let plain_date = to_temporal_date(&plain_date, None, context)?;
Ok((Some(plain_date), None))
}
pub(crate) fn is_partial_temporal_object<'value>(
value: &'value JsValue,
context: &mut Context,
) -> JsResult<Option<&'value JsObject>> {
let Some(obj) = value.as_object() else {
return Ok(None);
};
if obj.is::<PlainDate>()
|| obj.is::<PlainDateTime>()
|| obj.is::<PlainMonthDay>()
|| obj.is::<PlainYearMonth>()
|| obj.is::<PlainTime>()
|| obj.is::<ZonedDateTime>()
{
return Ok(None);
}
let calendar_property = obj.get(js_string!("calendar"), context)?;
if !calendar_property.is_undefined() {
return Ok(None);
}
let time_zone_property = obj.get(js_string!("timeZone"), context)?;
if !time_zone_property.is_undefined() {
return Ok(None);
}
Ok(Some(obj))
}
#[inline]
#[allow(unused)]
pub(crate) fn to_positive_integer_with_trunc(
value: &JsValue,
context: &mut Context,
) -> JsResult<i32> {
let int = to_integer_with_truncation(value, context)?;
if int <= 0 {
return Err(JsNativeError::range()
.with_message("value is not a positive integer")
.into());
}
Ok(int)
}
#[inline]
pub(crate) fn to_integer_with_truncation(value: &JsValue, context: &mut Context) -> JsResult<i32> {
let number = value.to_number(context)?;
if number.is_nan() || number.is_infinite() {
return Err(JsNativeError::range()
.with_message("truncation target must be an integer.")
.into());
}
Ok(number.trunc() as i32)
}
#[inline]
pub(crate) fn to_integer_if_integral(arg: &JsValue, context: &mut Context) -> JsResult<i32> {
if !arg.is_integral_number() {
return Err(JsNativeError::range()
.with_message("value to convert is not an integral number.")
.into());
}
arg.to_i32(context)
}
fn extract_from_temporal_type<DF, DTF, YMF, MDF, ZDTF, Ret>(
object: &JsObject,
date_f: DF,
datetime_f: DTF,
year_month_f: YMF,
month_day_f: MDF,
zoned_datetime_f: ZDTF,
) -> JsResult<Option<Ret>>
where
DF: FnOnce(JsObject<PlainDate>) -> JsResult<Option<Ret>>,
DTF: FnOnce(JsObject<PlainDateTime>) -> JsResult<Option<Ret>>,
YMF: FnOnce(JsObject<PlainYearMonth>) -> JsResult<Option<Ret>>,
MDF: FnOnce(JsObject<PlainMonthDay>) -> JsResult<Option<Ret>>,
ZDTF: FnOnce(JsObject<ZonedDateTime>) -> JsResult<Option<Ret>>,
{
if let Ok(date) = object.clone().downcast::<PlainDate>() {
return date_f(date);
} else if let Ok(dt) = object.clone().downcast::<PlainDateTime>() {
return datetime_f(dt);
} else if let Ok(ym) = object.clone().downcast::<PlainYearMonth>() {
return year_month_f(ym);
} else if let Ok(md) = object.clone().downcast::<PlainMonthDay>() {
return month_day_f(md);
} else if let Ok(dt) = object.clone().downcast::<ZonedDateTime>() {
return zoned_datetime_f(dt);
}
Ok(None)
}