Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = ["full"]
log = ["dep:log"]
experimental = ["tzdb"]
full = ["tzdb", "now"]
now = ["std", "dep:web-time"]
tzdb = ["dep:tzif", "std", "dep:jiff-tzdb", "dep:combine"]
std = []
112 changes: 112 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Library Architecture

TODO: FFI docs

This doc provides an overview of the layout of `temporal_rs`.

We will go over the Temporal Date/Time builtins, general primitives, and
utiltity crates.

## `temporal_rs` design considerations

`temporal_rs` is first and foremost designed to be a fully spec
compliant implementation of ECMAScript's Temporal date/time builtins.

As such, the main design consideration of `temporal_rs` is that it needs
to be able to service language interpreters / engines.

Thus, `temporal_rs` aims to provide an API along with tools to implement
Temporal while minimizing issue with integrating Temporal into engines.

## Date/Time builtins

The primary date & time builtins/components are located in the
`builtins` directory.

These builtins are then reexported from `lib.rs` to be available from
`temporal_rs`'s root module.

### Core vs. Native

`temporal_rs`'s builtins are split in two distinct directories `core`
and `native`. The core implementation contains the core implementation
of the Temporal builtins; meanwhile, the `native` implementation is a
Rust wrapper around the `core` implementation that simplifies some
"lower" level date/time API that may not be necessary for a general use
case.

### Core implementation

The core implementation is always publicly available, but may not be
available to import from the `temporal_rs`'s root.

The core implementation can be made available from the root by providing
the `--no-default-features` flag.

The core implementation exposes the Provider API that allows the user to
supply a "provider", or any type that implements the `TimeZoneProvider`
trait, for time zone data that the library can use to complete it's
calculations. This is useful from an engine / implementor perspective
because it allows the engine to source time zone data in their preferred
manner without locking them into a library specific implementation that
may or may not have side effects.

A `TimeZoneProvider` API on a core builtin will look like the below.

```rust
impl ZonedDateTime {
pub fn day_with_provider(&self, provider: &impl TimeZoneProvider) -> TemporalResult<u8> {
// Code goes here.
}
}
```

### Native implementation

The native implementation is only available via the "full" default
feature flag.

For the same reason that the Provider API is useful for language
implementors, it is a deterent from a general use case perspective. Most
people using a datetime library, outside of the self-proclaimed time
zone nerds, probably won't care from where their time zone data is being
sourced.

The native Rust wrapper of the core implementation provides a default
provider implementation to remove the need of the user to think or deal
with `TimeZoneProvider`.

```rust
impl ZonedDateTime {
pub fn day(&self) -> TemporalResult<u8> {
// Code goes here.
}
}
```

This greatly simplifies the API for general use cases.

## Primitives

<!-- Should IsoDate, IsoTime, and IsoDateTime be considered primitives? -->

`temporal_rs` has a primitive number implementation `FiniteF64` along
with a few date and time primitives: `IsoDate`, `IsoTime`,
`IsoDateTime`, and `EpochNanoseconds`.

`FiniteF64` allows an interface to translate between ECMAScript's number
type vs. `temporal_rs`'s strictly typed API.

Meanwhile the Date and Time primitives allow certain invariants to be
enforced on their records.

## Utiltiies

`temporal_rs` provides one implementation of the `TimeZoneProvider`
trait: `FsTzdbProvider`.

`FsTzdbProvider` reads from the file systems' tzdb and when not
available on the system, `FsTzdbProvider` relies on a prepackaged
`tzdb`.

<!-- TODO: add some more about parsers -->
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::str::FromStr;
use icu_calendar::types::{Era as IcuEra, MonthCode as IcuMonthCode, MonthInfo, YearInfo};

