5.2 KiB
PlanetaryTime
Python library for representing and working with time on other bodies in the Solar System — similar in spirit to the standard datetime module.
Time on a given body is expressed using a human-readable clock where one hour is as close to one Earth hour as possible. The number of hours per sol (day) is derived from the body's sidereal rotation period rounded to the nearest integer.
Installation
pip install planetarytime
Supported bodies
Planets
| Body | Hours per sol | Sols per year |
|---|---|---|
| Mercury | 1408 | 2 |
| Venus | 5833 | 1 |
| Mars | 25 | 670 |
| Jupiter | 10 | 10476 |
| Saturn | 11 | 24491 |
| Uranus | 17 | 42718 |
| Neptune | 16 | 89667 |
Moons
Accessible via Body.<PLANET>[index], ordered by distance from the planet.
| Planet | Index | Moon | Hours per sol | Tidally locked |
|---|---|---|---|---|
| Mars | 0 | Phobos | 8 | yes |
| Mars | 1 | Deimos | 30 | yes |
| Jupiter | 0 | Io | 42 | yes |
| Jupiter | 1 | Europa | 85 | yes |
| Jupiter | 2 | Ganymede | 172 | yes |
| Jupiter | 3 | Callisto | 401 | yes |
| Saturn | 0 | Titan | 383 | yes |
| Saturn | 1 | Enceladus | 33 | yes |
| Uranus | 0 | Miranda | 34 | yes |
| Uranus | 1 | Ariel | 60 | yes |
| Uranus | 2 | Umbriel | 99 | yes |
| Uranus | 3 | Titania | 209 | yes |
| Uranus | 4 | Oberon | 323 | yes |
| Neptune | 0 | Triton | 141 | yes |
For tidally locked moons, one sol equals one year (one orbit around the parent planet).
Usage
Planets
from datetime import datetime, timezone
from planetarytime import Body, EpochType, PlanetaryTime
now = datetime.now(timezone.utc)
# Mars time since discovery (Galileo, 1610)
pt = PlanetaryTime.from_earth(now, Body.MARS, EpochType.DISCOVERY)
print(pt)
# Year 217, Sol 579, 11:00:00 (Mars / discovery epoch)
print(pt.year) # 217
print(pt.sol) # 579
print(pt.hour) # 11
print(pt.minute) # 0
print(pt.second) # 0
print(pt.time) # "11:00:00"
print(pt.date) # "Year 217, Sol 579"
# Mars time since first contact (Viking 1, 1976)
pt = PlanetaryTime.from_earth(now, Body.MARS, EpochType.CONTACT)
print(pt)
# Year 26, Sol 25, 15:00:00 (Mars / contact epoch)
Moons
# Titan (Saturn's largest moon) — accessible via Body.SATURN[0]
titan = Body.SATURN[0]
# Time since discovery (Christiaan Huygens, 1655)
pt = PlanetaryTime.from_earth(now, titan, EpochType.DISCOVERY)
print(pt)
# Year 8492, Sol 0, 344:00:00 (Titan / discovery epoch)
# Time since Huygens probe landing (2005-01-14)
pt = PlanetaryTime.from_earth(now, titan, EpochType.CONTACT)
print(pt)
# Year 486, Sol 0, 282:00:00 (Titan / contact epoch)
# Check if a moon is tidally locked
print(titan.is_tidally_locked) # True
Epochs
| EpochType | Meaning |
|---|---|
EpochType.DISCOVERY |
First recorded observation of the body |
EpochType.CONTACT |
First probe landing or crewed landing |
EpochUnavailableError is raised when CONTACT is requested for a body that has not been visited yet.
Exceptions
from planetarytime import EpochType, PlanetaryTime, Body
from planetarytime.exceptions import EpochUnavailableError, DatetimePrecedesEpochError
# Body with no contact yet
try:
PlanetaryTime.from_earth(now, Body.JUPITER, EpochType.CONTACT)
except EpochUnavailableError as e:
print(e) # No contact with Jupiter has occurred — contact epoch is unavailable.
# Datetime before the epoch
from datetime import datetime, timezone
try:
PlanetaryTime.from_earth(datetime(1600, 1, 1, tzinfo=timezone.utc), Body.MARS, EpochType.DISCOVERY)
except DatetimePrecedesEpochError as e:
print(e)
Logging
This library uses loguru for internal logging.
By default, loguru has a sink to stderr enabled. If your application also uses loguru, library logs appear in your configured sinks automatically.
Suppress library logs
from loguru import logger
logger.disable("planetarytime")
Filter by level
from loguru import logger
import sys
logger.add(sys.stderr, filter={"planetarytime": "WARNING"})
Refreshing data
Rotation periods, orbital periods, and discovery/contact dates are stored in src/planetarytime/_data.py. To regenerate this file from Wikidata:
python scripts/refresh_data.py # fetch and write _data.py
python scripts/refresh_data.py --dry-run # preview without writing
The script requires only the Python standard library. Run your test suite afterwards to verify the updated values.
License
MIT