# Fix astropy Time scale, format, and leap-second mistakes
Use this when a `Time` object holds the wrong instant, construction raises
`ValueError`, `to_datetime()` explodes, or UTC-vs-TAI arithmetic gives
answers off by tens of seconds. Most `Time` bugs come from the scale being
wrong, and the scale is usually wrong because it was never stated.
## 1. Print scale and format before anything else
```python
from astropy.time import Time
t = Time('2026-09-17 12:00:00')
print(t.scale, t.format) # utc iso
```
Two defaults cause most of the trouble:
- **Default scale is UTC** — except for the epoch formats, which carry an
intrinsic scale: `unix` → UTC (ref 1970-01-01), `unix_tai` → TAI,
`gps` → TAI (ref 1980-01-06 00:00:19), `cxcsec` → TT (ref 1998-01-01).
The same number fed as `format='unix'` vs `format='unix_tai'` names two
different instants.
- **Format is guessed from the string** — `Time('2010-01-02 01:02:03')`
becomes `iso`; unambiguous inputs are fine, but if the guess is wrong you
get a `ValueError` listing the candidate formats. Then pass `format=`
explicitly (and `in_subfmt`/`out_subfmt` wildcards for format variants).
If the instant looks wrong, check whether you fed TAI/GPS/TT numbers into a
UTC-default constructor — that is the bug nine times out of ten.
## 2. Convert scales with attribute access; state the scale at construction
```python
t = Time('2026-09-17 12:00:00', scale='utc')
t.tai # new Time object, same instant, TAI scale
t.tt # Terrestrial Time
t.tdb # Barycentric Dynamical Time
```
Attribute access (`.utc`, `.tai`, `.tt`, `.tdb`, `.ut1`, ...) converts and
returns a *new* object; the original is unchanged. For sidereal time:
```python
t.sidereal_time('apparent', 'greenwich') # Longitude in hourangle
```
`kind` is `'mean'` or `'apparent'`; the longitude can be a string like
`'greenwich'`, an `EarthLocation`, or a longitude. Apparent sidereal time
goes through UT1, so it needs the IERS Earth-rotation table — auto-downloaded
on first use, with bundled data as the offline fallback.
## 3. TimeDelta has no 'utc' scale — that is the fix, not the bug
Subtracting two UTC times does not give a UTC delta:
```python
dt = t2 - t1
print(dt.scale) # 'tai' -- a UTC day is not always 86400 s, so deltas cannot be UTC
```
Valid `TimeDelta` scales are the geocentric (`tai`, `tt`, `tcg`),
barycentric (`tcb`, `tdb`), rotational (`ut1`), and `local` scales —
`'utc'` raises `ScaleValueError`. If you need uniform SI-second intervals
(durations, rates, ephemeris math), do the arithmetic in TAI:
```python
from astropy.time import TimeDelta
dt = TimeDelta(3600.0, format='sec', scale='tai')
t2 = t.tai + dt # Time + TimeDelta: scales are reconciled automatically
```
`Time + TimeDelta` works across compatible scales (a TAI delta added to a
UTC time is fine), and plain quantities work too:
`Time("2020-01-01") + 5 * u.day`.
## 4. Leap seconds: representable in Time, not in datetime
astropy can hold the leap second itself —
`Time('2016-12-31 23:59:60', scale='utc')` is valid — but Python `datetime`
cannot. Converting out needs a policy:
```python
t.to_datetime() # raises on times inside a leap second
t.to_datetime(leap_second_strict='warn') # 'raise', 'warn', or 'silent'
```
The leap-second table also goes stale: astropy refreshes IERS data
automatically (`from astropy.time import update_leap_seconds` forces it), so
a machine that has been offline can warn about expired leap-second data.
`Time.now()` gives the current instant from the system clock on the UTC
scale.
## 5. Precision: trust jd1/jd2, not .jd
Internally every `Time` is two float64s (`jd1`, `jd2`) holding
sub-nanosecond precision over the age of the universe. A single `.jd`
float64 keeps only ~microsecond precision over human timescales. When you
need the digits — file round-trips, ephemerides — use the high-precision
outputs:
```python
t.to_value('mjd', 'long') # numpy.longdouble
t.to_value('mjd', 'decimal') # decimal.Decimal, full precision
t.to_value('mjd', 'str') # exact string round-trip
```
And on input, `val2` exists for the same reason:
`Time(100.0, 0.000001, format='mjd', scale='tt')` keeps both doubles.
String output precision is `t.precision` (integer 0–9, default 3).
## 6. Checklist
1. Print `t.scale` and `t.format`; confirm the scale is the one you meant.
2. Wrong instant → you probably fed TAI/GPS/TT numbers to a UTC-default
constructor; state `scale=` explicitly.
3. `TimeDelta(..., scale='utc')` raises → use `'tai'`; differences of UTC
times come back TAI by design.
4. `to_datetime()` fails → the time is inside a leap second; set
`leap_second_strict='warn'` or `'silent'`.
5. Digits lost → read `jd1`/`jd2` or `to_value(..., 'decimal'/'str')`,
never a single `.jd` float.
6. Format guess wrong → pass `format=` explicitly (plus
`in_subfmt`/`out_subfmt` wildcards for variants).