Browse Source

Update tz components for new design (#3543)

pull/3569/head
Kevin 12 months ago committed by GitHub
parent
commit
0cb17cfc61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 76
      core/engine/src/builtins/temporal/time_zone/custom.rs
  2. 30
      core/engine/src/builtins/temporal/time_zone/mod.rs
  3. 28
      core/engine/src/builtins/temporal/zoned_date_time/mod.rs
  4. 6
      core/temporal/src/components/duration.rs
  5. 64
      core/temporal/src/components/tz.rs
  6. 33
      core/temporal/src/components/zoneddatetime.rs

76
core/engine/src/builtins/temporal/time_zone/custom.rs

@ -0,0 +1,76 @@
//! A custom `TimeZone` object.
use crate::{property::PropertyKey, string::utf16, Context, JsObject, JsValue};
use boa_gc::{Finalize, Trace};
use boa_temporal::{
components::{tz::TzProtocol, Instant},
TemporalError, TemporalResult,
};
use num_bigint::BigInt;
#[derive(Debug, Clone, Trace, Finalize)]
pub(crate) struct JsCustomTimeZone {
tz: JsObject,
}
impl TzProtocol for JsCustomTimeZone {
fn get_offset_nanos_for(&self, ctx: &mut dyn std::any::Any) -> TemporalResult<BigInt> {
let context = ctx
.downcast_mut::<Context>()
.expect("Context was not provided for a CustomTz");
let method = self
.tz
.get(utf16!("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,
ctx: &mut dyn std::any::Any,
) -> TemporalResult<Vec<Instant>> {
let _context = ctx
.downcast_mut::<Context>()
.expect("Context was not provided for a CustomTz");
// TODO: Implement once Instant has been migrated to `boa_temporal`'s Instant.
Err(TemporalError::range().with_message("Not yet implemented."))
}
fn id(&self, ctx: &mut dyn std::any::Any) -> TemporalResult<String> {
let context = ctx
.downcast_mut::<Context>()
.expect("Context was not provided for a CustomTz");
let ident = self
.tz
.__get__(
&PropertyKey::from(utf16!("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())
}
}

30
core/engine/src/builtins/temporal/time_zone/mod.rs

@ -1,3 +1,4 @@
//! Boa's implemetation of the `Temporal.TimeZone` builtin object.
#![allow(dead_code)]
use crate::{
@ -13,16 +14,29 @@ use crate::{
string::{common::StaticJsStrings, utf16},
Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_gc::{Finalize, Trace};
use boa_gc::{custom_trace, Finalize, Trace};
use boa_profiler::Profiler;
use boa_temporal::components::tz::{TimeZoneSlot, TzProtocol};
use boa_temporal::components::tz::TimeZoneSlot;
mod custom;
#[doc(inline)]
pub(crate) use custom::JsCustomTimeZone;
/// The `Temporal.TimeZone` object.
#[derive(Debug, Clone, Trace, Finalize, JsData)]
// SAFETY: `TimeZone` doesn't contain traceable data.
#[boa_gc(unsafe_empty_trace)]
#[derive(Debug, Clone, Finalize, JsData)]
pub struct TimeZone {
slot: TimeZoneSlot,
slot: TimeZoneSlot<JsCustomTimeZone>,
}
unsafe impl Trace for TimeZone {
custom_trace!(this, mark, {
match &this.slot {
TimeZoneSlot::Protocol(custom) => mark(custom),
// SAFETY: No values that are exposed to gc are in TZ
TimeZoneSlot::Tz(_) => {}
}
});
}
impl BuiltInObject for TimeZone {
@ -143,7 +157,7 @@ impl TimeZone {
.ok_or_else(|| {
JsNativeError::typ().with_message("this value must be a Temporal.TimeZone")
})?;
Ok(JsString::from(tz.slot.id(context)).into())
Ok(JsString::from(tz.slot.id(context)?).into())
}
pub(crate) fn get_offset_nanoseconds_for(
@ -257,7 +271,7 @@ impl TimeZone {
JsNativeError::typ().with_message("this value must be a Temporal.TimeZone")
})?;
// 3. Return timeZone.[[Identifier]].
Ok(JsString::from(tz.slot.id(context)).into())
Ok(JsString::from(tz.slot.id(context)?).into())
}
}

28
core/engine/src/builtins/temporal/zoned_date_time/mod.rs

@ -7,18 +7,32 @@ use crate::{
string::common::StaticJsStrings,
Context, JsBigInt, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
};
use boa_gc::{Finalize, Trace};
use boa_gc::{custom_trace, Finalize, Trace};
use boa_profiler::Profiler;
use boa_temporal::components::{Duration as TemporalDuration, ZonedDateTime as InnerZdt};
use boa_temporal::components::{
calendar::CalendarSlot, tz::TimeZoneSlot, Duration as TemporalDuration,
ZonedDateTime as InnerZdt,
};
use super::JsCustomCalendar;
use super::{JsCustomCalendar, JsCustomTimeZone};
/// The `Temporal.ZonedDateTime` object.
#[derive(Debug, Clone, Finalize, Trace, JsData)]
// SAFETY: ZonedDateTime does not contain any traceable types.
#[boa_gc(unsafe_empty_trace)]
#[derive(Debug, Clone, Finalize, JsData)]
pub struct ZonedDateTime {
pub(crate) inner: InnerZdt<JsCustomCalendar>,
pub(crate) inner: InnerZdt<JsCustomCalendar, JsCustomTimeZone>,
}
unsafe impl Trace for ZonedDateTime {
custom_trace!(this, mark, {
match this.inner.calendar() {
CalendarSlot::Protocol(custom) => mark(custom),
CalendarSlot::Builtin(_) => {}
}
match this.inner.tz() {
TimeZoneSlot::Protocol(custom) => mark(custom),
TimeZoneSlot::Tz(_) => {}
}
});
}
impl BuiltInObject for ZonedDateTime {

6
core/temporal/src/components/duration.rs

@ -8,7 +8,7 @@ use crate::{
};
use std::{any::Any, str::FromStr};
use super::calendar::CalendarProtocol;
use super::{calendar::CalendarProtocol, tz::TzProtocol};
// ==== `DateDuration` ====
@ -1161,7 +1161,7 @@ impl Duration {
/// seconds, milliseconds, microseconds, nanoseconds, increment, unit,
/// roundingMode [ , plainRelativeTo [, zonedRelativeTo [, precalculatedDateTime]]] )`
#[allow(clippy::type_complexity)]
pub fn round_duration<C: CalendarProtocol>(
pub fn round_duration<C: CalendarProtocol, Z: TzProtocol>(
&self,
unbalance_date_duration: DateDuration,
increment: f64,
@ -1169,7 +1169,7 @@ impl Duration {
rounding_mode: TemporalRoundingMode,
relative_targets: (
Option<&Date<C>>,
Option<&ZonedDateTime<C>>,
Option<&ZonedDateTime<C, Z>>,
Option<&DateTime<C>>,
),
context: &mut dyn Any,

64
core/temporal/src/components/tz.rs

@ -16,29 +16,14 @@ use super::calendar::CalendarProtocol;
pub const TIME_ZONE_PROPERTIES: [&str; 3] =
["getOffsetNanosecondsFor", "getPossibleInstantsFor", "id"];
/// A clonable `TzProtocol`
pub trait TzProtocolClone {
/// Clones the current `TimeZoneProtocol`.
fn clone_box(&self) -> Box<dyn TzProtocol>;
}
impl<P> TzProtocolClone for P
where
P: 'static + TzProtocol + Clone,
{
fn clone_box(&self) -> Box<dyn TzProtocol> {
Box::new(self.clone())
}
}
/// The Time Zone Protocol that must be implemented for time zones.
pub trait TzProtocol: TzProtocolClone {
pub trait TzProtocol: Clone {
/// Get the Offset nanoseconds for this `TimeZone`
fn get_offset_nanos_for(&self, context: &mut dyn Any) -> TemporalResult<BigInt>;
/// Get the possible Instant for this `TimeZone`
fn get_possible_instant_for(&self, context: &mut dyn Any) -> TemporalResult<Vec<Instant>>; // TODO: Implement Instant
/// Get the `TimeZone`'s identifier.
fn id(&self, context: &mut dyn Any) -> String;
fn id(&self, context: &mut dyn Any) -> TemporalResult<String>;
}
/// A Temporal `TimeZone`.
@ -50,14 +35,15 @@ pub struct TimeZone {
}
/// The `TimeZoneSlot` represents a `[[TimeZone]]` internal slot value.
pub enum TimeZoneSlot {
#[derive(Clone)]
pub enum TimeZoneSlot<Z: TzProtocol> {
/// A native `TimeZone` representation.
Tz(TimeZone),
/// A Custom `TimeZone` that implements the `TzProtocol`.
Protocol(Box<dyn TzProtocol>),
Protocol(Z),
}
impl core::fmt::Debug for TimeZoneSlot {
impl<Z: TzProtocol> core::fmt::Debug for TimeZoneSlot<Z> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Tz(tz) => write!(f, "{tz:?}"),
@ -66,16 +52,7 @@ impl core::fmt::Debug for TimeZoneSlot {
}
}
impl Clone for TimeZoneSlot {
fn clone(&self) -> Self {
match self {
Self::Tz(tz) => Self::Tz(tz.clone()),
Self::Protocol(p) => Self::Protocol(p.clone_box()),
}
}
}
impl TimeZoneSlot {
impl<Z: TzProtocol> TimeZoneSlot<Z> {
pub(crate) fn get_datetime_for<C: CalendarProtocol>(
&self,
instant: &Instant,
@ -87,8 +64,9 @@ impl TimeZoneSlot {
}
}
impl TzProtocol for TimeZoneSlot {
fn get_offset_nanos_for(&self, context: &mut dyn Any) -> TemporalResult<BigInt> {
impl<Z: TzProtocol> TimeZoneSlot<Z> {
/// Get the offset for this current `TimeZoneSlot`.
pub fn get_offset_nanos_for(&self, context: &mut dyn Any) -> TemporalResult<BigInt> {
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
// 3. Set instant to ? ToTemporalInstant(instant).
@ -106,14 +84,30 @@ impl TzProtocol for TimeZoneSlot {
}
}
fn get_possible_instant_for(&self, _context: &mut dyn Any) -> TemporalResult<Vec<Instant>> {
/// Get the possible `Instant`s for this `TimeZoneSlot`.
pub fn get_possible_instant_for(&self, _context: &mut dyn Any) -> TemporalResult<Vec<Instant>> {
Err(TemporalError::general("Not yet implemented."))
}
fn id(&self, context: &mut dyn Any) -> String {
/// Returns the current `TimeZoneSlot`'s identifier.
pub fn id(&self, context: &mut dyn Any) -> TemporalResult<String> {
match self {
Self::Tz(_) => todo!("implement tz.to_string"),
Self::Tz(_) => Err(TemporalError::range().with_message("Not yet implemented.")), // TODO: Implement Display for Time Zone.
Self::Protocol(tz) => tz.id(context),
}
}
}
impl TzProtocol for () {
fn get_offset_nanos_for(&self, _: &mut dyn Any) -> TemporalResult<BigInt> {
unreachable!()
}
fn get_possible_instant_for(&self, _: &mut dyn Any) -> TemporalResult<Vec<Instant>> {
unreachable!()
}
fn id(&self, _: &mut dyn Any) -> TemporalResult<String> {
Ok("() TimeZone".to_owned())
}
}

33
core/temporal/src/components/zoneddatetime.rs

@ -14,24 +14,26 @@ use crate::{
use core::any::Any;
use super::tz::TzProtocol;
/// The native Rust implementation of `Temporal.ZonedDateTime`.
#[derive(Debug, Clone)]
pub struct ZonedDateTime<C: CalendarProtocol> {
pub struct ZonedDateTime<C: CalendarProtocol, Z: TzProtocol> {
instant: Instant,
calendar: CalendarSlot<C>,
tz: TimeZoneSlot,
tz: TimeZoneSlot<Z>,
}
// ==== Private API ====
impl<C: CalendarProtocol> ZonedDateTime<C> {
impl<C: CalendarProtocol, Z: TzProtocol> ZonedDateTime<C, Z> {
/// Creates a `ZonedDateTime` without validating the input.
#[inline]
#[must_use]
pub(crate) fn new_unchecked(
instant: Instant,
calendar: CalendarSlot<C>,
tz: TimeZoneSlot,
tz: TimeZoneSlot<Z>,
) -> Self {
Self {
instant,
@ -43,21 +45,32 @@ impl<C: CalendarProtocol> ZonedDateTime<C> {
// ==== Public API ====
impl<C: CalendarProtocol> ZonedDateTime<C> {
impl<C: CalendarProtocol, Z: TzProtocol> ZonedDateTime<C, Z> {
/// Creates a new valid `ZonedDateTime`.
#[inline]
pub fn new(nanos: BigInt, calendar: CalendarSlot<C>, tz: TimeZoneSlot) -> TemporalResult<Self> {
pub fn new(
nanos: BigInt,
calendar: CalendarSlot<C>,
tz: TimeZoneSlot<Z>,
) -> TemporalResult<Self> {
let instant = Instant::new(nanos)?;
Ok(Self::new_unchecked(instant, calendar, tz))
}
/// Returns the `ZonedDateTime`'s Calendar identifier.
/// Returns `ZonedDateTime`'s Calendar.
#[inline]
#[must_use]
pub fn calendar(&self) -> &CalendarSlot<C> {
&self.calendar
}
/// Returns `ZonedDateTime`'s `TimeZone` slot.
#[inline]
#[must_use]
pub fn tz(&self) -> &TimeZoneSlot<Z> {
&self.tz
}
/// Returns the `epochSeconds` value of this `ZonedDateTime`.
#[must_use]
pub fn epoch_seconds(&self) -> f64 {
@ -85,7 +98,7 @@ impl<C: CalendarProtocol> ZonedDateTime<C> {
// ==== Context based API ====
impl<C: CalendarProtocol> ZonedDateTime<C> {
impl<C: CalendarProtocol, Z: TzProtocol> ZonedDateTime<C, Z> {
/// Returns the `year` value for this `ZonedDateTime`.
#[inline]
pub fn contextual_year(&self, context: &mut dyn Any) -> TemporalResult<i32> {
@ -236,7 +249,7 @@ mod tests {
fn basic_zdt_test() {
let nov_30_2023_utc = BigInt::from(1_701_308_952_000_000_000i64);
let zdt = ZonedDateTime::<()>::new(
let zdt = ZonedDateTime::<(), ()>::new(
nov_30_2023_utc.clone(),
CalendarSlot::from_str("iso8601").unwrap(),
TimeZoneSlot::Tz(TimeZone {
@ -253,7 +266,7 @@ mod tests {
assert_eq!(zdt.minute().unwrap(), 49);
assert_eq!(zdt.second().unwrap(), 12);
let zdt_minus_five = ZonedDateTime::<()>::new(
let zdt_minus_five = ZonedDateTime::<(), ()>::new(
nov_30_2023_utc,
CalendarSlot::from_str("iso8601").unwrap(),
TimeZoneSlot::Tz(TimeZone {

Loading…
Cancel
Save