Skip to content

Commit

Permalink
Add SystemHooks trait and adjust now as needed
Browse files Browse the repository at this point in the history
  • Loading branch information
nekevss committed Jan 24, 2025
1 parent 40e5dfb commit be816ab
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 66 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ combine = { workspace = true, optional = true }
web-time = { workspace = true, optional = true }

[features]
default = ["now"]
default = ["sys"]
log = ["dep:log"]
compiled_data = ["tzdb"]
now = ["std", "dep:web-time"]
sys = ["std", "dep:web-time"]
tzdb = ["dep:tzif", "std", "dep:jiff-tzdb", "dep:combine"]
std = []
13 changes: 10 additions & 3 deletions src/builtins/compiled/now.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use crate::builtins::{
};
use crate::{TemporalError, TemporalResult, TimeZone};

#[cfg(feature = "sys")]
use crate::sys::DefaultSystemHooks;

#[cfg(feature = "sys")]
impl Now {
/// Returns the current system time as a [`PlainDateTime`] with an optional
/// [`TimeZone`].
Expand All @@ -13,7 +17,8 @@ impl Now {
let provider = TZ_PROVIDER
.lock()
.map_err(|_| TemporalError::general("Unable to acquire lock"))?;
Now::plain_datetime_iso_with_provider(timezone, &*provider).map(Into::into)
Now::plain_datetime_iso_with_hooks_and_provider(timezone, &DefaultSystemHooks, &*provider)
.map(Into::into)
}

/// Returns the current system time as a [`PlainDate`] with an optional
Expand All @@ -24,7 +29,8 @@ impl Now {
let provider = TZ_PROVIDER
.lock()
.map_err(|_| TemporalError::general("Unable to acquire lock"))?;
Now::plain_date_iso_with_provider(timezone, &*provider).map(Into::into)
Now::plain_date_iso_with_hooks_and_provider(timezone, &DefaultSystemHooks, &*provider)
.map(Into::into)
}

/// Returns the current system time as a [`PlainTime`] with an optional
Expand All @@ -35,6 +41,7 @@ impl Now {
let provider = TZ_PROVIDER
.lock()
.map_err(|_| TemporalError::general("Unable to acquire lock"))?;
Now::plain_time_iso_with_provider(timezone, &*provider).map(Into::into)
Now::plain_time_iso_with_hooks_and_provider(timezone, &DefaultSystemHooks, &*provider)
.map(Into::into)
}
}
2 changes: 0 additions & 2 deletions src/builtins/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ mod time;
mod year_month;
pub(crate) mod zoneddatetime;

#[cfg(feature = "now")]
mod now;

#[cfg(feature = "now")]
#[doc(inline)]
pub use now::Now;

Expand Down
111 changes: 67 additions & 44 deletions src/builtins/core/now.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//! The Temporal Now component
use crate::iso::IsoDateTime;
use crate::provider::TimeZoneProvider;
use crate::{iso::IsoDateTime, time::EpochNanoseconds, TemporalUnwrap};
use crate::{sys, TemporalResult};
use crate::sys::SystemHooks;
use crate::TemporalResult;
use alloc::string::String;

use num_traits::FromPrimitive;
#[cfg(feature = "sys")]
use crate::sys::DefaultSystemHooks;

