Python bindings for tempoch, providing astronomical time primitives backed by Rust through PyO3.
The Crates.io and docs.rs badges above refer to the underlying tempoch Rust crate used by these bindings.
- Julian Date and Modified Julian Date objects with full arithmetic
- UTC conversion (ISO 8601 strings and Python
datetimeobjects) - Time-scale conversion between 11 astronomical scales (JD, MJD, TT, TDB, TAI, TCG, TCB, GPS, UnixTime, UT, JDE)
- Time periods (intervals) with duration, intersection, and containment
- Python exceptions for errors (no raw FFI/status codes)
- Pickle and hash support
- All computation in Rust (no reimplementation in Python)
- Reusable PyO3 interop for timezone-aware Python datetimes and
Time<UTC>
The bindings are currently built from source. Clone the repository with its Rust submodule, create a Python environment, and install with Maturin:
git clone --recurse-submodules https://github.com/Siderust/tempoch-py.git
cd tempoch-py
python -m venv .venv
source .venv/bin/activate # Windows: .venv\\Scripts\\activate
python -m pip install --upgrade pip maturin
maturin develop --releaseTo build a wheel instead:
maturin build --release
python -m pip install target/wheels/tempoch-*.whlfrom tempoch import JulianDate, ModifiedJulianDate, TimePeriod, TimeScale, convert_timescale
# Julian Date basics
j2000 = JulianDate.j2000() # J2000.0 epoch
print(j2000) # JulianDate(2451545.0)
print(j2000.to_utc()) # 2000-01-01T11:58:55...+00:00
# Modified Julian Date
mjd = j2000.to_mjd() # ModifiedJulianDate(51544.5)
jd_back = mjd.to_jd() # JulianDate(2451545.0)
# UTC conversion
jd = JulianDate.from_utc("2024-06-21T12:00:00Z")
dt = jd.to_datetime() # Python datetime object (UTC)
# Arithmetic
tomorrow = j2000 + 1.0 # Add 1 day
diff = tomorrow - j2000 # 1.0 (days)
# Julian centuries since J2000
centuries = jd.julian_centuries()
# Time-scale conversion
tdb = convert_timescale(2451545.0, TimeScale.JD, TimeScale.TDB)
tai = convert_timescale(2451545.0, TimeScale.JD, TimeScale.TAI)
# Time periods
period = TimePeriod(59000.0, 59010.0) # MJD-based
print(period.duration_days()) # 10.0
print(period.contains(59005.0)) # True
# Period intersection
p1 = TimePeriod(59000.0, 59010.0)
p2 = TimePeriod(59005.0, 59015.0)
overlap = p1.intersection(p2) # TimePeriod(59005.0, 59010.0)The package also builds an rlib named tempoch_py, so another PyO3 crate can
reuse the canonical datetime boundary while depending on tempoch directly:
[dependencies]
pyo3 = { version = "0.29", features = ["extension-module"] }
tempoch = "0.7"
tempoch-py = { git = "https://github.com/Siderust/tempoch-py.git" }use pyo3::prelude::*;
use tempoch::Time;
fn roundtrip<'py>(value: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
let instant: Time<tempoch::UTC> = tempoch_py::interop::datetime_to_time(value)?;
tempoch_py::interop::time_to_datetime(value.py(), instant)
}datetime_to_time intentionally rejects naive datetimes. Aware datetimes with
any valid UTC offset are normalized to UTC without converting through a
floating-point POSIX timestamp. Context-aware variants are available for users
that need to supply a specific tempoch::TimeContext, and
period_to_datetimes converts both endpoints of a Period<UTC>.
The Python TimeScale enum retains its historical names. In the 0.7 model,
JD, MJD, UnixTime, and GPS describe formats, while TT, TAI, TDB,
TCG, TCB, and UT (UT1) describe physical scales. JDE remains a
compatibility alias for a TT Julian Date.
| Class | Description |
|---|---|
JulianDate |
Julian Date (continuous day count) |
ModifiedJulianDate |
Modified Julian Date (JD − 2,400,000.5) |
TimePeriod |
Time interval defined by start/end MJD |
TimeScale |
Enum of astronomical time scales |
| Method | Description |
|---|---|
JulianDate(value) |
Create from day number |
JulianDate.j2000() |
J2000.0 epoch constant |
JulianDate.from_utc(str) |
Create from UTC string |
JulianDate.from_datetime(dt) |
Create from Python datetime |
.value |
Raw day number (float) |
.to_mjd() |
Convert to ModifiedJulianDate |
.to_utc() |
Convert to UTC string (ISO 8601) |
.to_datetime() |
Convert to Python datetime (UTC) |
.add_days(n) |
Add n days |
.difference(other) |
Days between two JDs |
.julian_centuries() |
Centuries since J2000.0 |
.julian_years() |
Years since J2000.0 |
.julian_millennia() |
Millennia since J2000.0 |
| Method | Description |
|---|---|
ModifiedJulianDate(value) |
Create from MJD day number |
ModifiedJulianDate.from_utc(str) |
Create from UTC string |
ModifiedJulianDate.from_datetime(dt) |
Create from Python datetime |
.value |
Raw MJD day number (float) |
.to_jd() |
Convert to JulianDate |
.to_utc() |
Convert to UTC string |
.to_datetime() |
Convert to Python datetime |
.add_days(n) |
Add n days |
.difference(other) |
Days between two MJDs |
| Method | Description |
|---|---|
TimePeriod(start_mjd, end_mjd) |
Create from MJD values |
TimePeriod.from_mjd(start, end) |
Create from MJD objects |
TimePeriod.from_jd(start, end) |
Create from JD objects |
TimePeriod.from_utc(start, end) |
Create from UTC strings |
.start / .end |
Start/end as ModifiedJulianDate |
.start_mjd / .end_mjd |
Start/end as float |
.duration_days() |
Duration in days |
.duration_hours() |
Duration in hours |
.duration_seconds() |
Duration in seconds |
.to_utc() |
Start/end as UTC strings |
.intersection(other) |
Intersection with another period |
.contains(mjd) |
Check if MJD is within period |
| Function | Description |
|---|---|
convert_timescale(value, from, to) |
Convert between time scales |
tai_minus_utc(jd) |
TAI − UTC leap seconds (seconds) |
intersect_periods(periods, bounds) |
Intersect period list with bounds |
| Exception | Base | Description |
|---|---|---|
NonFiniteTimeError |
ValueError |
NaN or infinite time value |
InvalidIntervalError |
ValueError |
Period start after end |
ConversionError |
ValueError |
UTC/scale conversion out of range |
| Scale | Description |
|---|---|
TimeScale.JD |
Julian Date (identity) |
TimeScale.JDE |
Julian Ephemeris Day |
TimeScale.MJD |
Modified Julian Date |
TimeScale.TDB |
Barycentric Dynamical Time |
TimeScale.TT |
Terrestrial Time |
TimeScale.TAI |
International Atomic Time |
TimeScale.TCG |
Geocentric Coordinate Time |
TimeScale.TCB |
Barycentric Coordinate Time |
TimeScale.GPS |
GPS Time |
TimeScale.UnixTime |
Unix/POSIX Time |
TimeScale.UT |
Universal Time (Earth rotation) |
tempoch-py is a thin Python interface over the tempoch Rust crate. Core astronomical time calculations remain implemented in Rust; the Python layer focuses on idiomatic Python types, exceptions, and interoperability.
- Rust crate: crates.io/crates/tempoch
- Rust API documentation: docs.rs/tempoch
python -m venv .venv
source .venv/bin/activate # Windows: .venv\\Scripts\\activate
python -m pip install --upgrade pip maturin pytest ruff
maturin develop
# Python tests
pytest tests/ -v
# Rust tests
cargo test --all-targets
# Formatting and linting checks used by CI
cargo fmt --check
cargo clippy --all-targets -- -D warnings
ruff format --check python tests examples
ruff check python tests examplesAGPL-3.0 — see LICENSE.