# deep-time
A fully featured and high performance **Rust date and time library** with attosecond precision that aims to blend **astronomical** and **civil** timekeeping.
[](https://docs.rs/deep-time)
[](https://crates.io/crates/deep-time)
[](https://github.com/ragardner/deep-time/blob/main/LICENSE)
### Overview
A non-exhaustive list of functionality:
- Auto-parsers for [datetimes](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.from_str_parse) and [durations](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.from_str_duration) that handle thousands of formats, relative dates and multiple languages, requires the `parse` feature
- No std, no alloc, and wide-spread [const fn](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.from_ymd).
- [Extensively validated](https://github.com/ragardner/deep-time/tree/main/tests) against outputs from **Astropy**, **Jiff**, and other libraries and sources
- Fast [ISO](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.from_str_iso) parser
- [Time scales](https://docs.rs/deep-time/latest/deep_time/enum.Scale.html) e.g. UTC with full leap second support, including historical, TT, TAI, TDB, LTC, GPS, etc.
- [Strptime](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.from_str)
- [Strftime](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_str) (multi-language day and month names available)
- First class [timezone](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_str_in_tz) support provided by the Rust library [jiff](https://github.com/BurntSushi/jiff) enabled with the `jiff-tz` feature.
- To and from all kinds of inputs and outputs, functions mostly prefixed with `to` and `from`, available on the library's types, see the main time types functions: [Dt](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html). Including [JD](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_jd_f), [MJD](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_mjd_f), [Unix](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_unix), [NTP](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_ntp), etc.
- [Calendar aware](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.add_days) and, with the `jiff-tz` feature, [timezone aware](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.add_days_tz) math
- To and from [jiff](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_jiff_timestamp), [chrono](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_chrono_datetime_utc), and [hifitime](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_hifitime_epoch) types
- No-alloc [error type](https://docs.rs/deep-time/latest/deep_time/struct.AnErr.html) with rich tracing
- No-alloc [string return type](https://docs.rs/deep-time/latest/deep_time/struct.LiteStr.html)
- Const fn [libm math](https://docs.rs/deep-time/latest/deep_time/math/index.html) functions
- Safe, saturating arithmetic throughout
- [Lunar](https://docs.rs/deep-time/latest/deep_time/lunar/index.html) and [Mars](https://docs.rs/deep-time/latest/deep_time/mars/index.html) modules
- [Sidereal time](https://docs.rs/deep-time/latest/deep_time/sidereal/struct.Sidereal.html) with a const fn implementation of ERFA Equation of the Origins / Equinoxes
- [UT1 and EOP](https://docs.rs/deep-time/latest/deep_time/eop/index.html)
- [Light-time (Shapiro delay, etc.)](https://docs.rs/deep-time/latest/deep_time/struct.Observer.html)
- [Proper time along trajectories](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.proper_time_from_states)
- Relativity: [Drift](https://docs.rs/deep-time/latest/deep_time/struct.Drift.html), [Spacetime](https://docs.rs/deep-time/latest/deep_time/struct.Spacetime.html), [Position](https://docs.rs/deep-time/latest/deep_time/struct.Position.html), and [Velocity](https://docs.rs/deep-time/latest/deep_time/struct.Velocity.html) — see [docs/relativity.md](docs/relativity.md) for the underlying model.
- CCSDS [CUC](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_ccsds_cuc), [CDS](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_ccsds_cds), and [CCS](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html#method.to_ccsds_ccs)
### Examples
```rust
use deep_time::{Dt, DtErr, Lang, LiteStr, ParseCfg, Scale, YmdHms};
fn main() -> Result<(), DtErr> {
// ============================================
// Parsing
// ============================================
// Smart auto-parsing (multi-language + timezone)
let cfg = ParseCfg {
lang: Lang::Fr,
..Default::default()
};
let dt = Dt::from_str_parse("15 août 2024 à 14:30 [Europe/Paris]", &cfg)?;
let s = dt.to_str_rfc9557("Europe/Paris")?;
assert_eq!("2024-08-15T14:30:00+02:00[Europe/Paris]", s);
// or with .parse
let dt: Dt = "1 jan 2000 07:00 [America/New_York] TAI".parse()?; // noon
assert_eq!(Dt::ZERO, dt);
// Relative dates are also supported
let ref_time = Dt::from_ymd(2026, 6, 16, Scale::UTC, 12, 0, 0, 0);
let en_cfg = ParseCfg {
ref_time: Some(ref_time),
..Default::default()
};
let dt = Dt::from_str_parse("2 days from now at 9am", &en_cfg)?;
assert_eq!(dt, Dt::from_ymd(2026, 6, 18, Scale::UTC, 9, 0, 0, 0));
let dt = Dt::from_str_parse("next Monday at 14:00", &en_cfg)?;
assert_eq!(dt, Dt::from_ymd(2026, 6, 22, Scale::UTC, 14, 0, 0, 0));
// Relative dates use Dt::now if the `std` feature is enabled and no
// ref_time is provided in the ParseCfg
let _ = Dt::from_str_parse("next Monday at 14:00", &ParseCfg::DEFAULT)?;
// Fast ISO parsing with time scale and no alloc output
let dt = Dt::from_str_iso("2000-01-01T12:00:00 TAI")?;
let lite_str: LiteStr<512> = dt.to_str_lite_iso8601()?;
assert_eq!("2000-01-01T12:00:00+00:00", lite_str.as_str());
// ============================================
// Formatting
// ============================================
let s = dt.to_str_in_tz("%A, %d %B %Y %I:%M%P", "America/New_York", Lang::En)?;
assert_eq!("Saturday, 01 January 2000 07:00am", s);
let s = dt.to_str_in_tz("%A, %-d de %B de %Y %H:%M", "America/New_York", Lang::Es)?;
assert_eq!("Sábado, 1 de enero de 2000 07:00", s);
// ============================================
// Duration parsing
// ============================================
let span: Dt = Dt::from_str_duration("3 days 12 hours", Lang::En)?;
let dur = span.to_str_lite_media_duration();
assert_eq!("3:12:00:00", dur.to_string());
// ============================================
// Time scale conversions + round-tripping
// ============================================
let dt = Dt::from_ymd(2000, 1, 1, Scale::TAI, 0, 0, 0, 123456789);
let tt = dt.to(Scale::TT);
let tdb = tt.to(Scale::TDB);
let ltc = tdb.to(Scale::LTC);
let utc = ltc.to(Scale::UTC);
let tcl = utc.to(Scale::TCL);
let tcg = tcl.to(Scale::TCG);
let tai = tcg.to_tai();
// round trips work for pretty much everything except UTCHist
assert_eq!(dt, tai);
let ymd: YmdHms = tai.to_ymd();
assert_eq!(ymd.attos(), 123456789);
// ============================================
// Other conversions
// ============================================
// unix
let dt = Dt::from_ymd(1970, 1, 1, Scale::UTC, 0, 0, 0, 0);
let unix = dt.to_unix().to_sec_f();
assert_eq!(unix, 0.0);
// or to milliseconds
let unix: i128 = dt.add_ms(1000).to_unix().to_ms();
assert_eq!(unix, 1000);
// to and from jd
let jd = Dt::ZERO.to_jd_f();
assert_eq!(2451545.0, jd);
let dt = Dt::from_jd_f(jd, Scale::TAI);
assert_eq!(0, dt.attos);
// ============================================
// Calendar math
// ============================================
// calendar math and negative year
let dt = Dt::from_ymd(-2000, 1, 31, Scale::TAI, 12, 0, 0, 0);
let ymd = dt.add_mo(1).to_ymd();
assert_eq!(ymd.day(), 29);
// Timezone-aware calendar math (respects DST transitions, requires jiff-tz feature)
let dt = Dt::from_str_iso("2025-03-30T00:30:00Z")?; // Just before London DST start
// Normal (naive) addition — ignores DST rules
let normal = dt.add_hr(1);
// Timezone-aware addition — correctly handles the transition
let aware = dt.add_hr_tz(1, "Europe/London")?;
println!("Normal: {}", normal.to_str_rfc9557("Europe/London")?);
println!("Aware: {}", aware.to_str_rfc9557("Europe/London")?);
// ============================================
// Leap seconds
// ============================================
// genuine leap second input round trips
let dt: Dt = "2015-06-30T23:59:60".parse()?;
let s = dt.to_str_iso8601();
assert_eq!("2015-06-30T23:59:60+00:00", s);
Ok(())
}
```
### Documentation
- [Library's main documentation page](https://docs.rs/deep-time/latest)
- [Changelog](https://github.com/ragardner/deep-time/blob/main/docs/CHANGELOG.md)
- [The main time type](https://docs.rs/deep-time/latest/deep_time/struct.Dt.html)
- [Time scales](https://docs.rs/deep-time/latest/deep_time/enum.Scale.html)
### Installation
- This crate has no default features.
- Enable `parse` for the auto-parsers and `jiff-tz` for timezone support and DST-aware calendar math.
For example, add this to your `Cargo.toml` in the `dependencies` section:
```toml
[dependencies]
deep-time = { version = "0.1", features = ["parse", "jiff-tz"] }
```
### Feature Flags
| Feature | Description | Requires |
|----------------------|-----------------------------------------------------------------------------|--------------|
| `parse` | Enables the auto-parsers (`from_str_parse`, `from_str_duration`, etc.) | `alloc` |
| `jiff-tz` | Enables timezone-aware calendar math (`add_days_tz`, `add_hr_tz`, etc.) and `to_str_in_tz` | `std` |
| `jiff-tz-bundle` | Same as `jiff-tz` but bundles the full timezone database | `std` |
| `jiff` | Enables basic Jiff interop | `alloc` |
| `chrono` | Enables Chrono interop | `alloc` |
| `hifitime` | Enables Hifitime interop | — |
| `serde` | Enables `Serialize` / `Deserialize` for `Dt` and other types | `alloc` |
| `js` | WebAssembly support (includes `serde` and JS bindings) | `std` |
| `tsify` | TypeScript definitions via `tsify` (for WASM) | `js` |
| `std` | Enables `std` functionality including `Dt::now()` and file handling | — |
| `alloc` | Enables allocation (required for parsing and some conversions) | — |
| `es` / `de` / `fr` | Language support, parsing different languages requires alloc, formatting does not | — |
| `euro` | Enables all European languages | |
| `lang` | Enables all languages | `euro` |
| `panic-handler` | Provides an optional simple `#[panic_handler]` for `no_std` environments | `no_std` |
| `wire` | Enables wire format (serialization) support | — |
| `mars` | Enables Mars time support (`to_msd`, `to_mars_ls`, etc.) | — |
| `sidereal` | Enables sidereal time support | — |
| `eop` | Enables Earth Orientation Parameters (UT1, etc.) | `alloc` |
| `locale` | Enables system locale detection | `std` |
#### Optional No-Alloc Panic Handler
`deep-time` supports `no_std` + `no_alloc` environments. When targeting bare-metal or embedded systems, you can enable a minimal panic handler:
```toml
[dependencies]
deep-time = { version = "0.1", features = ["panic-handler"] }
```
This provides a simple `#[panic_handler]` that uses `core::hint::spin_loop()` (more power-efficient than a plain `loop {}`).
You only need this if you are building a binary crate in a `no_std` environment without your own panic handler.
#### Notes
- The fast ISO 8601 parser (`from_str_iso`) works **without** the `parse` feature.
- Multi-language **parsing** requires the `parse` feature, but multi-language **formatting** works without it.
- The `.parse()` implementation on `Dt` automatically chooses between the full parser and the ISO parser depending on enabled features.
### Performance
Benchmarks were measured on an AMD Ryzen 7 7800X3D.
#### Parsing and Formatting
| Operation | Time | vs Jiff 0.2.28 |
|----------------------------------------|---------------|--------------------------|
| ISO datetime parsing | 17.7 ns | 33.4% faster |
| `strptime` | 34.5 ns | 16.0% faster |
| TZ `strptime` -> `Dt` vs `jiff:Zoned` | 193 ns | 18.4% slower |
| `strftime` | 93.5 ns | 50.9% slower |
| Auto parser (`from_str_parse`) | 650 ns | — |
#### Time Scale Conversions
| Conversion | deep-time | hifitime 4.3 | Relative Performance |
|------------------|---------------|---------------|---------------------------|
| TAI → UTC | 9.7 ns | 45.2 ns | 4.6× faster |
| UTC → TAI | 13.0 ns | 47.2 ns | 3.6× faster |
| TAI → TDB | 136 ns | 90.3 ns | 1.5× slower |
| TDB → TAI | 599 ns | 27.0 ns | 22.2× slower |
| GPS conversion | 21.6 ns | 5.3 ns | 4.1× slower |
| GPS week + TOW | 28.0 ns | 7.0 ns | 4.0× slower |
The tests were run with:
```sh
cargo test --release --features "parse hifitime std jiff-tz perf-tests" -- --nocapture perf_tests
```