From ed32d15ea1dd9533dbb1e0778d315d58a9ea01f1 Mon Sep 17 00:00:00 2001 From: nekevss Date: Thu, 25 Jan 2024 16:25:19 -0500 Subject: [PATCH] New date methods and update builtin --- core/engine/src/builtins/temporal/now.rs | 2 +- .../src/builtins/temporal/plain_date/mod.rs | 237 +++++++++++++----- core/temporal/src/components/date.rs | 189 +++++++++++++- core/temporal/src/components/duration/date.rs | 2 +- core/temporal/src/iso.rs | 4 +- core/temporal/src/utils.rs | 2 +- 6 files changed, 356 insertions(+), 80 deletions(-) diff --git a/core/engine/src/builtins/temporal/now.rs b/core/engine/src/builtins/temporal/now.rs index d628884bf31..4e13a3424d8 100644 --- a/core/engine/src/builtins/temporal/now.rs +++ b/core/engine/src/builtins/temporal/now.rs @@ -67,7 +67,7 @@ impl Now { #[allow(clippy::unnecessary_wraps)] fn time_zone_id(_: &JsValue, _args: &[JsValue], context: &mut Context) -> JsResult { // 1. Return ! SystemTimeZone(). - Ok(system_time_zone(context).expect("retrieving the system timezone must not fail")) + system_time_zone(context) } /// `Temporal.Now.instant()` diff --git a/core/engine/src/builtins/temporal/plain_date/mod.rs b/core/engine/src/builtins/temporal/plain_date/mod.rs index 6b3d4b9832a..c558e0e1f35 100644 --- a/core/engine/src/builtins/temporal/plain_date/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date/mod.rs @@ -1,6 +1,8 @@ //! Boa's implementation of the ECMAScript `Temporal.PlainDate` builtin object. #![allow(dead_code, unused_variables)] +// TODO (nekevss): DOCS DOCS AND MORE DOCS + use crate::{ builtins::{ options::{get_option, get_options_object}, @@ -21,7 +23,7 @@ use boa_temporal::{ options::ArithmeticOverflow, }; -use super::{calendar, JsCustomCalendar, PlainDateTime, ZonedDateTime}; +use super::{calendar, create_temporal_calendar, JsCustomCalendar, PlainDateTime, ZonedDateTime}; /// The `Temporal.PlainDate` object. #[derive(Debug, Clone, Trace, Finalize, JsData)] @@ -229,90 +231,183 @@ impl BuiltInConstructor for PlainDate { } } -// -- `PlainDate` getter methods -- +// ==== `PlainDate` getter methods ==== + impl PlainDate { - fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) - } + /// 3.3.3 get `Temporal.PlainDate.prototype.calendarId` + fn get_calendar_id(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; - fn get_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + Ok(JsString::from(date.inner.calendar().identifier(context)?).into()) } - fn get_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + /// 3.3.4 get `Temporal.PlainDate.prototype.year` + fn get_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + Ok(date.inner.contextual_year(context)?.into()) } - fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + /// 3.3.5 get `Temporal.PlainDate.prototype.month` + fn get_month(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + Ok(date.inner.contextual_month(context)?.into()) + } + + /// 3.3.6 get Temporal.PlainDate.prototype.monthCode + fn get_month_code(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + Ok(JsString::from(date.inner.contextual_month_code(context)?.as_str()).into()) + } + + /// 3.3.7 get `Temporal.PlainDate.prototype.day` + fn get_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + Ok(date.inner.contextual_day(context)?.into()) + } + + /// 3.3.8 get `Temporal.PlainDate.prototype.dayOfWeek` + fn get_day_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + Ok(date.inner.contextual_day_of_week(context)?.into()) + } + + /// 3.3.9 get `Temporal.PlainDate.prototype.dayOfYear` + fn get_day_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + Ok(date.inner.contextual_day_of_year(context)?.into()) + } + + /// 3.3.10 get `Temporal.PlainDate.prototype.weekOfYear` + fn get_week_of_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + Ok(date.inner.contextual_week_of_year(context)?.into()) + } + + /// 3.3.11 get `Temporal.PlainDate.prototype.yearOfWeek` + fn get_year_of_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + Ok(date.inner.contextual_year_of_week(context)?.into()) } - fn get_day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } + /// 3.3.12 get `Temporal.PlainDate.prototype.daysInWeek` + fn get_days_in_week(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; - fn get_day_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + Ok(date.inner.contextual_days_in_week(context)?.into()) } - fn get_day_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } + /// 3.3.13 get `Temporal.PlainDate.prototype.daysInMonth` + fn get_days_in_month( + this: &JsValue, + _: &[JsValue], + context: &mut Context, + ) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; - fn get_week_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + Ok(date.inner.contextual_days_in_month(context)?.into()) } - fn get_year_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } + /// 3.3.14 get `Temporal.PlainDate.prototype.daysInYear` + fn get_days_in_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; - fn get_days_in_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + Ok(date.inner.contextual_days_in_year(context)?.into()) } - fn get_days_in_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } + /// 3.3.15 get `Temporal.PlainDate.prototype.monthsInYear` + fn get_months_in_year( + this: &JsValue, + _: &[JsValue], + context: &mut Context, + ) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; - fn get_days_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + Ok(date.inner.contextual_months_in_year(context)?.into()) } - fn get_months_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) - } + /// 3.3.16 get `Temporal.PlainDate.prototype.inLeapYear` + fn get_in_leap_year(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; - fn get_in_leap_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + Ok(date.inner.contextual_in_leap_year(context)?.into()) } } @@ -337,10 +432,16 @@ impl PlainDate { .into()) } - fn get_calendar(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("not yet implemented.") - .into()) + /// 3.3.20 `Temporal.PlainDate.prototype.getCalendar ( )` + fn get_calendar(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + let date = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be an instant object.") + })?; + + create_temporal_calendar(date.inner.calendar().clone(), None, context) } fn add(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { diff --git a/core/temporal/src/components/date.rs b/core/temporal/src/components/date.rs index b94ff6131d8..be918d72887 100644 --- a/core/temporal/src/components/date.rs +++ b/core/temporal/src/components/date.rs @@ -1,5 +1,7 @@ //! This module implements `Date` and any directly related algorithms. +use tinystr::TinyAsciiStr; + use crate::{ components::{ calendar::{CalendarProtocol, CalendarSlot}, @@ -72,29 +74,29 @@ impl Date { #[inline] #[must_use] - /// Returns this `Date`'s year value. - pub const fn year(&self) -> i32 { + /// Returns this `Date`'s ISO year value. + pub const fn iso_year(&self) -> i32 { self.iso.year() } #[inline] #[must_use] - /// Returns this `Date`'s month value. - pub const fn month(&self) -> u8 { + /// Returns this `Date`'s ISO month value. + pub const fn iso_month(&self) -> u8 { self.iso.month() } #[inline] #[must_use] - /// Returns this `Date`'s day value. - pub const fn day(&self) -> u8 { + /// Returns this `Date`'s ISO day value. + pub const fn iso_day(&self) -> u8 { self.iso.day() } #[inline] #[must_use] /// Returns the `Date`'s inner `IsoDate` record. - pub const fn iso_date(&self) -> IsoDate { + pub const fn iso(&self) -> IsoDate { self.iso } @@ -123,6 +125,179 @@ impl Date { } } +// ==== Calendar-derived Public API ==== + +impl Date { + /// Returns the calendar year value with provided context. + pub fn contextual_year(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.year( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar year value. + pub fn year(&self) -> TemporalResult { + self.contextual_year(&mut ()) + } + + /// Returns the calendar month value with provided context. + pub fn contextual_month(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.month( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar month value. + pub fn month(&self) -> TemporalResult { + self.contextual_month(&mut ()) + } + + /// Returns the calendar month code value with provided context. + pub fn contextual_month_code(&self, context: &mut dyn Any) -> TemporalResult> { + self.calendar.month_code( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar month code value. + pub fn month_code(&self) -> TemporalResult> { + self.contextual_month_code(&mut ()) + } + + /// Returns the calendar day value with provided context. + pub fn contextual_day(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.day( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar day value. + pub fn day(&self) -> TemporalResult { + self.contextual_day(&mut ()) + } + + /// Returns the calendar day of week value with provided context. + pub fn contextual_day_of_week(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.day_of_week( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar day of week value. + pub fn day_of_week(&self) -> TemporalResult { + self.contextual_day_of_week(&mut ()) + } + + /// Returns the calendar day of year value with provided context. + pub fn contextual_day_of_year(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.day_of_week( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar day of year value. + pub fn day_of_year(&self) -> TemporalResult { + self.contextual_day_of_week(&mut ()) + } + + /// Returns the calendar week of year value with provided context. + pub fn contextual_week_of_year(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.week_of_year( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar week of year value. + pub fn week_of_year(&self) -> TemporalResult { + self.contextual_week_of_year(&mut ()) + } + + /// Returns the calendar year of week value with provided context. + pub fn contextual_year_of_week(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.year_of_week( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar year of week value. + pub fn year_of_week(&self) -> TemporalResult { + self.contextual_year_of_week(&mut ()) + } + + /// Returns the calendar days in week value with provided context. + pub fn contextual_days_in_week(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.days_in_week( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar days in week value. + pub fn days_in_week(&self) -> TemporalResult { + self.contextual_days_in_week(&mut ()) + } + + /// Returns the calendar days in month value with provided context. + pub fn contextual_days_in_month(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.days_in_month( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar days in month value. + pub fn days_in_month(&self) -> TemporalResult { + self.contextual_days_in_month(&mut ()) + } + + /// Returns the calendar days in year value with provided context. + pub fn contextual_days_in_year(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.days_in_year( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar days in year value. + pub fn days_in_year(&self) -> TemporalResult { + self.contextual_days_in_year(&mut ()) + } + + /// Returns the calendar months in year value with provided context. + pub fn contextual_months_in_year(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.months_in_year( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns the calendar months in year value. + pub fn months_in_year(&self) -> TemporalResult { + self.contextual_months_in_year(&mut ()) + } + + /// Returns whether the date is in a leap year for the given calendar with provided context. + pub fn contextual_in_leap_year(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.in_leap_year( + &super::calendar::CalendarDateLike::Date(self.clone()), + context, + ) + } + + /// Returns returns whether the date in a leap year for the given calendar. + pub fn in_leap_year(&self) -> TemporalResult { + self.contextual_in_leap_year(&mut ()) + } +} + impl IsoDateSlots for Date { /// Returns the structs `IsoDate` fn iso_date(&self) -> IsoDate { diff --git a/core/temporal/src/components/duration/date.rs b/core/temporal/src/components/duration/date.rs index 8d169684377..e044aacfc07 100644 --- a/core/temporal/src/components/duration/date.rs +++ b/core/temporal/src/components/duration/date.rs @@ -212,7 +212,7 @@ impl DateDuration { fractional_days += f64::from(months_weeks_in_days); // k. Let isoResult be ! AddISODate(plainRelativeTo.[[ISOYear]]. plainRelativeTo.[[ISOMonth]], plainRelativeTo.[[ISODay]], 0, 0, 0, truncate(fractionalDays), "constrain"). - let iso_result = plain_relative_to.iso_date().add_iso_date( + let iso_result = plain_relative_to.iso().add_iso_date( &DateDuration::new_unchecked(0.0, 0.0, 0.0, fractional_days.trunc()), ArithmeticOverflow::Constrain, )?; diff --git a/core/temporal/src/iso.rs b/core/temporal/src/iso.rs index c9e6f6753e0..a11d897843d 100644 --- a/core/temporal/src/iso.rs +++ b/core/temporal/src/iso.rs @@ -171,11 +171,11 @@ impl IsoDate { ) -> TemporalResult { match overflow { ArithmeticOverflow::Constrain => { - let m = month.clamp(1, 12); + let month = month.clamp(1, 12); let days_in_month = utils::iso_days_in_month(year, month); let d = day.clamp(1, days_in_month); // NOTE: Values are clamped in a u8 range. - Ok(Self::new_unchecked(year, m as u8, d as u8)) + Ok(Self::new_unchecked(year, month as u8, d as u8)) } ArithmeticOverflow::Reject => { if !is_valid_date(year, month, day) { diff --git a/core/temporal/src/utils.rs b/core/temporal/src/utils.rs index 7e858e97c3e..d0abf2343ea 100644 --- a/core/temporal/src/utils.rs +++ b/core/temporal/src/utils.rs @@ -305,7 +305,7 @@ pub(crate) fn iso_days_in_month(year: i32, month: i32) -> i32 { 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, 4 | 6 | 9 | 11 => 30, 2 => 28 + mathematical_in_leap_year(epoch_time_for_year(year)), - _ => unreachable!("an invalid month value is an implementation error."), + _ => unreachable!("ISODaysInMonth panicking is an implementation error."), } }