boa_engine/builtins/temporal/now.rs
1//! Boa's implementation of `Temporal.Now` ECMAScript global namespace object.
2
3use crate::{
4 Context, JsArgs, JsObject, JsResult, JsString, JsSymbol, JsValue,
5 builtins::{BuiltInBuilder, BuiltInObject, IntrinsicObject},
6 context::intrinsics::Intrinsics,
7 js_string,
8 property::Attribute,
9 realm::Realm,
10 string::StaticJsStrings,
11};
12use temporal_rs::{
13 TemporalResult, TimeZone,
14 host::{HostClock, HostHooks, HostTimeZone},
15 now::Now as InnerNow,
16 provider::TimeZoneProvider,
17 unix_time::EpochNanoseconds,
18};
19
20#[cfg(feature = "system-time-zone")]
21use temporal_rs::TemporalError;
22
23use super::{
24 create_temporal_date, create_temporal_datetime, create_temporal_instant, create_temporal_time,
25 create_temporal_zoneddatetime, to_temporal_timezone_identifier,
26};
27
28/// The `Temporal.Now` global namespace object
29///
30/// More information:
31///
32/// - [ECMAScript Temporal proposal][spec]
33/// - [MDN reference][mdn]
34///
35/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-now-object
36/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now
37#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38pub struct Now;
39
40impl IntrinsicObject for Now {
41 /// Initializes the `Temporal.Now` object.
42 fn init(realm: &Realm) {
43 // is an ordinary object.
44 // has a [[Prototype]] internal slot whose value is %Object.prototype%.
45 // is not a function object.
46 // does not have a [[Construct]] internal method; it cannot be used as a constructor with the new operator.
47 // does not have a [[Call]] internal method; it cannot be invoked as a function.
48 BuiltInBuilder::with_intrinsic::<Self>(realm)
49 .static_property(
50 JsSymbol::to_string_tag(),
51 StaticJsStrings::NOW_TAG,
52 Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
53 )
54 .static_method(Self::time_zone_id, js_string!("timeZoneId"), 0)
55 .static_method(Self::instant, js_string!("instant"), 0)
56 .static_method(Self::plain_date_time_iso, js_string!("plainDateTimeISO"), 0)
57 .static_method(Self::zoned_date_time_iso, js_string!("zonedDateTimeISO"), 0)
58 .static_method(Self::plain_date_iso, js_string!("plainDateISO"), 0)
59 .static_method(Self::plain_time_iso, js_string!("plainTimeISO"), 0)
60 .build();
61 }
62
63 fn get(intrinsics: &Intrinsics) -> JsObject {
64 intrinsics.objects().now()
65 }
66}
67
68impl BuiltInObject for Now {
69 const NAME: JsString = StaticJsStrings::NOW_NAME;
70}
71
72impl Now {
73 /// 2.2.1 `Temporal.Now.timeZoneId ( )`
74 ///
75 /// Returns the currently active system time zone identifier.
76 ///
77 /// More information:
78 /// - [ECMAScript specification][spec]
79 /// - [MDN reference][mdn]
80 ///
81 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.timezone
82 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/timeZoneId
83 fn time_zone_id(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
84 // TODO: this should be optimized once system time zone is in context
85 // 1. Return ! SystemTimeZone().
86 let context: &Context = context;
87 let time_zone = context.get_system_time_zone(context.timezone_provider())?;
88 Ok(JsString::from(time_zone.identifier_with_provider(context.timezone_provider())?).into())
89 }
90
91 /// 2.2.2 `Temporal.Now.instant()`
92 ///
93 /// Returns the current time as an `Temporal.Instant`.
94 ///
95 /// More information:
96 /// - [ECMAscript specification][spec]
97 /// - [MDN reference][mdn]
98 ///
99 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.instant
100 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/instant
101 fn instant(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
102 let now: InnerNow<&Context> = InnerNow::new(context);
103 let instant = now.instant()?;
104 create_temporal_instant(instant, None, context)
105 }
106
107 /// 2.2.3 `Temporal.Now.plainDateTimeISO ( [ temporalTimeZoneLike ] )`
108 ///
109 /// Returns the current date and time as a `Temporal.PlainDateTime` with an ISO8601 calendar.
110 ///
111 /// Takes an optional time zone, which defaults to the system time zone if undefined.
112 ///
113 /// More information:
114 /// - [ECMAscript specification][spec]
115 /// - [MDN reference][mdn]
116 ///
117 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetimeiso
118 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainDateTimeISO
119 fn plain_date_time_iso(
120 _: &JsValue,
121 args: &[JsValue],
122 context: &mut Context,
123 ) -> JsResult<JsValue> {
124 let time_zone = args
125 .get_or_undefined(0)
126 .map(|v| to_temporal_timezone_identifier(v, context))
127 .transpose()?;
128
129 let now: InnerNow<&Context> = InnerNow::new(context);
130
131 let datetime =
132 now.plain_date_time_iso_with_provider(time_zone, context.timezone_provider())?;
133 create_temporal_datetime(datetime, None, context).map(Into::into)
134 }
135
136 /// 2.2.4 `Temporal.Now.zonedDateTimeISO ( [ temporalTimeZoneLike ] )`
137 ///
138 /// Returns the current date and time as a `Temporal.ZonedDateTime` with an ISO8601 calendar.
139 ///
140 /// Takes an optional time zone, which defaults to the system time zone if undefined.
141 ///
142 /// More information:
143 /// - [ECMAscript specification][spec]
144 /// - [MDN reference][mdn]
145 ///
146 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.zoneddatetimeiso
147 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/zonedDateTimeISO
148 fn zoned_date_time_iso(
149 _: &JsValue,
150 args: &[JsValue],
151 context: &mut Context,
152 ) -> JsResult<JsValue> {
153 let time_zone = args
154 .get_or_undefined(0)
155 .map(|v| to_temporal_timezone_identifier(v, context))
156 .transpose()?;
157
158 let now: InnerNow<&Context> = InnerNow::new(context);
159 let zdt = now.zoned_date_time_iso_with_provider(time_zone, context.timezone_provider())?;
160 create_temporal_zoneddatetime(zdt, None, context).map(Into::into)
161 }
162
163 /// 2.2.5 `Temporal.Now.plainDateISO ( [ temporalTimeZoneLike ] )`
164 ///
165 /// Returns the current date as a `Temporal.PlainDate` with an ISO8601 calendar.
166 ///
167 /// Takes an optional time zone, which defaults to the system time zone if undefined.
168 ///
169 /// More information:
170 /// - [ECMAscript specification][spec]
171 /// - [MDN reference][mdn]
172 ///
173 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.plaindateiso
174 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainDateISO
175 fn plain_date_iso(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
176 let time_zone = args
177 .get_or_undefined(0)
178 .map(|v| to_temporal_timezone_identifier(v, context))
179 .transpose()?;
180
181 let now: InnerNow<&Context> = InnerNow::new(context);
182
183 let pd = now.plain_date_iso_with_provider(time_zone, context.timezone_provider())?;
184 create_temporal_date(pd, None, context).map(Into::into)
185 }
186
187 /// 2.2.6 `Temporal.Now.plainTimeISO ( [ temporalTimeZoneLike ] )`
188 ///
189 /// Returns the current time as a `Temporal.PlainTime` with an ISO8601 calendar.
190 ///
191 /// Takes an optional time zone, which defaults to the system time zone if undefined.
192 ///
193 /// More information:
194 /// - [ECMAscript specification][spec]
195 /// - [MDN reference][mdn]
196 ///
197 /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.plaintimeiso
198 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainTimeISO
199 fn plain_time_iso(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
200 let time_zone = args
201 .get_or_undefined(0)
202 .map(|v| to_temporal_timezone_identifier(v, context))
203 .transpose()?;
204
205 let now: InnerNow<&Context> = InnerNow::new(context);
206
207 let pt = now.plain_time_iso_with_provider(time_zone, context.timezone_provider())?;
208 create_temporal_time(pt, None, context).map(Into::into)
209 }
210}
211
212impl HostHooks for &Context {}
213
214impl HostClock for &Context {
215 fn get_host_epoch_nanoseconds(&self) -> TemporalResult<EpochNanoseconds> {
216 // Temporal needs actual Unix epoch time, not monotonic time
217 let millis = self.clock().system_time_millis();
218 let nanos = i128::from(millis) * 1_000_000;
219 Ok(EpochNanoseconds::from(nanos))
220 }
221}
222
223impl HostTimeZone for &Context {
224 fn get_host_time_zone(
225 &self,
226 provider: &(impl TimeZoneProvider + ?Sized),
227 ) -> TemporalResult<TimeZone> {
228 #[cfg(not(feature = "system-time-zone"))]
229 {
230 Ok(TimeZone::utc_with_provider(provider))
231 }
232 #[cfg(feature = "system-time-zone")]
233 {
234 iana_time_zone::get_timezone()
235 .map_err(|_| {
236 TemporalError::range().with_message("Unable to fetch system time zone")
237 })
238 .and_then(|id| TimeZone::try_from_str_with_provider(&id, provider))
239 }
240 }
241}