Skip to content

Jpl time 2.0 - #3

Open
FloraRidenhour wants to merge 14 commits into
mainfrom
jpl-time-2.0
Open

Jpl time 2.0#3
FloraRidenhour wants to merge 14 commits into
mainfrom
jpl-time-2.0

Conversation

@FloraRidenhour

Copy link
Copy Markdown
Collaborator

This updates jpl_time to 2.0.0. The main change: SPICE kernels are no longer needed for most operations. UTC parsing/formatting, ET/TAI/GPS conversions, time zones, and all time and duration math now run in pure Python using a built-in leap second table and the DELTET formula from the LSK. Loading a newer leapseconds kernel still takes precedence over the built-in table. SCLK, LMST, LTST, and light-time conversions use kernels exactly as before. LMST can now also work without kernels by setting the sol-0 epoch (Time.set_lmst_epoch()); this matches kernel LMST exactly because mission LMST kernels are a single linear segment at 88775.244 s/sol.

No public interface changed: same methods, signatures, input formats, and output strings, and from jpl_time.jpl_time import Time still works. This isn't just asserted — test/regression_data/ holds ~6,700 conversions captured from v1.4.5 (every leap second 1900–2299, all input formats, and M2020/MSL/InSight/Psyche kernel conversions), and the test suite replays them with no kernels loaded. All of the old unit tests were ported and pass unchanged.

Also in this PR:

  • Python 3.9+ with pyproject.toml; pytz replaced by stdlib zoneinfo; numpy/six/nose/tornado dropped from dependencies; spiceypy capped below 8.1.1 on Python < 3.11 (8.1.1 dropped 3.10 without updating its metadata)
  • jpl_time.py split into focused modules; jpl_time/jpl_time.py kept as a shim
  • the epoch-relative epochs feature from Improved support for Epoch-relative Epochs #1 carried into the new layout, with fixes so CVFs can reference epochs in any order (needed for write/read round trips), set_epochs rebuilds the parent map, and remove_epoch can't recurse forever
  • tests rewritten with pytest (81 → 202), GitHub Actions CI, type hints with py.typed, updated README and Sphinx docs

Two known behavior differences, both intentional: string outputs at precision 6 beyond ~year 2100 can differ from SPICE by 1 µs (SPICE's own double-precision rounding; the new integer math is exact), and the CLI without kernels now prints N/A for kernel-dependent formats instead of refusing to run.

Carolyn Ortega and others added 14 commits October 16, 2025 12:52
Captures exact outputs of the current SPICE-based implementation:
UTC/ET/TAI across 1900-2299 including every leap second, all supported
input string formats, GPS/timezone/ISO-week conversions, and per-mission
LMST/LTST/SCLK/OWLT samples (M2020, MSL, InSight, Psyche). These files
are the compatibility contract for the 2.0 refactor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 82fed0c7394df7c99a1f3ec71b7a1929de9d4d1c)
- UTC parsing/formatting, TAI/ET/GPS conversions, time zones and all
  time/duration math now run in pure Python using a built-in leap second
  table plus the full DELTET formula, matching SPICE outputs exactly.
  Loading a .tls kernel updates the table; SCLK/OWLT/LTST still use SPICE.
- New Time.set_lmst_epoch() enables kernel-free linear LMST (rate
  88775.244 s/sol, identical to single-record mission LMST kernels);
  a loaded LMST kernel always takes precedence.
- Implementation split into _time, _duration, _epoch_relative,
  _leapseconds, _lmst, _spice, _config, _cli and _exceptions;
  jpl_time.jpl_time remains as a shim so all 1.x imports keep working.
- The epoch-relative epochs feature (#1) carries into _epoch_relative.py,
  with three robustness fixes: CVF epochs may reference epochs defined
  later in the file (write_epoch_cvf sorts by time, which can place a
  negative-offset child before its parent), set_epochs fully rebuilds
  the parents map, and remove_epoch cannot recurse forever on a
  malformed parent cycle.
- Python 3 only: import fallbacks, Duration.__div__ and py2 remnants
  removed; pytz replaced with stdlib zoneinfo; regexes made raw strings.
- CLI degrades gracefully without kernels (kernel-dependent outputs N/A).

(cherry picked from commit 0e2cc0e6f45402d385938adc48808572bcbee31a)

Co-authored-by: Carolyn Ortega <carolyn.ortega@jpl.caltech.edu>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-area test files with shared mission-kernel fixtures, automatic
state isolation, and skip markers when kernels are unavailable. Most
tests run with no kernels loaded to exercise the SPICE-free paths.
test_regression_baseline.py replays the v1.4.5 baselines to pin exact
output compatibility, including linear-vs-kernel LMST equivalence.
Replaces time_test.py and duration_test.py (all cases ported, including
the epoch-relative epoch tests from #1, which gain coverage for CVF
round trips, any-order references, and chained removal).

(cherry picked from commit 50fb7e9f4b5847c4926de83925652b026021a021)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces setup.py/setup.cfg. The only runtime dependency is spiceypy;
unused dependencies (numpy, six, nose, tornado, wheel, certifi, pytz)
are dropped. Adds pytest configuration and expands .gitignore with
build artifacts and virtualenvs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit a91d667d5cc3c274f1d67330edbc750f936c5ec0)
README: Python 2 instructions removed, kernel-free scope documented,
LMST-without-kernels section and a 2.0 changelog added. Sphinx docs
rebuilt with a user guide, developer guide (architecture, leap second
math, LMST models, regression baseline rules) and an autodoc API
reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit af40426e5d4f2b68ba8c72cf33e729f0096f73e2)
spiceypy 8.1.1 dropped Python 3.10 (and 7.0 dropped 3.9) without
updating its requires-python metadata, so pip on 3.10 resolves a
release with no compatible wheel. Cap spiceypy below 8.1.1 for
python_version < 3.11 so all supported Pythons install cleanly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit fd43797878069e8f437f003fb6c83f472181f226)
Runs the test suite on Python 3.9-3.13 (the mission kernels in
test/inputs enable the full suite, including the regression baselines),
and verifies the wheel builds, installs, and runs the CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit d4e082689e10963a96ef7cd96a728c63b87c97a4)
Annotates Time, Duration, EpochRelativeTime, the config/CLI helpers and
the exception hierarchy. Arithmetic operators get @overload declarations
so type checkers resolve Time - Time -> Duration, Time - Duration ->
Time, Duration / number -> Duration, etc. without false positives.
Annotations use typing.Optional/Union so they stay valid at runtime on
Python 3.9. The py.typed marker lets mypy/pyright consume the hints.

No runtime behavior changes; all 193 tests pass on 3.9 and 3.11.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit d1c0954a6041b7e0ced5b860960da098c05636c1)
The test kernels live in git-lfs, and actions/checkout leaves them as
pointer files. furnsh silently accepts a pointer file (a text file with
no begindata block is a valid comment-only kernel), so kernel-dependent
tests failed with confusing conversion errors instead of load errors.

CI now downloads the kernels (~0.5 GB) in a single job, cached by LFS
content so matrix jobs and later runs never re-download. conftest
detects pointer files and skips kernel-dependent tests with a clear
reason, so checkouts without 'git lfs pull' degrade to the SPICE-free
subset (138 tests) instead of failing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant