Time and Time Scales¶
satkit::Instant is a point in time, stored as integer TAI microseconds since 1970-01-01 00:00:00 TAI, and satkit::Duration is an exact microsecond interval. Arithmetic on them is integer arithmetic that counts leap seconds, and saturates at the ends of the ±292,000-year range (checked_add / checked_sub report overflow instead). The Time Systems tutorial explains the scales and how satkit converts between them; this page shows the Rust API.
Time scales¶
TimeScale has UTC, TAI, TT, GPS, UT1 and TDB. Calendar constructors take UTC unless they say otherwise (from_datetime_with_scale), and the scale of a Julian date is always explicit: as_mjd_with_scale, as_jd_with_scale, from_mjd_with_scale, from_jd_with_scale, and the _utc shortcuts (as_mjd_utc, from_jd_utc, ...). UT1 uses the Earth orientation table (UT1 = UTC with a warning when it is not available).
Every function in the crate that takes a time is generic over the TimeLike trait, implemented by Instant and, with the chrono feature, by chrono::DateTime.
Parsing and formatting¶
Instant::from_rfc3339for RFC 3339 / ISO 8601 (2024-06-15T12:00:00.25Z, with or without an offset).Instant::from_stringfor free-form dates ("June 15 2024 13:30","2024-06-15 13:30:00"); it tries RFC 3339 first. Locale-ordered numeric dates such as06/15/2024are ambiguous and not supported; usestrptimefor those.Instant::strptime(s, fmt)with an explicit format (%Y %m %d %H %M %S %f %z %b %B).Displayandas_rfc3339()print2024-06-15T12:00:00.000000Z;strftimeformats with the same codes plus weekday names.
Parse failures are InstantError.
Example¶
//! Time scales, arithmetic, parsing and formatting with `satkit::Instant`.
//!
//! Runs offline: the leap-second table is compiled in. UT1 needs the Earth
//! orientation file `finals2000A.all` (downloaded on first use); without it
//! satkit warns once and uses UT1 = UTC.
//!
//! ```text
//! cargo run --example time_scales
//! ```
use satkit::{Duration, Instant, TimeScale};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Calendar input is UTC; the seconds are rounded to the microsecond
let t = Instant::from_datetime(2024, 6, 15, 12, 0, 0.0)?;
println!("t = {t}");
// The same instant expressed in each scale, as seconds ahead of UTC
let mjd_utc = t.as_mjd_with_scale(TimeScale::UTC);
for scale in [
TimeScale::TAI,
TimeScale::TT,
TimeScale::GPS,
TimeScale::UT1,
TimeScale::TDB,
] {
let ahead = (t.as_mjd_with_scale(scale) - mjd_utc) * 86_400.0;
println!(" {scale:?} - UTC = {ahead:+.4} s");
}
// Calendar components interpreted in another scale
let t_tt = Instant::from_datetime_with_scale(2024, 6, 15, 12, 0, 0.0, TimeScale::TT)?;
let dt: Duration = t - t_tt; // exact, in integer microseconds
println!("12:00 UTC - 12:00 TT = {} us", dt.as_microseconds());
// Arithmetic is exact and counts leap seconds
let t0 = Instant::from_datetime(2016, 12, 31, 23, 59, 59.0)?;
let t1 = t0 + Duration::from_seconds(1.0);
let t2 = t0 + Duration::from_seconds(2.0);
println!("leap second: {t0} -> {t1} -> {t2}");
let jan1 = Instant::from_date(2017, 1, 1)?;
let dec31 = Instant::from_date(2016, 12, 31)?;
println!("2016-12-31 lasted {} s", (jan1 - dec31).as_seconds());
// Parsing: RFC 3339, free-form strings, and explicit formats
let a = Instant::from_rfc3339("2024-06-15T12:00:00.25Z")?;
let b = Instant::from_string("June 15 2024 13:30")?;
let c = Instant::strptime("15/06/2024 14:45:10", "%d/%m/%Y %H:%M:%S")?;
println!("parsed: {a}, {b}, {c}");
// Formatting
println!("strftime: {}", c.strftime("%A %d %B %Y, %H:%M")?);
println!("day of year {}, {}", c.day_of_year(), c.day_of_week());
// Other representations
println!("JD (UTC) = {:.6}", t.as_jd_with_scale(TimeScale::UTC));
println!("MJD (TT) = {:.6}", t.as_mjd_with_scale(TimeScale::TT));
println!("Unix time = {}", t.as_unixtime());
let g = Instant::from_gps_week_and_second(2318, 561_618.0);
println!("GPS week 2318, 561618 s = {g}");
Ok(())
}
$ cargo run --example time_scales
t = 2024-06-15T12:00:00.000000Z
TAI - UTC = +37.0000 s
TT - UTC = +69.1840 s
GPS - UTC = +18.0000 s
UT1 - UTC = -0.0164 s
TDB - UTC = +69.1845 s
12:00 UTC - 12:00 TT = 69184000 us
leap second: 2016-12-31T23:59:59.000000Z -> 2016-12-31T23:59:60.000000Z -> 2017-01-01T00:00:00.000000Z
2016-12-31 lasted 86401 s
parsed: 2024-06-15T12:00:00.250000Z, 2024-06-15T13:30:00.000000Z, 2024-06-15T14:45:10.000000Z
strftime: Saturday 15 June 2024, 14:45
...
GPS week 2318, 561618 s = 2024-06-15T12:00:00.000000Z
The UT1 line depends on the Earth orientation table; everything else is fixed by the compiled-in leap-second table.
chrono interoperability¶
With the chrono feature, Instant and chrono::DateTime convert both ways with From / Into, exactly to the microsecond, and a chrono::DateTime can be passed directly to any function that takes a time. chrono has no leap seconds: an instant inside one (23:59:60.5) converts to 23:59:59.5, as Unix time does.
//! Converting between `satkit::Instant` and `chrono::DateTime`.
//!
//! Needs the `chrono` feature (off by default):
//!
//! ```text
//! cargo run --example chrono_interop --features chrono
//! ```
use chrono::{DateTime, TimeZone, Utc};
use satkit::{Duration, Instant, TimeScale};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// chrono -> satkit, exact to the microsecond (any time zone)
let dt = Utc.with_ymd_and_hms(2024, 6, 15, 12, 0, 0).unwrap();
let t = Instant::from(dt);
println!("from chrono: {t}");
// satkit -> chrono
let back: DateTime<Utc> = (t + Duration::from_hours(1.5)).into();
println!("to chrono: {back}");
// Every satkit function that takes a time is generic over `TimeLike`,
// which `chrono::DateTime<Utc>` implements, so it can be passed as is
let tt = satkit::TimeLike::as_mjd_with_scale(&dt, TimeScale::TT);
let pos = satkit::lpephem::sun::pos_gcrf(&dt);
println!("MJD (TT) = {tt:.6}, Sun distance = {:.4e} m", pos.norm());
// chrono has no leap seconds: 23:59:60 maps to 23:59:59, as Unix time does
let leap = Instant::from_datetime(2016, 12, 31, 23, 59, 60.5)?;
let leap_chrono: DateTime<Utc> = leap.into();
println!("{leap} -> {leap_chrono}");
Ok(())
}
$ cargo run --example chrono_interop --features chrono
from chrono: 2024-06-15T12:00:00.000000Z
to chrono: 2024-06-15 13:30:00 UTC
MJD (TT) = 60476.500801, Sun distance = 1.5196e11 m
2016-12-31T23:59:60.500000Z -> 2016-12-31 23:59:59.500 UTC
See also¶
- Time Systems: the scales, leap seconds, UT1 and TDB.
Instant,DurationandTimeScaleon docs.rs.