use crate::{
components::{
builtins::core::{
duration::{DateDuration, TimeDuration},
Duration, PlainDate, PlainDateTime, PlainMonthDay, PlainYearMonth,
},
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::iso::{constrain_iso_day, is_valid_iso_day};
use crate::options::ArithmeticOverflow;
use crate::{TemporalError, TemporalResult};

use crate::components::{calendar::Calendar, PartialDate};
use crate::builtins::core::{calendar::Calendar, PartialDate};

/// `ResolvedCalendarFields` represents the resolved field values necessary for
/// creating a Date from potentially partial values.
Expand Down Expand Up @@ -322,7 +322,7 @@ mod tests {
use tinystr::tinystr;

use crate::{
components::{calendar::Calendar, PartialDate},
builtins::core::{calendar::Calendar, PartialDate},
options::ArithmeticOverflow,
};

Expand Down
8 changes: 5 additions & 3 deletions src/components/date.rs → src/builtins/core/date.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
//! This module implements `Date` and any directly related algorithms.

use crate::{
components::{calendar::Calendar, duration::DateDuration, Duration, PlainDateTime},
builtins::core::{
calendar::Calendar, duration::DateDuration, Duration, PlainDateTime, PlainTime,
},
iso::{IsoDate, IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
ResolvedRoundingOptions, TemporalUnit,
},
parsers::{parse_date_time, IxdtfStringBuilder},
primitive::FiniteF64,
provider::NeverProvider,
TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
};
use alloc::{format, string::String};
Expand All @@ -17,8 +20,7 @@ use core::str::FromStr;
use super::{
calendar::{ascii_four_to_integer, month_to_month_code},
duration::{normalized::NormalizedDurationRecord, TimeDuration},
timezone::NeverProvider,
PlainMonthDay, PlainTime, PlainYearMonth,
PlainMonthDay, PlainYearMonth,
};
use tinystr::TinyAsciiStr;

Expand Down
15 changes: 7 additions & 8 deletions src/components/datetime.rs → src/builtins/core/datetime.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
//! This module implements `DateTime` any directly related algorithms.

use super::{
duration::normalized::{NormalizedDurationRecord, NormalizedTimeDuration},
Duration, PartialDate, PartialTime, PlainDate, PlainTime,
};
use crate::{
components::{calendar::Calendar, Instant},
builtins::core::{calendar::Calendar, Instant},
iso::{IsoDate, IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, DifferenceOperation, DifferenceSettings, DisplayCalendar,
ResolvedRoundingOptions, RoundingOptions, TemporalUnit, ToStringRoundingOptions,
},
parsers::{parse_date_time, IxdtfStringBuilder},
provider::NeverProvider,
temporal_assert, TemporalError, TemporalResult, TemporalUnwrap, TimeZone,
};
use alloc::string::String;
use core::{cmp::Ordering, str::FromStr};

use super::{
duration::normalized::{NormalizedDurationRecord, NormalizedTimeDuration},
timezone::NeverProvider,
Duration, PartialDate, PartialTime, PlainDate, PlainTime,
};
use tinystr::TinyAsciiStr;

/// A partial PlainDateTime record
Expand Down Expand Up @@ -693,7 +692,7 @@ mod tests {
use tinystr::{tinystr, TinyAsciiStr};

use crate::{
components::{
builtins::core::{
calendar::Calendar, duration::DateDuration, Duration, PartialDate, PartialDateTime,
PartialTime, PlainDateTime,
},
Expand Down
30 changes: 6 additions & 24 deletions src/components/duration.rs → src/builtins/core/duration.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! This module implements `Duration` along with it's methods and components.

use crate::{
components::{timezone::TimeZoneProvider, PlainDateTime, PlainTime},
builtins::core::{options::RelativeTo, PlainDateTime, PlainTime, ZonedDateTime},
iso::{IsoDateTime, IsoTime},
options::{
ArithmeticOverflow, RelativeTo, ResolvedRoundingOptions, RoundingIncrement,
RoundingOptions, TemporalUnit, ToStringRoundingOptions,
ArithmeticOverflow, ResolvedRoundingOptions, RoundingIncrement, RoundingOptions,
TemporalUnit, ToStringRoundingOptions,
},
parsers::{FormattableDuration, Precision},
primitive::FiniteF64,
provider::TimeZoneProvider,
temporal_assert, Sign, TemporalError, TemporalResult,
};
use alloc::format;
Expand All @@ -25,9 +26,6 @@ use num_traits::AsPrimitive;

use self::normalized::NormalizedTimeDuration;

#[cfg(feature = "experimental")]
use crate::components::timezone::TZ_PROVIDER;

mod date;
pub(crate) mod normalized;
mod time;
Expand All @@ -40,8 +38,6 @@ pub use date::DateDuration;
#[doc(inline)]
pub use time::TimeDuration;

use super::ZonedDateTime;

/// A `PartialDuration` is a Duration that may have fields not set.
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
pub struct PartialDuration {
Expand Down Expand Up @@ -91,7 +87,7 @@ impl core::fmt::Display for Duration {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(
&self
.to_temporal_string(ToStringRoundingOptions::default())
.as_temporal_string(ToStringRoundingOptions::default())
.expect("Duration must return a valid string with default options."),
)
}
Expand Down Expand Up @@ -639,7 +635,7 @@ impl Duration {
}
}

pub fn to_temporal_string(&self, options: ToStringRoundingOptions) -> TemporalResult<String> {
pub fn as_temporal_string(&self, options: ToStringRoundingOptions) -> TemporalResult<String> {
if options.smallest_unit == Some(TemporalUnit::Hour)
|| options.smallest_unit == Some(TemporalUnit::Minute)
{
Expand Down Expand Up @@ -732,20 +728,6 @@ pub fn duration_to_formattable(
})
}

#[cfg(feature = "experimental")]
impl Duration {
pub fn round(
&self,
options: RoundingOptions,
relative_to: Option<RelativeTo>,
) -> TemporalResult<Self> {
let provider = TZ_PROVIDER
.lock()
.map_err(|_| TemporalError::general("Unable to acquire lock"))?;
self.round_with_provider(options, relative_to, &*provider)
}
}

// TODO: Update, optimize, and fix the below. is_valid_duration should probably be generic over a T.

const TWO_POWER_FIFTY_THREE: i128 = 9_007_199_254_740_992;
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ use core::{num::NonZeroU128, ops::Add};
use num_traits::{AsPrimitive, Euclid, FromPrimitive};

use crate::{
components::{
timezone::{TimeZone, TimeZoneProvider},
PlainDate, PlainDateTime,
},
builtins::core::{timezone::TimeZone, PlainDate, PlainDateTime},
iso::{IsoDate, IsoDateTime},
options::{
ArithmeticOverflow, Disambiguation, ResolvedRoundingOptions, TemporalRoundingMode,
TemporalUnit,
},
primitive::FiniteF64,
provider::TimeZoneProvider,
rounding::{IncrementRounder, Round},
TemporalError, TemporalResult, TemporalUnwrap, NS_PER_DAY,
};
Expand Down
Loading
Loading