Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Working with Other Crates

A Timestamp and a Timedelta convert to and from std’s time types and those of chrono, jiff and time. Where every t2t value fits the other type, the conversion is a From; the way back is a TryFrom, refusing with OutOfRangeError what t2t’s range does not hold. The other points, a TaiTimestamp and those counted from boot or by a counter, have no counterpart in these crates, and no conversion.

Other typeFeatureInto itFrom it
core::time::Durationnonea Timedelta forwardsa Duration up to Timedelta::MAX
std::time::SystemTimestdevery Timestampa moment from 1677-09-21 to 2262-04-11
chrono 0.4’s DateTime<Utc>chrono-04every Timestampa DateTime in any zone, within the range
chrono 0.4’s TimeDeltachrono-04every Timedeltaa TimeDelta within the range
jiff 0.2’s Timestampjiff-02every Timestampa Timestamp within the range
jiff 0.2’s SignedDurationjiff-02every Timedeltaa SignedDuration within the range
time 0.3’s OffsetDateTimetime-03every Timestamp, in UTCan OffsetDateTime at any offset, within the range
time 0.3’s Durationtime-03every Timedeltaa Duration within the range

A zone or an offset converts as the instant it names, read in UTC. Each feature is named for the version of the crate it converts with, and with std on, it turns on that crate’s own std too. The listings are t2t-core’s tests, which have each crate as a dependency, so they name the crate t2t_core.

std

A Timedelta crosses to std’s Duration and back by TryFrom both ways: a Duration never runs backwards, so a negative span is refused, and a Timedelta holds about 292 years, so a longer Duration is refused. The conversion is core’s, and needs no feature.

fn a_span_crosses_to_a_duration_where_it_runs_forwards() {
    use core::time::Duration;

    use t2t_core::{OutOfRangeError, Timedelta};

    let span = Timedelta::from_millis(5);
    assert_eq!(Duration::try_from(span), Ok(Duration::from_millis(5)), "a span forwards");
    assert_eq!(Timedelta::try_from(Duration::from_millis(5)), Ok(span), "and back");
    assert_eq!(Duration::try_from(-span), Err(OutOfRangeError), "no duration runs backwards");
    assert_eq!(Timedelta::try_from(Duration::MAX), Err(OutOfRangeError), "nor past 292 years");
}

A Timestamp becomes a SystemTime with From, since a SystemTime on 64-bit Linux and macOS reaches past both ends of a timestamp’s range, and comes back with TryFrom, refusing a moment outside it. These need std, on 64-bit Linux or macOS.

fn a_timestamp_crosses_to_a_system_time_and_back_within_its_range() {
    use core::time::Duration;
    use std::time::{SystemTime, UNIX_EPOCH};

    use t2t_core::{OutOfRangeError, Timestamp};

    let instant = Timestamp::from_secs(1_700_000_000);
    let system_time = SystemTime::from(instant);
    let since_epoch = system_time.duration_since(UNIX_EPOCH).ok();
    assert_eq!(since_epoch, Some(Duration::from_secs(1_700_000_000)), "the same moment");
    assert_eq!(Timestamp::try_from(system_time), Ok(instant), "and back");

    let far_future = UNIX_EPOCH
        .checked_add(Duration::from_secs(1 << 40))
        .expect("a 64-bit SystemTime reaches 2^40 seconds past 1970");
    assert_eq!(Timestamp::try_from(far_future), Err(OutOfRangeError), "past 2262 is refused");
}

std’s Instant has no conversion: it is opaque, and std gives no way to read its count or build one from a count. On Linux and macOS it reads CLOCK_MONOTONIC and CLOCK_UPTIME_RAW, the clocks MonotonicClock reads, so code that would convert an Instant reads MonotonicClock in its place.

chrono

chrono’s years reach far past 1677 and 2262, so a DateTime<Utc> holds every timestamp, and a TimeDelta every span; a DateTime in another zone comes back as the instant it names.

fn a_timestamp_and_a_span_cross_to_chrono() {
    use chrono::{DateTime, FixedOffset, TimeDelta, Utc};
    use t2t_core::{OutOfRangeError, Timedelta, Timestamp};

    let instant = Timestamp::from_nanos(1_789_544_735_123_456_789);
    let theirs = DateTime::<Utc>::from(instant);
    assert_eq!(theirs.timestamp_nanos_opt(), Some(instant.as_nanos()), "the same instant");

    let paris = theirs.with_timezone(&FixedOffset::east_opt(7_200).expect("two hours east"));
    assert_eq!(Timestamp::try_from(paris), Ok(instant), "in any zone, read as its instant");
    let latest = DateTime::<Utc>::MAX_UTC;
    assert_eq!(Timestamp::try_from(latest), Err(OutOfRangeError), "past 2262 is refused");

    let span = Timedelta::from_millis(-250);
    assert_eq!(TimeDelta::from(span), TimeDelta::milliseconds(-250), "every span crosses");
    assert_eq!(Timedelta::try_from(TimeDelta::MAX), Err(OutOfRangeError), "but not every back");
}

jiff

jiff’s Timestamp reaches the years -9999 to 9999, so it holds every timestamp, and its SignedDuration every span.

fn a_timestamp_and_a_span_cross_to_jiff() {
    use jiff::{SignedDuration, Timestamp as JiffTimestamp};
    use t2t_core::{OutOfRangeError, Timedelta, Timestamp};

    let instant = Timestamp::from_nanos(1_789_544_735_123_456_789);
    let theirs = JiffTimestamp::from(instant);
    assert_eq!(theirs.to_string(), "2026-09-16T07:45:35.123456789Z", "the same instant");
    assert_eq!(Timestamp::try_from(theirs), Ok(instant), "and back");
    let latest = JiffTimestamp::MAX;
    assert_eq!(Timestamp::try_from(latest), Err(OutOfRangeError), "past 2262 is refused");

    let span = Timedelta::from_millis(-250);
    assert_eq!(SignedDuration::from(span), SignedDuration::from_millis(-250), "a span");
    let longest = SignedDuration::MAX;
    assert_eq!(Timedelta::try_from(longest), Err(OutOfRangeError), "and one too long for it");
}

time

time’s OffsetDateTime takes every timestamp, at the UTC offset; one at another offset comes back as the instant it names. Its Duration holds every span.

fn a_timestamp_and_a_span_cross_to_time() {
    use t2t_core::{OutOfRangeError, Timedelta, Timestamp};
    use time::{Duration, OffsetDateTime, UtcOffset};

    let instant = Timestamp::from_nanos(1_789_544_735_123_456_789);
    let theirs = OffsetDateTime::from(instant);
    assert_eq!(theirs.offset(), UtcOffset::UTC, "in UTC");
    let east = theirs.to_offset(UtcOffset::from_hms(2, 0, 0).expect("two hours east"));
    assert_eq!(Timestamp::try_from(east), Ok(instant), "at any offset, read as its instant");

    let span = Timedelta::from_millis(-250);
    assert_eq!(Duration::from(span), Duration::milliseconds(-250), "a span");
    assert_eq!(Timedelta::try_from(Duration::MAX), Err(OutOfRangeError), "and one too long");
}