-
Notifications
You must be signed in to change notification settings - Fork 6
temporal_rs v0.1 release post #210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
b7db68a
4b3aa94
f1ff7fd
407bdb8
39ad73f
bb39730
c2a2642
0df2d73
6e52045
c6d19b3
2714a84
2b9fcd0
6728cab
a0708b0
103f65e
5f687ed
6be2ded
807ac64
e4116cb
63b36ae
d72acbf
9b2f76b
4c3ec30
dfd6351
cd29081
cc068a2
0b56ed8
6d6c2be
ce2f674
5f61010
91b1f5a
bf68b50
b996a63
d6db7c9
8997b46
567e3e4
6c5daea
2e5d631
e489191
be5b408
217d2d1
01f4a50
5d3cd82
7162e9d
178118c
08c3adf
915b5fd
5a1c174
6fa9f07
9d6e17a
b58db37
1742a3f
8049024
3314af8
b439c19
1706ff3
006f7ba
3ba5e87
ccf5b44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,385 @@ | ||||||
# Temporal_rs release v0.1 | ||||||
|
||||||
After almost 2+ years of development, we're pleased to announce the 0.1 | ||||||
release of `temporal_rs`. A calendar and time zone aware Rust date/time | ||||||
library based on ECMAScript's Temporal API. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
`temporal_rs` is a highly conformant implementation of the Temporal API | ||||||
in Rust that can be used in native Rust code or embedded into ECMAScript | ||||||
engines / interpreters to support their implementations. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
Currently, `temporal_rs` is being used by Boa, Kiesel, V8, and Yavashark | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
for their Temporal implementations (but more on that later). | ||||||
|
||||||
To celebrate the 0.1 release of `temporal_rs`, we'll cover a short | ||||||
background of the Temporal implementation in Boa and why `temporal_rs` | ||||||
was split into it's own crate, we'll go over the libraries general | ||||||
design, then we'll walk through a couple brief examples of using | ||||||
`temporal_rs`, and finally we'll talk about the FFI and engine adoption. | ||||||
|
||||||
|
||||||
## Some background and history | ||||||
|
||||||
In this section, we'll reflect on the overall implementation, some | ||||||
general difficulties we had along with lessons learned. | ||||||
|
||||||
The temporal implementation in Boa began over two years ago and | ||||||
culminated in a absolutely massive PR, | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
[#3277](https://github.com/boa-dev/boa/pull/3277) (ASIDE from nekevss: | ||||||
mea culpa). | ||||||
|
||||||
The PR itself stubbed out a lot of the methods, implemented some | ||||||
Duration and Instant functionality, and started the support for custom | ||||||
calendars. There were, however, 2 major take aways from this PR: first, | ||||||
Temporal is a massive specification update; and second, there is a lot | ||||||
of room to potentially optimize Temporal if we did not deal with | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
`JsValue` directly. | ||||||
|
||||||
After a couple weeks, the question came up amongst the maintainers: | ||||||
could we separate the Boa's implementation off into a completely | ||||||
separate library? Sure, why not. | ||||||
|
||||||
The first commit of then `boa_temporal` occurred in PR | ||||||
[#3461](https://github.com/boa-dev/boa/pull/3461), which moved the | ||||||
majority of the existing functionality into a separate crate with the | ||||||
primary concern at the time of being able to support custom calendars, | ||||||
which was then ported into it's | ||||||
[own repository later](https://github.com/boa-dev/temporal/pull/1) a | ||||||
couple months later. | ||||||
|
||||||
These early versions of `temporal_rs` were vastly different than the 0.1 | ||||||
release version, and it can be easily seen with a short glance through | ||||||
the first PR. `temporal_rs` needed to support custom calendars and time | ||||||
zones. In order to do this, each calendar was generic on a | ||||||
`CalendarProtocol` trait. | ||||||
|
||||||
So to create a new date in the early versions of `temporal_rs`, you | ||||||
would have something like the following code: | ||||||
|
||||||
```rust | ||||||
use temporal_rs::{Date, Calendar, options::ArithmeticOverflow}; | ||||||
let date = Date::new_with_overflow( | ||||||
2025, | ||||||
9, | ||||||
21, | ||||||
Calendar::<()>::from_str("iso8601").unwrap(), | ||||||
ArithmeticOverflow::Reject | ||||||
).unwrap(); | ||||||
``` | ||||||
|
||||||
Luckily, custom calendar and time zone were removed from the | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
specification. In the first half of 2024, so `temporal_rs` was able to | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
remove that support, which greatly benefitted the entire API. For | ||||||
instance, here's the same code in the 0.1 version of `temporal_rs`: | ||||||
|
||||||
```rust | ||||||
use temporal_rs::PlainDate; | ||||||
let date = PlainDate::try_new_iso(2025, 9, 21).unwrap(); | ||||||
``` | ||||||
|
||||||
That was 2024 though, we're in September of 2025, so what's happened | ||||||
since the crate was initially split off from Boa over a year and a half | ||||||
ago? Well, plenty! | ||||||
|
||||||
- In early 2024 the internal algorithm for `Duration` was overhauled in | ||||||
the specification, so `temporal_rs` had a complete rewrite of | ||||||
`Duration`. | ||||||
- `Duration` moved from using `f64` internally to `FiniteF64`, and then | ||||||
to non-floating-point integers. | ||||||
- We moved from a large `TemporalFields` type to the "Partial" objects | ||||||
to adapt for JavaScript property bags. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
- A good portion of missing method implementations were added as well. | ||||||
- Internal utility methods were moved to the Neri-Schneider algorithms. | ||||||
|
||||||
In general, the implementation was moving along at a pretty decent pace | ||||||
and would continue to well into roughly April of 2025 (mostly helped | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
along by a group of students from the University of Bergen who began | ||||||
helping with the implementation in January 2025), but there was one | ||||||
final hurdle: time zone data and `ZonedDateTime`. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
Time zones and time zone data are a topic for a completely different | ||||||
blog post in the future. But suffice to say, it took a little time, and | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
the `ZonedDateTime` implementation developed alongside the development | ||||||
of time zone data. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
The work began to stub out the general support of time zone data | ||||||
sourcing and `ZonedDateTime` in November 2024. Finally after almost 10 | ||||||
months of general work. The last major updates to time zone data | ||||||
sourcing was merged at the beginning of September in PR | ||||||
[#537](https://github.com/boa-dev/temporal/pull/537) and | ||||||
[#538](https://github.com/boa-dev/temporal/pull/538) and as a result, we | ||||||
were finally able to stabilize `temporal_rs`'s API for a 0.1 release. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
That's it for our brief background on `temporal_rs`. | ||||||
|
||||||
Date and time is hard, and there is a lot that goes into it, especially | ||||||
when it comes to calendars and time zones. But that also doesn't mean | ||||||
its not interesting. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
## Temporal API overview | ||||||
|
||||||
The Temporal API focuses on a group of 8 date and time types, each of | ||||||
which corresponds to a different aspect of date and time with varying | ||||||
support for `Calendar`s and `TimeZone`s. | ||||||
|
||||||
| Temporal type | Category | Calendar support | Time zone support | | ||||||
| -------------- | --------------------------------- | ---------------- | ----------------- | | ||||||
| PlainDate | Calendar date | yes | no | | ||||||
| PlainTime | Wall-clock time | no | no | | ||||||
| PlainDateTime | Calendar date and wall-clock time | yes | no | | ||||||
| ZonedDateTime | Calendar date and exact time | yes | yes | | ||||||
| Instant | Exact time | no | no | | ||||||
| Duration | None | no | no | | ||||||
|
||||||
| PlainYearMonth | Calendar date | yes | no | | ||||||
| PlainMonthDay | Calendar date | yes | no | | ||||||
|
||||||
There is also the `Now`, which provides access to the current host | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
system time. This can then be used to map the current `Instant` to any | ||||||
of the above Temporal types. | ||||||
|
||||||
The types in the same categories will share similar APIs that are | ||||||
related to that category. For instance, all types that support a | ||||||
calendar date will have a `with_calendar` method as well as calendar | ||||||
date accessors. The exception being PlainYearMonth and PlainMonthDay | ||||||
which are missing their day and year, respectively ... for all intents | ||||||
and purposes. | ||||||
|
||||||
## `temporal_rs` design overview | ||||||
|
||||||
`temporal_rs` in general implements large portions of the specification | ||||||
directly in the codebase. However, it does still have some dependencies, | ||||||
which can be broken down into 4 main groups. | ||||||
|
||||||
1. Time zone data, for sourcing time zone data | ||||||
2. Calendrical calculations, for handling non-ISO calendar calculations | ||||||
3. RFC9557 parsing, for parsing of RFC9557's internet extended date/time | ||||||
format (IXDTF) | ||||||
4. Utilities | ||||||
|
||||||
 | ||||||
|
||||||
Notably, the dependencies that are highlighted in purple come from | ||||||
ICU4X. ICU4X is a phenomenal Rust project that takes a new approach to | ||||||
Unicode's ICU in order to make a new, more modular version of ICU. | ||||||
|
||||||
While ICU4X provides the majority of the internationalization (i18n), | ||||||
Unicode, and formatting focused functionality, `temporal_rs` builds on | ||||||
top of ICU4X to provide an ECMAScript compliant date/time API for both | ||||||
native Rust and ECMAScript implementers. | ||||||
|
||||||
### Time zone data | ||||||
|
||||||
While we plan to go into time zones in a completely separate post, one | ||||||
of `temporal_rs`'s primary design decisions was to leave time zone data | ||||||
sourcing as customizable with available default sources. The time zone | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
data sourcing functionality is provided by `timezone_provider`, a sister | ||||||
crate of `temporal_rs` that provides a project agnostic crate alongside | ||||||
default trait implementations for sourcing time zone data. | ||||||
|
||||||
There default trait implementations currently consist of: | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
- `CompiledTzdbProvider` (current default), a provider that parses time | ||||||
zone data at runtime using data compiled into the binary. | ||||||
- `FsTzdbProvider`, a provider that parses time zone data at runtime | ||||||
using the file system time zone database (if it exists for that OS). | ||||||
- `ZoneInfo64TzdbProvider`, a provider using ICU's zoneinfo64 resource | ||||||
bundle. | ||||||
|
||||||
We hope to have a zerocopy compiled timezone provider available in the | ||||||
near future. | ||||||
|
||||||
## Using `temporal_rs` | ||||||
|
||||||
Let's dive into using `temporal_rs` from Rust. | ||||||
|
||||||
### Setup | ||||||
|
||||||
First, add `temporal_rs` as a dependency to your project using cargo: | ||||||
|
||||||
```bash | ||||||
cargo add temporal_rs | ||||||
``` | ||||||
|
||||||
Or include the below in your project's `Cargo.toml`. | ||||||
|
||||||
```toml | ||||||
temporal_rs = "0.1.0" | ||||||
``` | ||||||
|
||||||
By default, `temporal_rs` will use a compiled time zone data provider | ||||||
that compiles the time zone data into the binary. | ||||||
|
||||||
Currently, we do not have a way to select certain provider's via feature | ||||||
flag, but a provider can be selected by setting `no-default-features` | ||||||
and importing the preferred provider from `timezone_provider` for the | ||||||
API's that require time zone data. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
For instance, to use the `FsTzdbProvider`, your `Cargo.toml` would look | ||||||
like the below. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
```toml | ||||||
timezone_provider = { version = "0.0.17", features = ["tzif"] } | ||||||
temporal_rs = { version = "0.1.0", no-default-features = true, features = ["sys"]} | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
``` | ||||||
|
||||||
We include `sys` as a `temporal_rs` feature to have access to the system | ||||||
for the default `Now` implementation. We provide the `tzif` feature to | ||||||
`timezone_provider` for the feature gate on `FsTzdbProvider`. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
Please note: `timezone_provider` is still considered unstable for the | ||||||
near future. | ||||||
|
||||||
### Some examples | ||||||
|
||||||
The below examples will be using `temporal_rs` with the default | ||||||
features. | ||||||
|
||||||
#### Retrieve today's date | ||||||
|
||||||
```rust | ||||||
use temporal_rs::Temporal; | ||||||
|
||||||
// Get today's date | ||||||
let today = Temporal::now().to_plain_date_iso(None).unwrap() | ||||||
``` | ||||||
|
||||||
#### Date operation's available | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
Temporal provides a nice API for working with date and date/time via | ||||||
`PlainDate` and `PlainDateTime`. | ||||||
|
||||||
```rust | ||||||
use std::convert::TryFrom; | ||||||
use temporal_rs::{Calendar, Temporal, options::DifferenceSettings, partial::PartialDuration}; | ||||||
|
||||||
// Get today's date | ||||||
let today = Temporal::now().plain_date_iso(None).unwrap(); | ||||||
|
||||||
// We can add a Duration. | ||||||
let partial = PartialDuration::empty().with_days(1); | ||||||
let tomorrow = today.add(&partial.try_into().unwrap(), None).unwrap(); | ||||||
|
||||||
// We can get the difference two dates | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
let diff = today | ||||||
.since(&tomorrow, DifferenceSettings::default()) | ||||||
.unwrap(); | ||||||
|
||||||
// We can change the calendar | ||||||
let tomorrow_japanese = tomorrow.with_calendar(Calendar::JAPANESE); | ||||||
|
||||||
// We can retrieve the calendar's RFC9557 string | ||||||
println!("{tomorrow_japanese}"); | ||||||
|
||||||
``` | ||||||
|
||||||
#### Working with dates and time zones | ||||||
|
||||||
You can also easily work with dates and time zones with the | ||||||
`ZonedDateTime` type. | ||||||
|
||||||
```rust | ||||||
use temporal_rs::options::{DifferenceSettings, Disambiguation, OffsetDisambiguation, Unit}; | ||||||
use temporal_rs::{Calendar, Temporal, TimeZone, ZonedDateTime}; | ||||||
|
||||||
// Parse a ZonedDateTime from utf8 bytes. | ||||||
let zdt = ZonedDateTime::from_utf8( | ||||||
b"2025-03-01T11:16:10Z[America/Chicago][u-ca=iso8601]", | ||||||
Disambiguation::Compatible, | ||||||
OffsetDisambiguation::Reject, | ||||||
) | ||||||
.unwrap(); | ||||||
|
||||||
// Change the time zone. | ||||||
let zurich_zone = TimeZone::try_from_str("Europe/Zurich").unwrap(); | ||||||
let _zdt_zurich = zdt.with_timezone(zurich_zone).unwrap(); | ||||||
|
||||||
// Or get the current ZonedDateTime | ||||||
let today = Temporal::now().zoned_date_time_iso(None).unwrap(); | ||||||
|
||||||
// Difference the two `ZonedDateTime`s | ||||||
let mut options = DifferenceSettings::default(); | ||||||
options.largest_unit = Some(Unit::Year); | ||||||
let diff = today.since(&zdt, options).unwrap(); | ||||||
println!("{diff}"); | ||||||
|
||||||
// Change the calendar | ||||||
let today_coptic = today.with_calendar(Calendar::COPTIC); | ||||||
println!("{today_coptic}"); | ||||||
``` | ||||||
|
||||||
While we can extend these examples further, a more fun exercise for the | ||||||
reader would be taking a look at the | ||||||
[Temporal cookbook](https://tc39.es/proposal-temporal/docs/cookbook.html) | ||||||
as it displays the utility of the Temporal API using JavaScript and all | ||||||
of these examples are now usable from Rust as well. | ||||||
|
||||||
## FFI and engine adoption | ||||||
|
||||||
As previously stated, `temporal_rs` is used in Boa, Kiesel, and V8. | ||||||
There's just one thing, the latter of the two are ECMAScript | ||||||
implementations written in Zig and C++, respectively, not Rust. This was | ||||||
made possible through `temporal_rs`'s sister crate `temporal_capi`, a | ||||||
FFI library that provides C and C++ bindings to `temporal_rs`. | ||||||
|
||||||
The bindings are autogenerated via | ||||||
[Diplomat](https://github.com/rust-diplomat/diplomat), which is a | ||||||
project for generating FFI definitions for Rust libraries. In general, | ||||||
it's a really cool project and would definitely recommend checking it | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
out if you're looking to generate FFI bindings for other languages for | ||||||
your Rust library. | ||||||
|
||||||
There is some added benefits to offering C and C++ bindings beyond the | ||||||
classic: oh, let's (re)write it in Rust. | ||||||
|
||||||
First, this allows other languages and engines to benefit from Rust's | ||||||
type system and memory safety guarantees without having to rewrite | ||||||
everything in Rust. It's a more modular and incremental approach that | ||||||
provides some level of flexibility. | ||||||
|
||||||
Secondly, with how large the API is, `temporal_rs` streamlines the | ||||||
ability to adopt the Temporal API for any current and future | ||||||
implementations, alongside any future update that needs to be made can | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
be done primarily in one place and then released downstream. While it's | ||||||
easy to say: "just use our library" to promote adoption. Seriously, just | ||||||
use the library. The Temporal API is massive from an implementation | ||||||
perspective and the glue code plus `temporal_rs` is relatively trivial | ||||||
in comparison to a fresh implementation. | ||||||
|
||||||
Third, with adoption from multiple engines, `temporal_rs` benefits via | ||||||
further test coverage beyond the native unit tests. For instance, of the | ||||||
engines that offer conformance numbers (Boa, Kiesel, and V8), all of | ||||||
them are currently north of 95% conformance with V8 reaching the highest | ||||||
at around 99% conformance. The conformance difference between the | ||||||
engines being due to the current implementation state of other features, | ||||||
i.e. Boa still hasn't completed its `Intl.DateTimeFormat` implementation | ||||||
yet so it fails all ECMA402 `toLocaleString` tests. As a result though, | ||||||
we can be fairly confident in the general correctness of `temporal_rs`, | ||||||
and any potential bugs will ideally be found and addressed fairly | ||||||
quickly. | ||||||
|
||||||
In general, `temporal_rs` is a pretty good test case with reference code | ||||||
for setting up a Rust library over FFI with usage in a C++ and Zig | ||||||
codebase, so that's really cool. | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
||||||
## Conclusion | ||||||
|
||||||
The 0.1 release of `temporal_rs` is out. We expected the general API to | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
remain fairly stable moving forward, with any non-patch bumps being for | ||||||
added features. Feel free to try it out, and provide feedback / file any | ||||||
issues you come across. | ||||||
|
||||||
`temporal_rs` started as an interesting experiment in creating an engine | ||||||
agnostic library of the Temporal API that could also be usable as a | ||||||
date/time library in native Rust code. We've seen pretty successful | ||||||
results from other engines adopting the library for use. And with any | ||||||
nekevss marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
luck, it will also be useful for the Rust ecosystem as a whole. | ||||||
|
||||||
## Special thanks | ||||||
|
||||||
We'd like to thank all the contributors to `temporal_rs` for helping it | ||||||
|
We'd like to thank all the contributors to `temporal_rs` for helping it | |
We'd like to thank all the [contributors](https://github.com/boa-dev/temporal/graphs/contributors) to `temporal_rs` for helping it |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get to 0.1. | |
get to v0.1. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get that this is a release post, but i think we need a catchier title as many people reading this won't know what temporal_rs is..
"Powering Temporal in both V8 and Boa, Temporal_rs"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Temporal is finally shipping (and it's in Rust)" could be a good title