Files
PlanetaryTime/README.md

163 lines
5.2 KiB
Markdown

# 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
```bash
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
```python
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
```python
# 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
```python
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](https://github.com/Delgan/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
```python
from loguru import logger
logger.disable("planetarytime")
```
### Filter by level
```python
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`](src/planetarytime/_data.py). To regenerate this file from Wikidata:
```bash
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