diff --git a/core/engine/src/builtins/temporal/calendar/object.rs b/core/engine/src/builtins/temporal/calendar/object.rs index 198d2f58ee..3f1d385d7d 100644 --- a/core/engine/src/builtins/temporal/calendar/object.rs +++ b/core/engine/src/builtins/temporal/calendar/object.rs @@ -4,7 +4,8 @@ use crate::{ builtins::{ iterable::IteratorHint, temporal::{ - fields::object_to_temporal_fields, plain_date, plain_month_day, plain_year_month, + fields::object_to_temporal_fields, plain_date, plain_date_time, plain_month_day, + plain_year_month, }, Array, }, @@ -951,8 +952,10 @@ pub(crate) fn date_like_to_object( 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) => { - todo!() + 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::MonthDay(md) => { plain_month_day::create_temporal_month_day(md.clone(), None, context) diff --git a/core/engine/src/builtins/temporal/plain_date_time/mod.rs b/core/engine/src/builtins/temporal/plain_date_time/mod.rs index b655965531..78e9319e2a 100644 --- a/core/engine/src/builtins/temporal/plain_date_time/mod.rs +++ b/core/engine/src/builtins/temporal/plain_date_time/mod.rs @@ -2,12 +2,17 @@ #![allow(dead_code, unused_variables)] use crate::{ - builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject}, + builtins::{ + temporal::{calendar, to_integer_with_truncation}, + BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, + }, context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors}, + js_string, + object::internal_methods::get_prototype_from_constructor, property::Attribute, realm::Realm, - string::common::StaticJsStrings, - Context, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, + string::{common::StaticJsStrings, utf16}, + Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue, }; use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; @@ -41,12 +46,207 @@ impl IntrinsicObject for PlainDateTime { fn init(realm: &Realm) { let _timer = Profiler::global().start_event(std::any::type_name::(), "init"); + let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id) + .name(js_string!("get calendarId")) + .build(); + + let get_year = BuiltInBuilder::callable(realm, Self::get_year) + .name(js_string!("get year")) + .build(); + + let get_month = BuiltInBuilder::callable(realm, Self::get_month) + .name(js_string!("get month")) + .build(); + + let get_month_code = BuiltInBuilder::callable(realm, Self::get_month_code) + .name(js_string!("get monthCode")) + .build(); + + let get_day = BuiltInBuilder::callable(realm, Self::get_day) + .name(js_string!("get day")) + .build(); + + let get_hour = BuiltInBuilder::callable(realm, Self::get_hour) + .name(js_string!("get hour")) + .build(); + + let get_minute = BuiltInBuilder::callable(realm, Self::get_minute) + .name(js_string!("get minute")) + .build(); + + let get_second = BuiltInBuilder::callable(realm, Self::get_second) + .name(js_string!("get second")) + .build(); + + let get_millisecond = BuiltInBuilder::callable(realm, Self::get_millisecond) + .name(js_string!("get millisecond")) + .build(); + + let get_microsecond = BuiltInBuilder::callable(realm, Self::get_microsecond) + .name(js_string!("get microsecond")) + .build(); + + let get_nanosecond = BuiltInBuilder::callable(realm, Self::get_nanosecond) + .name(js_string!("get nanosecond")) + .build(); + + let get_day_of_week = BuiltInBuilder::callable(realm, Self::get_day_of_week) + .name(js_string!("get dayOfWeek")) + .build(); + + let get_day_of_year = BuiltInBuilder::callable(realm, Self::get_day_of_year) + .name(js_string!("get dayOfYear")) + .build(); + + let get_week_of_year = BuiltInBuilder::callable(realm, Self::get_week_of_year) + .name(js_string!("get weekOfYear")) + .build(); + + let get_year_of_week = BuiltInBuilder::callable(realm, Self::get_year_of_week) + .name(js_string!("get yearOfWeek")) + .build(); + + let get_days_in_week = BuiltInBuilder::callable(realm, Self::get_days_in_week) + .name(js_string!("get daysInWeek")) + .build(); + + let get_days_in_month = BuiltInBuilder::callable(realm, Self::get_days_in_month) + .name(js_string!("get daysInMonth")) + .build(); + + let get_days_in_year = BuiltInBuilder::callable(realm, Self::get_days_in_year) + .name(js_string!("get daysInYear")) + .build(); + + let get_months_in_year = BuiltInBuilder::callable(realm, Self::get_months_in_year) + .name(js_string!("get monthsInYear")) + .build(); + + let get_in_leap_year = BuiltInBuilder::callable(realm, Self::get_in_leap_year) + .name(js_string!("get inLeapYear")) + .build(); + BuiltInBuilder::from_standard_constructor::(realm) .static_property( JsSymbol::to_string_tag(), Self::NAME, Attribute::CONFIGURABLE, ) + .accessor( + utf16!("calendarId"), + Some(get_calendar_id), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("year"), + Some(get_year), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("month"), + Some(get_month), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("monthCode"), + Some(get_month_code), + None, + Attribute::CONFIGURABLE, + ) + .accessor(utf16!("day"), Some(get_day), None, Attribute::CONFIGURABLE) + .accessor( + utf16!("hour"), + Some(get_hour), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("minute"), + Some(get_minute), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("second"), + Some(get_second), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("millisecond"), + Some(get_millisecond), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("microsecond"), + Some(get_microsecond), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("nanosecond"), + Some(get_nanosecond), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("dayOfWeek"), + Some(get_day_of_week), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("dayOfYear"), + Some(get_day_of_year), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("weekOfYear"), + Some(get_week_of_year), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("yearOfWeek"), + Some(get_year_of_week), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("daysInWeek"), + Some(get_days_in_week), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("daysInMonth"), + Some(get_days_in_month), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("daysInYear"), + Some(get_days_in_year), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("monthsInYear"), + Some(get_months_in_year), + None, + Attribute::CONFIGURABLE, + ) + .accessor( + utf16!("inLeapYear"), + Some(get_in_leap_year), + None, + Attribute::CONFIGURABLE, + ) .build(); } @@ -66,94 +266,381 @@ impl BuiltInConstructor for PlainDateTime { args: &[JsValue], context: &mut Context, ) -> JsResult { - Err(JsNativeError::range() - .with_message("Not yet implemented.") - .into()) + // 1. If NewTarget is undefined, then + if new_target.is_undefined() { + // a. Throw a TypeError exception. + return Err(JsNativeError::typ() + .with_message("NewTarget cannot be undefined when contructing PlainDateTime.") + .into()); + }; + + // 2. Set isoYear to ? ToIntegerWithTruncation(isoYear). + let iso_year = to_integer_with_truncation(args.get_or_undefined(0), context)?; + // 3. Set isoMonth to ? ToIntegerWithTruncation(isoMonth). + let iso_month = to_integer_with_truncation(args.get_or_undefined(1), context)?; + // 4. Set isoDay to ? ToIntegerWithTruncation(isoDay). + let iso_day = to_integer_with_truncation(args.get_or_undefined(2), context)?; + // 5. If hour is undefined, set hour to 0; else set hour to ? ToIntegerWithTruncation(hour). + let hour = args + .get(3) + .map_or(Ok(0), |v| to_integer_with_truncation(v, context))?; + // 6. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute). + let minute = args + .get(4) + .map_or(Ok(0), |v| to_integer_with_truncation(v, context))?; + // 7. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second). + let second = args + .get(5) + .map_or(Ok(0), |v| to_integer_with_truncation(v, context))?; + // 8. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond). + let millisecond = args + .get(6) + .map_or(Ok(0), |v| to_integer_with_truncation(v, context))?; + // 9. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond). + let microsecond = args + .get(7) + .map_or(Ok(0), |v| to_integer_with_truncation(v, context))?; + // 10. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond). + let nanosecond = args + .get(8) + .map_or(Ok(0), |v| to_integer_with_truncation(v, context))?; + // 11. Let calendar be ? ToTemporalCalendarSlotValue(calendarLike, "iso8601"). + let calendar_slot = + calendar::to_temporal_calendar_slot_value(args.get_or_undefined(9), context)?; + + let dt = InnerDateTime::new( + iso_year, + iso_month, + iso_day, + hour, + minute, + second, + millisecond, + microsecond, + nanosecond, + calendar_slot, + )?; + + // 12. Return ? CreateTemporalDateTime(isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond, calendar, NewTarget). + create_temporal_datetime(dt, Some(new_target), context).map(Into::into) } } -// ==== `PlainDateTime` Accessor Properties ==== +// ==== `PlainDateTimeTime` accessor implmentations ==== impl PlainDateTime { - fn calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.3 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(JsString::from(date.inner.calendar().identifier(context)?).into()) + } + + /// 5.3.4 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_year(context)?.into()) + } + + /// 5.3.5 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_month(context)?.into()) + } + + /// 5.3.6 get Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(JsString::from(date.inner.contextual_month_code(context)?.as_str()).into()) + } + + /// 5.3.7 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_day(context)?.into()) } - fn year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.8 get `Temporal.PlainDateTime.prototype.hour` + fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + // 1. Let dateTime be the this value. + // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). + let time = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Return ๐”ฝ(dateTime.[[ISOHour]]). + Ok(time.inner.hour().into()) } - fn month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.9 get `Temporal.PlainDateTime.prototype.minute` + fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + // 1. Let dateTime be the this value. + // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). + let time = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Return ๐”ฝ(dateTime.[[ISOMinute]]). + Ok(time.inner.minute().into()) } - fn month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.10 get `Temporal.PlainDateTime.prototype.second` + fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + // 1. Let dateTime be the this value. + // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). + let time = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Return ๐”ฝ(dateTime.[[ISOSecond]]). + Ok(time.inner.second().into()) } - fn day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.11 get `Temporal.PlainDateTime.prototype.millisecond` + fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + // 1. Let dateTime be the this value. + // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). + let time = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Return ๐”ฝ(dateTime.[[ISOMillisecond]]). + Ok(time.inner.millisecond().into()) } - fn hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.12 get `Temporal.PlainDateTime.prototype.microsecond` + fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + // 1. Let dateTime be the this value. + // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). + let time = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Return ๐”ฝ(dateTime.[[ISOMicrosecond]]). + Ok(time.inner.microsecond().into()) } - fn minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.13 get `Temporal.PlainDateTime.prototype.nanosecond` + fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + // 1. Let dateTime be the this value. + // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). + let time = this + .as_object() + .and_then(JsObject::downcast_ref::) + .ok_or_else(|| { + JsNativeError::typ().with_message("the this object must be a PlainDateTime object.") + })?; + + // 3. Return ๐”ฝ(dateTime.[[ISONanosecond]]). + Ok(time.inner.nanosecond().into()) } - fn second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.14 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_day_of_week(context)?.into()) } - fn millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.15 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_day_of_year(context)?.into()) } - fn microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.16 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_week_of_year(context)?.into()) } - fn nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.17 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_year_of_week(context)?.into()) } - fn era(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.18 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_days_in_week(context)?.into()) } - fn era_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { - Err(JsNativeError::error() - .with_message("calendars not yet implemented.") - .into()) + /// 5.3.19 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_days_in_month(context)?.into()) + } + + /// 5.3.20 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_days_in_year(context)?.into()) + } + + /// 5.3.21 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_months_in_year(context)?.into()) + } + + /// 5.3.22 get `Temporal.PlainDateTime.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 a PlainDateTime object.") + })?; + + Ok(date.inner.contextual_in_leap_year(context)?.into()) } } // ==== `PlainDateTime` Abstract Operations` ==== -// See `IsoDateTimeRecord` +pub(crate) fn create_temporal_datetime( + inner: InnerDateTime, + new_target: Option<&JsValue>, + context: &mut Context, +) -> JsResult { + // NOTE(nekevss): The below validations should be upheld with the creation of `InnerDateTime`. + // 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception. + // 2. If IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception. + // 3. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond) is false, then + // a. Throw a RangeError exception. + + // 4. If newTarget is not present, set newTarget to %Temporal.PlainDateTime%. + let new_target = if let Some(new_target) = new_target { + new_target.clone() + } else { + context + .realm() + .intrinsics() + .constructors() + .plain_date_time() + .constructor() + .into() + }; + + // 5. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDateTime.prototype%", ยซ [[InitializedTemporalDateTime]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] ยป). + let prototype = get_prototype_from_constructor( + &new_target, + StandardConstructors::plain_date_time, + context, + )?; + + // 6. Set object.[[ISOYear]] to isoYear. + // 7. Set object.[[ISOMonth]] to isoMonth. + // 8. Set object.[[ISODay]] to isoDay. + // 9. Set object.[[ISOHour]] to hour. + // 10. Set object.[[ISOMinute]] to minute. + // 11. Set object.[[ISOSecond]] to second. + // 12. Set object.[[ISOMillisecond]] to millisecond. + // 13. Set object.[[ISOMicrosecond]] to microsecond. + // 14. Set object.[[ISONanosecond]] to nanosecond. + // 15. Set object.[[Calendar]] to calendar. + let obj = JsObject::from_proto_and_data(prototype, PlainDateTime::new(inner)); + + // 16. Return object. + Ok(obj) +} diff --git a/core/temporal/src/components/calendar.rs b/core/temporal/src/components/calendar.rs index 8e35ea0754..03cc57341f 100644 --- a/core/temporal/src/components/calendar.rs +++ b/core/temporal/src/components/calendar.rs @@ -96,7 +96,7 @@ impl CalendarDateLike { pub fn as_iso_date(&self) -> IsoDate { match self { CalendarDateLike::Date(d) => d.iso_date(), - CalendarDateLike::DateTime(dt) => dt.iso_date(), + CalendarDateLike::DateTime(dt) => *dt.iso_date(), CalendarDateLike::MonthDay(md) => md.iso_date(), CalendarDateLike::YearMonth(ym) => ym.iso_date(), } @@ -516,7 +516,7 @@ impl CalendarSlot { context: &mut dyn Any, ) -> TemporalResult { match self { - CalendarSlot::Builtin(AnyCalendar::Iso(_)) => Ok(date_like.as_iso_date().year()), + CalendarSlot::Builtin(AnyCalendar::Iso(_)) => Ok(date_like.as_iso_date().year), CalendarSlot::Builtin(builtin) => { let calendar_date = builtin.date_from_iso(date_like.as_iso_date().as_icu4x()?); Ok(builtin.year(&calendar_date).number) @@ -532,7 +532,7 @@ impl CalendarSlot { context: &mut dyn Any, ) -> TemporalResult { match self { - CalendarSlot::Builtin(AnyCalendar::Iso(_)) => Ok(date_like.as_iso_date().month()), + CalendarSlot::Builtin(AnyCalendar::Iso(_)) => Ok(date_like.as_iso_date().month), CalendarSlot::Builtin(_) => { Err(TemporalError::range().with_message("Not yet implemented.")) } @@ -564,7 +564,7 @@ impl CalendarSlot { context: &mut dyn Any, ) -> TemporalResult { match self { - CalendarSlot::Builtin(AnyCalendar::Iso(_)) => Ok(date_like.as_iso_date().day()), + CalendarSlot::Builtin(AnyCalendar::Iso(_)) => Ok(date_like.as_iso_date().day), CalendarSlot::Builtin(_) => { Err(TemporalError::range().with_message("Not yet implemented.")) } diff --git a/core/temporal/src/components/date.rs b/core/temporal/src/components/date.rs index be918d7288..85a4279393 100644 --- a/core/temporal/src/components/date.rs +++ b/core/temporal/src/components/date.rs @@ -67,7 +67,7 @@ impl Date { /// Creates a `Date` from a `DateTime`. pub fn from_datetime(dt: &DateTime) -> Self { Self { - iso: dt.iso_date(), + iso: *dt.iso_date(), calendar: dt.calendar().clone(), } } @@ -76,21 +76,21 @@ impl Date { #[must_use] /// Returns this `Date`'s ISO year value. pub const fn iso_year(&self) -> i32 { - self.iso.year() + self.iso.year } #[inline] #[must_use] /// Returns this `Date`'s ISO month value. pub const fn iso_month(&self) -> u8 { - self.iso.month() + self.iso.month } #[inline] #[must_use] /// Returns this `Date`'s ISO day value. pub const fn iso_day(&self) -> u8 { - self.iso.day() + self.iso.day } #[inline] @@ -373,9 +373,9 @@ impl Date { largest_unit: TemporalUnit, context: &mut dyn Any, ) -> TemporalResult { - if self.iso.year() == other.iso.year() - && self.iso.month() == other.iso.month() - && self.iso.day() == other.iso.day() + if self.iso.year == other.iso.year + && self.iso.month == other.iso.month + && self.iso.day == other.iso.day { return Ok(Duration::default()); } diff --git a/core/temporal/src/components/datetime.rs b/core/temporal/src/components/datetime.rs index e6417aeaf7..0b16669062 100644 --- a/core/temporal/src/components/datetime.rs +++ b/core/temporal/src/components/datetime.rs @@ -1,7 +1,5 @@ //! This module implements `DateTime` any directly related algorithms. -use std::str::FromStr; - use crate::{ components::{ calendar::{CalendarProtocol, CalendarSlot}, @@ -13,6 +11,9 @@ use crate::{ TemporalError, TemporalResult, }; +use std::{any::Any, str::FromStr}; +use tinystr::TinyAsciiStr; + /// The native Rust implementation of `Temporal.PlainDateTime` #[derive(Debug, Default, Clone)] pub struct DateTime { @@ -26,11 +27,8 @@ impl DateTime { /// Creates a new unchecked `DateTime`. #[inline] #[must_use] - pub(crate) fn new_unchecked(date: IsoDate, time: IsoTime, calendar: CalendarSlot) -> Self { - Self { - iso: IsoDateTime::new_unchecked(date, time), - calendar, - } + pub(crate) fn new_unchecked(iso: IsoDateTime, calendar: CalendarSlot) -> Self { + Self { iso, calendar } } #[inline] @@ -50,6 +48,13 @@ impl DateTime { let iso = IsoDateTime::from_epoch_nanos(&instant.nanos, offset)?; Ok(Self { iso, calendar }) } + + /// Returns the inner `IsoDate` value. + #[inline] + #[must_use] + pub(crate) fn iso_date(&self) -> &IsoDate { + self.iso.date() + } } // ==== Public DateTime API ==== @@ -80,7 +85,10 @@ impl DateTime { nanosecond, ArithmeticOverflow::Reject, )?; - Ok(Self::new_unchecked(iso_date, iso_time, calendar)) + Ok(Self::new_unchecked( + IsoDateTime::new(iso_date, iso_time)?, + calendar, + )) } /// Validates whether ISO date slots are within iso limits at noon. @@ -89,59 +97,66 @@ impl DateTime { Self::validate_iso(target.iso_date()) } - /// Returns the inner `IsoDate` value. + /// Returns this `Date`'s ISO year value. #[inline] #[must_use] - pub fn iso_date(&self) -> IsoDate { - self.iso.date() + pub const fn iso_year(&self) -> i32 { + self.iso.date().year + } + + /// Returns this `Date`'s ISO month value. + #[inline] + #[must_use] + pub const fn iso_month(&self) -> u8 { + self.iso.date().month } - /// Returns the inner `IsoTime` value. + /// Returns this `Date`'s ISO day value. #[inline] #[must_use] - pub fn iso_time(&self) -> IsoTime { - self.iso.time() + pub const fn iso_day(&self) -> u8 { + self.iso.date().day } /// Returns the hour value #[inline] #[must_use] - pub fn hours(&self) -> u8 { + pub fn hour(&self) -> u8 { self.iso.time().hour } /// Returns the minute value #[inline] #[must_use] - pub fn minutes(&self) -> u8 { + pub fn minute(&self) -> u8 { self.iso.time().minute } /// Returns the second value #[inline] #[must_use] - pub fn seconds(&self) -> u8 { + pub fn second(&self) -> u8 { self.iso.time().second } /// Returns the `millisecond` value #[inline] #[must_use] - pub fn milliseconds(&self) -> u16 { + pub fn millisecond(&self) -> u16 { self.iso.time().millisecond } /// Returns the `microsecond` value #[inline] #[must_use] - pub fn microseconds(&self) -> u16 { + pub fn microsecond(&self) -> u16 { self.iso.time().microsecond } /// Returns the `nanosecond` value #[inline] #[must_use] - pub fn nanoseconds(&self) -> u16 { + pub fn nanosecond(&self) -> u16 { self.iso.time().nanosecond } @@ -153,6 +168,179 @@ impl DateTime { } } +// ==== Calendar-derived public API ==== + +impl DateTime { + /// Returns the calendar year value with provided context. + pub fn contextual_year(&self, context: &mut dyn Any) -> TemporalResult { + self.calendar.year( + &super::calendar::CalendarDateLike::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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::DateTime(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 ()) + } +} + // ==== Trait impls ==== impl FromStr for DateTime { @@ -182,9 +370,51 @@ impl FromStr for DateTime { )?; Ok(Self::new_unchecked( - date, - time, + IsoDateTime::new(date, time)?, CalendarSlot::from_str(&calendar)?, )) } } + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use crate::components::calendar::CalendarSlot; + + use super::DateTime; + + #[test] + #[allow(clippy::float_cmp)] + fn plain_date_time_limits() { + // This test is primarily to assert that the `expect` in the epoch methods is + // valid, i.e., a valid instant is within the range of an f64. + let negative_limit = DateTime::<()>::new( + -271_821, + 4, + 19, + 0, + 0, + 0, + 0, + 0, + 0, + CalendarSlot::from_str("iso8601").unwrap(), + ); + let positive_limit = DateTime::<()>::new( + 275_760, + 9, + 14, + 0, + 0, + 0, + 0, + 0, + 0, + CalendarSlot::from_str("iso8601").unwrap(), + ); + + assert!(negative_limit.is_err()); + assert!(positive_limit.is_err()); + } +} diff --git a/core/temporal/src/components/month_day.rs b/core/temporal/src/components/month_day.rs index 0253c8f4dd..b31ec1391d 100644 --- a/core/temporal/src/components/month_day.rs +++ b/core/temporal/src/components/month_day.rs @@ -42,14 +42,14 @@ impl MonthDay { #[inline] #[must_use] pub fn month(&self) -> u8 { - self.iso.month() + self.iso.month } /// Returns the `day` value of `MonthDay`. #[inline] #[must_use] pub fn day(&self) -> u8 { - self.iso.day() + self.iso.day } /// Returns a reference to `MonthDay`'s `CalendarSlot` diff --git a/core/temporal/src/components/year_month.rs b/core/temporal/src/components/year_month.rs index a1a399ce97..0cb7f4f0a7 100644 --- a/core/temporal/src/components/year_month.rs +++ b/core/temporal/src/components/year_month.rs @@ -44,14 +44,14 @@ impl YearMonth { #[inline] #[must_use] pub fn year(&self) -> i32 { - self.iso.year() + self.iso.year } /// Returns the `month` value for this `YearMonth`. #[inline] #[must_use] pub fn month(&self) -> u8 { - self.iso.month() + self.iso.month } #[inline] diff --git a/core/temporal/src/components/zoneddatetime.rs b/core/temporal/src/components/zoneddatetime.rs index 7985dfed83..489efd1f6d 100644 --- a/core/temporal/src/components/zoneddatetime.rs +++ b/core/temporal/src/components/zoneddatetime.rs @@ -162,7 +162,7 @@ impl ZonedDateTime { let dt = self .tz .get_datetime_for(&self.instant, &self.calendar, context)?; - Ok(dt.hours()) + Ok(dt.hour()) } /// Returns the `hour` value for this `ZonedDateTime`. @@ -175,7 +175,7 @@ impl ZonedDateTime { let dt = self .tz .get_datetime_for(&self.instant, &self.calendar, context)?; - Ok(dt.minutes()) + Ok(dt.minute()) } /// Returns the `minute` value for this `ZonedDateTime`. @@ -188,7 +188,7 @@ impl ZonedDateTime { let dt = self .tz .get_datetime_for(&self.instant, &self.calendar, context)?; - Ok(dt.seconds()) + Ok(dt.second()) } /// Returns the `second` value for this `ZonedDateTime`. @@ -201,7 +201,7 @@ impl ZonedDateTime { let dt = self .tz .get_datetime_for(&self.instant, &self.calendar, context)?; - Ok(dt.milliseconds()) + Ok(dt.millisecond()) } /// Returns the `millisecond` value for this `ZonedDateTime`. @@ -214,7 +214,7 @@ impl ZonedDateTime { let dt = self .tz .get_datetime_for(&self.instant, &self.calendar, context)?; - Ok(dt.milliseconds()) + Ok(dt.millisecond()) } /// Returns the `microsecond` value for this `ZonedDateTime`. @@ -227,7 +227,7 @@ impl ZonedDateTime { let dt = self .tz .get_datetime_for(&self.instant, &self.calendar, context)?; - Ok(dt.nanoseconds()) + Ok(dt.nanosecond()) } /// Returns the `nanosecond` value for this `ZonedDateTime`. diff --git a/core/temporal/src/iso.rs b/core/temporal/src/iso.rs index 9cdcd87439..96bec11c0b 100644 --- a/core/temporal/src/iso.rs +++ b/core/temporal/src/iso.rs @@ -35,6 +35,16 @@ impl IsoDateTime { Self { date, time } } + /// Creates a new validated `IsoDateTime` that is within valid limits. + pub(crate) fn new(date: IsoDate, time: IsoTime) -> TemporalResult { + if !iso_dt_within_valid_limits(date, &time) { + return Err( + TemporalError::range().with_message("IsoDateTime not within a valid range.") + ); + } + Ok(Self::new_unchecked(date, time)) + } + // NOTE: The below assumes that nanos is from an `Instant` and thus in a valid range. -> Needs validation. /// Creates an `IsoDateTime` from a `BigInt` of epochNanoseconds. pub(crate) fn from_epoch_nanos(nanos: &BigInt, offset: f64) -> TemporalResult { @@ -104,36 +114,15 @@ impl IsoDateTime { /// Returns whether the `IsoDateTime` is within valid limits. pub(crate) fn is_within_limits(&self) -> bool { - let Some(ns) = self.to_utc_epoch_nanoseconds(0f64) else { - return false; - }; - - let max = BigInt::from(crate::NS_MAX_INSTANT + i128::from(NS_PER_DAY)); - let min = BigInt::from(crate::NS_MIN_INSTANT - i128::from(NS_PER_DAY)); - - min < ns && max > ns - } - - /// Returns the UTC epoch nanoseconds for this `IsoDateTime`. - pub(crate) fn to_utc_epoch_nanoseconds(self, offset: f64) -> Option { - let day = self.date.to_epoch_days(); - let time = self.time.to_epoch_ms(); - let epoch_ms = utils::epoch_days_to_epoch_ms(day, time); - - let epoch_nanos = epoch_ms.mul_add( - 1_000_000f64, - f64::from(self.time.microsecond).mul_add(1_000f64, f64::from(self.time.nanosecond)), - ); - - BigInt::from_f64(epoch_nanos - offset) + iso_dt_within_valid_limits(self.date, &self.time) } - pub(crate) fn date(&self) -> IsoDate { - self.date + pub(crate) const fn date(&self) -> &IsoDate { + &self.date } - pub(crate) fn time(&self) -> IsoTime { - self.time + pub(crate) const fn time(&self) -> &IsoTime { + &self.time } } @@ -154,9 +143,9 @@ pub trait IsoDateSlots { /// `Temporal.YearMonth` object, and the `Temporal.MonthDay` object. #[derive(Debug, Clone, Copy, Default)] pub struct IsoDate { - year: i32, - month: u8, - day: u8, + pub(crate) year: i32, + pub(crate) month: u8, + pub(crate) day: u8, } impl IsoDate { @@ -202,21 +191,6 @@ impl IsoDate { ) } - /// Returns the year field - pub(crate) const fn year(self) -> i32 { - self.year - } - - /// Returns the month field - pub(crate) const fn month(self) -> u8 { - self.month - } - - /// Returns the day field - pub(crate) const fn day(self) -> u8 { - self.day - } - /// Functionally the same as Date's abstract operation `MakeDay` /// /// Equivalent to `IsoDateToEpochDays` @@ -588,11 +562,43 @@ impl IsoTime { /// /// Functionally the same as Date's `MakeTime` pub(crate) fn to_epoch_ms(self) -> f64 { - f64::from(self.hour).mul_add( - utils::MS_PER_HOUR, - f64::from(self.minute) * utils::MS_PER_MINUTE, - ) + f64::from(self.second).mul_add(1000f64, f64::from(self.millisecond)) + ((f64::from(self.hour) * utils::MS_PER_HOUR + + f64::from(self.minute) * utils::MS_PER_MINUTE) + + f64::from(self.second) * 1000f64) + + f64::from(self.millisecond) + } +} + +// ==== `IsoDateTime` specific utility functions ==== + +#[inline] +/// Utility function to determine if a `DateTime`'s components create a `DateTime` within valid limits +fn iso_dt_within_valid_limits(date: IsoDate, time: &IsoTime) -> bool { + if iso_date_to_epoch_days(date.year, (date.month).into(), date.day.into()).abs() > 100_000_001 { + return false; } + let Some(ns) = utc_epoch_nanos(date, time, 0.0) else { + return false; + }; + + let max = BigInt::from(crate::NS_MAX_INSTANT + i128::from(NS_PER_DAY)); + let min = BigInt::from(crate::NS_MIN_INSTANT - i128::from(NS_PER_DAY)); + + min < ns && max > ns +} + +#[inline] +/// Utility function to convert a `IsoDate` and `IsoTime` values into epoch nanoseconds +fn utc_epoch_nanos(date: IsoDate, time: &IsoTime, offset: f64) -> Option { + let ms = time.to_epoch_ms(); + let epoch_ms = utils::epoch_days_to_epoch_ms(date.to_epoch_days(), ms); + + let epoch_nanos = epoch_ms.mul_add( + 1_000_000f64, + f64::from(time.microsecond).mul_add(1_000f64, f64::from(time.nanosecond)), + ); + + BigInt::from_f64(epoch_nanos - offset) } // ==== `IsoDate` specific utiltiy functions ==== @@ -610,7 +616,7 @@ fn iso_date_to_epoch_days(year: i32, month: i32, day: i32) -> i32 { let month_t = utils::epoch_time_for_month_given_year(resolved_month, resolved_year); // 4. Return EpochTimeToDayNumber(t) + date - 1. - utils::epoch_time_to_day_number(year_t + month_t) + day - 1 + utils::epoch_time_to_day_number((year_t.abs() + month_t).copysign(year_t)) + day - 1 } #[inline] diff --git a/core/temporal/src/lib.rs b/core/temporal/src/lib.rs index c8ca605fad..24dbf5f357 100644 --- a/core/temporal/src/lib.rs +++ b/core/temporal/src/lib.rs @@ -61,7 +61,7 @@ pub type TemporalResult = Result; // Relevant numeric constants /// Nanoseconds per day constant: 8.64e+13 -pub const NS_PER_DAY: i64 = 86_400_000_000_000; +pub const NS_PER_DAY: i64 = MS_PER_DAY as i64 * 1_000_000; /// Milliseconds per day constant: 8.64e+7 pub const MS_PER_DAY: i32 = 24 * 60 * 60 * 1000; /// Max Instant nanosecond constant diff --git a/core/temporal/src/parser/tests.rs b/core/temporal/src/parser/tests.rs index a51fb3c5b6..077dc74d43 100644 --- a/core/temporal/src/parser/tests.rs +++ b/core/temporal/src/parser/tests.rs @@ -14,15 +14,12 @@ fn temporal_parser_basic() { let sep_result = basic_separated.parse::>().unwrap(); - assert_eq!(basic_result.iso_date().year(), 2020); - assert_eq!(basic_result.iso_date().month(), 11); - assert_eq!(basic_result.iso_date().day(), 8); - assert_eq!(basic_result.iso_date().year(), sep_result.iso_date().year()); - assert_eq!( - basic_result.iso_date().month(), - sep_result.iso_date().month() - ); - assert_eq!(basic_result.iso_date().day(), sep_result.iso_date().day()); + assert_eq!(basic_result.iso_year(), 2020); + assert_eq!(basic_result.iso_month(), 11); + assert_eq!(basic_result.iso_day(), 8); + assert_eq!(basic_result.iso_year(), sep_result.iso_year()); + assert_eq!(basic_result.iso_month(), sep_result.iso_month()); + assert_eq!(basic_result.iso_day(), sep_result.iso_day()); } #[test] @@ -34,14 +31,12 @@ fn temporal_date_time_max() { let result = date_time.parse::>().unwrap(); - let time_results = result.iso_time(); - - assert_eq!(time_results.hour, 12); - assert_eq!(time_results.minute, 28); - assert_eq!(time_results.second, 32); - assert_eq!(time_results.millisecond, 329); - assert_eq!(time_results.microsecond, 402); - assert_eq!(time_results.nanosecond, 834); + assert_eq!(result.hour(), 12); + assert_eq!(result.minute(), 28); + assert_eq!(result.second(), 32); + assert_eq!(result.millisecond(), 329); + assert_eq!(result.microsecond(), 402); + assert_eq!(result.nanosecond(), 834); } #[test] @@ -50,7 +45,7 @@ fn temporal_year_parsing() { let bad_year = "-000000-11-08"; let result_good = long.parse::>().unwrap(); - assert_eq!(result_good.iso_date().year(), 2020); + assert_eq!(result_good.iso_year(), 2020); let err_result = bad_year.parse::>(); assert!( diff --git a/core/temporal/src/utils.rs b/core/temporal/src/utils.rs index d0abf2343e..cc370f42a9 100644 --- a/core/temporal/src/utils.rs +++ b/core/temporal/src/utils.rs @@ -5,8 +5,6 @@ use crate::{ TemporalError, TemporalResult, MS_PER_DAY, }; -use std::ops::Mul; - // NOTE: Review the below for optimizations and add ALOT of tests. /// Converts and validates an `Option` rounding increment value into a valid increment result. @@ -264,7 +262,7 @@ pub(crate) fn epoch_time_for_month_given_year(m: i32, y: i32) -> f64 { _ => unreachable!(), }; - f64::from(MS_PER_DAY).mul(f64::from(days)) + f64::from(MS_PER_DAY) * f64::from(days) } pub(crate) fn epoch_time_to_date(t: f64) -> u8 {