deep-time 0.1.0-beta.12

High-precision, no-std, no-alloc date-time library, leap-seconds, time scales, relativistic time, and a powerful date & duration parser
Documentation

deep-time

A fully featured and high performance Rust date and time library with attosecond precision that aims to blend astronomical and civil timekeeping.

docs.rs Crates.io License

Overview

A non-exhaustive list of functionality:

Examples

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::De,
        ..Default::default()
    };
    let dt = Dt::from_str_parse("15 mars 2024 à 14:30 [Europe/Paris]", &cfg)?;
    let s = dt.to_str_rfc9557("Europe/Paris")?;
    assert_eq!("2024-03-15T14:30:00+01: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).unwrap();
    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).unwrap();
    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).unwrap();

    // 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(())
}

Installation

Add this to your Cargo.toml in the dependencies section:

[dependencies]
deep-time = { version = "0.1", features = ["parse", "jiff-tz"] }

Important: This crate has no default features.

Most users will want to enable at least parse (for the auto-parsers) and jiff-tz (for timezone support and DST-aware calendar math).

Feature Flags

Feature Description Requires
parse Enables the powerful multi-language 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
js WebAssembly support (includes serde and JS bindings) std
tsify TypeScript definitions via tsify (for WASM) js
std Enables std functionality (including Dt::now())
alloc Enables allocation (required for parsing and some conversions)
es / de / fr Individual language support for parsing and formatting parse (for parsing)
euro Enables all European languages
lang Enables all languages euro
panic-handler Provides a 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:

[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:

cargo test --release --features "parse hifitime std jiff-tz perf-tests" -- --nocapture perf_tests