Skip to main content

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}