boa_engine/builtins/temporal/
mod.rs1mod calendar;
11mod duration;
12mod error;
13mod instant;
14mod now;
15mod options;
16mod plain_date;
17mod plain_date_time;
18mod plain_month_day;
19mod plain_time;
20mod plain_year_month;
21mod zoneddatetime;
22
23#[cfg(test)]
24mod tests;
25
26pub use self::{
27 duration::*, instant::*, now::*, plain_date::*, plain_date_time::*, plain_month_day::*,
28 plain_time::*, plain_year_month::*, zoneddatetime::*,
29};
30
31use crate::{
32 Context, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,
33 builtins::{
34 BuiltInBuilder, BuiltInObject, IntrinsicObject,
35 temporal::calendar::get_temporal_calendar_slot_value_with_default,
36 },
37 context::intrinsics::Intrinsics,
38 js_string,
39 property::Attribute,
40 realm::Realm,
41 string::StaticJsStrings,
42};
43use temporal_rs::{
44 PlainDate as TemporalDate, ZonedDateTime as TemporalZonedDateTime,
45 partial::PartialZonedDateTime, primitive::FiniteF64,
46};
47use temporal_rs::{options::RelativeTo, partial::PartialDate};
48
49pub(crate) enum DateTimeValues {
51 Year,
52 Month,
53 MonthCode,
54 Week,
55 Day,
56 Hour,
57 Minute,
58 Second,
59 Millisecond,
60 Microsecond,
61 Nanosecond,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
78pub(crate) struct Temporal;
79
80impl BuiltInObject for Temporal {
81 const NAME: JsString = StaticJsStrings::TEMPORAL;
82}
83
84impl IntrinsicObject for Temporal {
85 fn init(realm: &Realm) {
86 BuiltInBuilder::with_intrinsic::<Self>(realm)
87 .static_property(
88 JsSymbol::to_string_tag(),
89 Self::NAME,
90 Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
91 )
92 .static_property(
93 js_string!("Now"),
94 realm.intrinsics().objects().now(),
95 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
96 )
97 .static_property(
98 js_string!("Duration"),
99 realm.intrinsics().constructors().duration().constructor(),
100 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
101 )
102 .static_property(
103 js_string!("Instant"),
104 realm.intrinsics().constructors().instant().constructor(),
105 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
106 )
107 .static_property(
108 js_string!("PlainDate"),
109 realm.intrinsics().constructors().plain_date().constructor(),
110 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
111 )
112 .static_property(
113 js_string!("PlainDateTime"),
114 realm
115 .intrinsics()
116 .constructors()
117 .plain_date_time()
118 .constructor(),
119 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
120 )
121 .static_property(
122 js_string!("PlainMonthDay"),
123 realm
124 .intrinsics()
125 .constructors()
126 .plain_month_day()
127 .constructor(),
128 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
129 )
130 .static_property(
131 js_string!("PlainTime"),
132 realm.intrinsics().constructors().plain_time().constructor(),
133 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
134 )
135 .static_property(
136 js_string!("PlainYearMonth"),
137 realm
138 .intrinsics()
139 .constructors()
140 .plain_year_month()
141 .constructor(),
142 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
143 )
144 .static_property(
145 js_string!("ZonedDateTime"),
146 realm
147 .intrinsics()
148 .constructors()
149 .zoned_date_time()
150 .constructor(),
151 Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
152 )
153 .build();
154 }
155
156 fn get(intrinsics: &Intrinsics) -> JsObject {
157 intrinsics.objects().temporal()
158 }
159}
160
161pub(crate) fn get_relative_to_option(
164 options: &JsObject,
165 context: &mut Context,
166) -> JsResult<Option<RelativeTo>> {
167 let value = options.get(js_string!("relativeTo"), context)?;
169 if value.is_undefined() {
171 return Ok(None);
172 }
173 if let Some(object) = value.as_object() {
177 if let Some(zdt) = object.downcast_ref::<ZonedDateTime>() {
179 return Ok(Some(RelativeTo::ZonedDateTime(zdt.inner.as_ref().clone())));
181 } else if let Some(date) = object.downcast_ref::<PlainDate>() {
183 return Ok(Some(RelativeTo::PlainDate(date.inner.clone())));
185 } else if let Some(dt) = object.downcast_ref::<PlainDateTime>() {
187 return Ok(Some(RelativeTo::PlainDate(dt.inner.clone().into())));
190 }
191 let calendar = get_temporal_calendar_slot_value_with_default(&object, context)?;
193 let (fields, timezone) = to_zoned_date_time_fields(
196 &object,
197 &calendar,
198 ZdtFieldsType::TimeZoneNotRequired,
199 context,
200 )?;
201 if timezone.is_none() {
209 return Ok(Some(RelativeTo::PlainDate(TemporalDate::from_partial(
210 PartialDate {
211 calendar_fields: fields.calendar_fields,
212 calendar,
213 },
214 None,
215 )?)));
216 }
217 let zdt = TemporalZonedDateTime::from_partial_with_provider(
219 PartialZonedDateTime {
220 fields,
221 timezone,
222 calendar,
223 },
224 None,
225 None,
226 None,
227 context.tz_provider(),
228 )?;
229 return Ok(Some(RelativeTo::ZonedDateTime(zdt)));
230 }
231 let Some(relative_to_str) = value.as_string() else {
234 return Err(JsNativeError::typ()
235 .with_message("relativeTo must be an object or string.")
236 .into());
237 };
238 Ok(Some(RelativeTo::try_from_str_with_provider(
240 &relative_to_str.to_std_string_escaped(),
241 context.tz_provider(),
242 )?))
243}
244
245pub(crate) fn is_partial_temporal_object(
247 value: &JsValue,
248 context: &mut Context,
249) -> JsResult<Option<JsObject>> {
250 let Some(obj) = value.as_object() else {
252 return Ok(None);
253 };
254
255 if obj.is::<PlainDate>()
260 || obj.is::<PlainDateTime>()
261 || obj.is::<PlainMonthDay>()
262 || obj.is::<PlainYearMonth>()
263 || obj.is::<PlainTime>()
264 || obj.is::<ZonedDateTime>()
265 {
266 return Ok(None);
267 }
268
269 let calendar_property = obj.get(js_string!("calendar"), context)?;
271 if !calendar_property.is_undefined() {
273 return Ok(None);
274 }
275 let time_zone_property = obj.get(js_string!("timeZone"), context)?;
277 if !time_zone_property.is_undefined() {
279 return Ok(None);
280 }
281 Ok(Some(obj))
283}
284
285impl JsValue {
286 pub(crate) fn to_finitef64(&self, context: &mut Context) -> JsResult<FiniteF64> {
287 let number = self.to_number(context)?;
288 let result = FiniteF64::try_from(number)?;
289 Ok(result)
290 }
291}
292
293fn extract_from_temporal_type<DF, DTF, YMF, MDF, ZDTF, Ret>(
294 object: &JsObject,
295 date_f: DF,
296 datetime_f: DTF,
297 year_month_f: YMF,
298 month_day_f: MDF,
299 zoned_datetime_f: ZDTF,
300) -> JsResult<Option<Ret>>
301where
302 DF: FnOnce(&PlainDate) -> JsResult<Option<Ret>>,
303 DTF: FnOnce(&PlainDateTime) -> JsResult<Option<Ret>>,
304 YMF: FnOnce(&PlainYearMonth) -> JsResult<Option<Ret>>,
305 MDF: FnOnce(&PlainMonthDay) -> JsResult<Option<Ret>>,
306 ZDTF: FnOnce(&ZonedDateTime) -> JsResult<Option<Ret>>,
307{
308 if let Some(date) = object.downcast_ref::<PlainDate>() {
309 return date_f(&date);
310 } else if let Some(dt) = object.downcast_ref::<PlainDateTime>() {
311 return datetime_f(&dt);
312 } else if let Some(ym) = object.downcast_ref::<PlainYearMonth>() {
313 return year_month_f(&ym);
314 } else if let Some(md) = object.downcast_ref::<PlainMonthDay>() {
315 return month_day_f(&md);
316 } else if let Some(dt) = object.downcast_ref::<ZonedDateTime>() {
317 return zoned_datetime_f(&dt);
318 }
319
320 Ok(None)
321}