use super::{
calendar::Calendar, timezone::TimeZone, Instant, PlainDate, PlainDateTime, PlainTime,
Expand All @@ -16,24 +18,47 @@ use super::{
pub struct Now;

impl Now {
/// Returns the current instant
pub fn instant() -> TemporalResult<Instant> {
system_instant()
pub fn instant_with_hooks(system_hooks: &impl SystemHooks) -> TemporalResult<Instant> {
let epoch_nanoseconds = system_hooks.get_system_nanoseconds()?;
Ok(Instant::from(epoch_nanoseconds))
}

/// Returns the current time zone.
pub fn time_zone_id() -> TemporalResult<String> {
sys::get_system_tz_identifier()
pub fn system_time_zone_identifier_with_hooks(
system_hooks: &impl SystemHooks,
) -> TemporalResult<String> {
system_hooks.get_system_time_zone()
}

pub fn system_datetime_with_hooks_and_provider(
time_zone: Option<TimeZone>,
system_hooks: &impl SystemHooks,
provider: &impl TimeZoneProvider,
) -> TemporalResult<IsoDateTime> {
// 1. If temporalTimeZoneLike is undefined, then
// a. Let timeZone be SystemTimeZoneIdentifier().
// 2. Else,
// a. Let timeZone be ? ToTemporalTimeZoneIdentifier(temporalTimeZoneLike).
let tz = time_zone.unwrap_or(TimeZone::IanaIdentifier(
system_hooks.get_system_time_zone()?,
));
// 3. Let epochNs be SystemUTCEpochNanoseconds().
let epoch_ns = system_hooks.get_system_nanoseconds()?;
// 4. Return GetISODateTimeFor(timeZone, epochNs).
tz.get_iso_datetime_for(&Instant::from(epoch_ns), provider)
}

/// Returns the current system time as a `ZonedDateTime` with an ISO8601 calendar.
///
/// The time zone will be set to either the `TimeZone` if a value is provided, or
/// according to the system timezone if no value is provided.
pub fn zoneddatetime_iso(timezone: Option<TimeZone>) -> TemporalResult<ZonedDateTime> {
let timezone =
timezone.unwrap_or(TimeZone::IanaIdentifier(sys::get_system_tz_identifier()?));
let instant = system_instant()?;
pub fn zoneddatetime_iso_with_hooks(
timezone: Option<TimeZone>,
system_hooks: &impl SystemHooks,
) -> TemporalResult<ZonedDateTime> {
let timezone = timezone.unwrap_or(TimeZone::IanaIdentifier(
system_hooks.get_system_time_zone()?,
));
let instant = Self::instant_with_hooks(system_hooks)?;
Ok(ZonedDateTime::new_unchecked(
instant,
Calendar::default(),
Expand All @@ -42,17 +67,31 @@ impl Now {
}
}

#[cfg(feature = "sys")]
impl Now {
/// Returns the current instant
pub fn instant() -> TemporalResult<Instant> {
Self::instant_with_hooks(&DefaultSystemHooks)
}

/// Returns the current time zone.
pub fn time_zone_identifier() -> TemporalResult<String> {
Self::system_time_zone_identifier_with_hooks(&DefaultSystemHooks)
}
}

impl Now {
/// Returns the current system time as a `PlainDateTime` with an ISO8601 calendar.
///
/// The time zone used to calculate the `PlainDateTime` will be set to either the
/// `TimeZone` if a value is provided, or according to the system timezone if no
/// value is provided.
pub fn plain_datetime_iso_with_provider(
pub fn plain_datetime_iso_with_hooks_and_provider(
timezone: Option<TimeZone>,
system_hooks: &impl SystemHooks,
provider: &impl TimeZoneProvider,
) -> TemporalResult<PlainDateTime> {
let iso = system_datetime(timezone, provider)?;
let iso = Self::system_datetime_with_hooks_and_provider(timezone, system_hooks, provider)?;
Ok(PlainDateTime::new_unchecked(iso, Calendar::default()))
}

Expand All @@ -61,11 +100,12 @@ impl Now {
/// The time zone used to calculate the `PlainDate` will be set to either the
/// `TimeZone` if a value is provided, or according to the system timezone if no
/// value is provided.
pub fn plain_date_iso_with_provider(
pub fn plain_date_iso_with_hooks_and_provider(
timezone: Option<TimeZone>,
system_hooks: &impl SystemHooks,
provider: &impl TimeZoneProvider,
) -> TemporalResult<PlainDate> {
let iso = system_datetime(timezone, provider)?;
let iso = Self::system_datetime_with_hooks_and_provider(timezone, system_hooks, provider)?;
Ok(PlainDate::new_unchecked(iso.date, Calendar::default()))
}

Expand All @@ -74,53 +114,36 @@ impl Now {
/// The time zone used to calculate the `PlainTime` will be set to either the
/// `TimeZone` if a value is provided, or according to the system timezone if no
/// value is provided.
pub fn plain_time_iso_with_provider(
pub fn plain_time_iso_with_hooks_and_provider(
timezone: Option<TimeZone>,
system_hooks: &impl SystemHooks,
provider: &impl TimeZoneProvider,
) -> TemporalResult<PlainTime> {
let iso = system_datetime(timezone, provider)?;
let iso = Self::system_datetime_with_hooks_and_provider(timezone, system_hooks, provider)?;
Ok(PlainTime::new_unchecked(iso.time))
}
}

fn system_datetime(
tz: Option<TimeZone>,
provider: &impl TimeZoneProvider,
) -> TemporalResult<IsoDateTime> {
// 1. If temporalTimeZoneLike is undefined, then
// a. Let timeZone be SystemTimeZoneIdentifier().
// 2. Else,
// a. Let timeZone be ? ToTemporalTimeZoneIdentifier(temporalTimeZoneLike).
let tz = tz.unwrap_or(TimeZone::IanaIdentifier(sys::get_system_tz_identifier()?));
// 3. Let epochNs be SystemUTCEpochNanoseconds().
// TODO: Handle u128 -> i128 better for system nanoseconds
let epoch_ns = EpochNanoseconds::try_from(sys::get_system_nanoseconds()?)?;
// 4. Return GetISODateTimeFor(timeZone, epochNs).
tz.get_iso_datetime_for(&Instant::from(epoch_ns), provider)
}

fn system_instant() -> TemporalResult<Instant> {
let nanos = sys::get_system_nanoseconds()?;
Instant::try_new(i128::from_u128(nanos).temporal_unwrap()?)
}

#[cfg(feature = "tzdb")]
#[cfg(all(feature = "tzdb", feature = "sys"))]
#[cfg(test)]
mod tests {
use std::thread;
use std::time::Duration as StdDuration;

use crate::builtins::core::Now;
use crate::{options::DifferenceSettings, tzdb::FsTzdbProvider};
use crate::{options::DifferenceSettings, sys::DefaultSystemHooks, tzdb::FsTzdbProvider};

#[test]
fn now_datetime_test() {
let provider = &FsTzdbProvider::default();
let system_hooks = DefaultSystemHooks;
let sleep = 2;

let before = Now::plain_datetime_iso_with_provider(None, provider).unwrap();
let before =
Now::plain_datetime_iso_with_hooks_and_provider(None, &system_hooks, provider).unwrap();
thread::sleep(StdDuration::from_secs(sleep));
let after = Now::plain_datetime_iso_with_provider(None, provider).unwrap();
let after =
Now::plain_datetime_iso_with_hooks_and_provider(None, &system_hooks, provider).unwrap();

let diff = after.since(&before, DifferenceSettings::default()).unwrap();

Expand Down
9 changes: 2 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,12 @@ pub mod options;
pub mod parsers;
pub mod primitive;
pub mod provider;
pub mod sys;

mod epoch_nanoseconds;

mod builtins;

#[cfg(feature = "now")]
mod sys;

#[cfg(feature = "tzdb")]
pub mod tzdb;

Expand Down Expand Up @@ -97,13 +95,10 @@ pub mod time {
}

pub use crate::builtins::{
calendar::Calendar, core::timezone::TimeZone, DateDuration, Duration, Instant, PlainDate,
calendar::Calendar, core::timezone::TimeZone, DateDuration, Duration, Instant, Now, PlainDate,
PlainDateTime, PlainMonthDay, PlainTime, PlainYearMonth, TimeDuration, ZonedDateTime,
};

#[cfg(feature = "now")]
pub use crate::builtins::Now;

/// A library specific trait for unwrapping assertions.
pub(crate) trait TemporalUnwrap {
type Output;
Expand Down
37 changes: 29 additions & 8 deletions src/sys.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,42 @@
use alloc::string::{String, ToString};
use alloc::string::String;

use crate::{TemporalError, TemporalResult};
use crate::{time::EpochNanoseconds, TemporalResult};

#[cfg(feature = "sys")]
use crate::TemporalError;
#[cfg(feature = "sys")]
use alloc::string::ToString;
#[cfg(feature = "sys")]
use web_time::{SystemTime, UNIX_EPOCH};

// TODO: Need to implement SystemTime handling for non_std.

pub trait SystemHooks {
/// Returns the current system time in `EpochNanoseconds`.
fn get_system_nanoseconds(&self) -> TemporalResult<EpochNanoseconds>;
/// Returns the current system IANA Time Zone Identifier.
fn get_system_time_zone(&self) -> TemporalResult<String>;
}

#[cfg(feature = "sys")]
pub struct DefaultSystemHooks;

#[cfg(feature = "sys")]
impl SystemHooks for DefaultSystemHooks {
fn get_system_nanoseconds(&self) -> TemporalResult<EpochNanoseconds> {
EpochNanoseconds::try_from(get_system_nanoseconds()?)
}

fn get_system_time_zone(&self) -> TemporalResult<String> {
iana_time_zone::get_timezone().map_err(|e| TemporalError::general(e.to_string()))
}
}

/// Returns the system time in nanoseconds.
#[cfg(feature = "now")]
#[cfg(feature = "sys")]
pub(crate) fn get_system_nanoseconds() -> TemporalResult<u128> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|e| TemporalError::general(e.to_string()))
.map(|d| d.as_nanos())
}

/// Returns the system tz identifier
pub(crate) fn get_system_tz_identifier() -> TemporalResult<String> {
iana_time_zone::get_timezone().map_err(|e| TemporalError::general(e.to_string()))
}

0 comments on commit be816ab

Please sign in to comment.