Working with Time

Timestamp is the core time primitive across adam_core. It stores integer MJD days + integer nanoseconds with an explicit scale, which keeps joins, windowing, and cache keys deterministic.

Construction Patterns

Simple: operator-facing ISO input.

import numpy as np

from adam_core.time import Timestamp

t_utc: Timestamp = Timestamp.from_iso8601(
    ["2026-01-01T00:00:00", "2026-01-02T12:30:00"],
    scale="utc",
)

Numeric: simulation and propagation pipelines.

from adam_core.time import Timestamp

t_tdb: Timestamp = Timestamp.from_mjd(
    np.array([60200.0, 60200.25, 60201.0]), scale="tdb"
)
t_jd: Timestamp = Timestamp.from_jd([2460200.5], scale="tt")

Interoperability: Astropy and ET.

from adam_core.time import Timestamp

t_et: Timestamp = Timestamp.from_et([0.0, 86400.0], scale="tdb")
astropy_time = t_et.to_astropy()
round_trip: Timestamp = Timestamp.from_astropy(astropy_time)

Atomic Operations

Representation and formatting.

iso = t_tdb.to_iso8601()
mjd = t_tdb.mjd()
jd = t_tdb.jd()
et = t_tdb.et()
as_numpy_tdb_mjd = t_tdb.to_numpy()

Arithmetic.

shifted = (
    t_tdb
    .add_days(3)
    .add_seconds(45)
    .add_millis(10)
    .add_micros(5)
    .add_nanos(25)
)

plus_fractional = t_tdb.add_fractional_days([0.125, 0.125, 0.125])

Differences and equality.

delta_days, delta_nanos = shifted.difference(t_tdb)
exact_equal = shifted.equals(t_tdb, precision="ns")
second_equal = shifted.equals(t_tdb, precision="s")

Min/max/unique for time windows.

earliest = t_tdb.min()
latest = t_tdb.max()
unique_epochs = t_tdb.unique()

Scale Management

Use explicit scales whenever data crosses system boundaries.

tdb = t_utc.rescale("tdb")
tai = tdb.rescale("tai")
tt = tai.rescale("tt")

If you need the astropy implementation for a specific validation path:

tdb_astropy = t_utc.rescale_astropy("tdb")

Linking Time Arrays

Timestamp.link returns a quivr.Linkage object. The linkage preserves left/right table context and gives explicit selection methods for joins at controlled precision.

import quivr as qv

rounded_ms: Timestamp = t_utc.rounded("ms")
time_link: qv.Linkage[Timestamp, Timestamp] = t_utc.link(rounded_ms, precision="ms")

# Use linkage methods for explicit join behavior.
print(type(time_link.left_table), type(time_link.right_table))
print(len(time_link))

Cache and Grouping Keys

High-throughput pipelines can use key, signature, and cache_digest for stable cache keys and quick identity checks.

keys = t_tdb.key(scale="tdb")
signature = t_tdb.signature(scale="tdb")
digest = t_tdb.cache_digest(scale="tdb")

When to Use Which Constructor

  • from_iso8601: API inputs, operator tooling, external payloads.

  • from_mjd/from_jd: numerical pipelines and propagation windows.

  • from_et: SPICE-style integration points.

  • from_astropy: interoperability with existing astronomy tooling.