Mixing naive and aware values, or guessing which zone a naive value meant, causes the worst date bugs.
A datetime is aware if its tzinfo is set and returns an offset; then it denotes one exact instant. A naive datetime has no time zone: datetime(2025, 3, 1, 9, 0) could be 9:00 in Tokyo or in New York, and Python can't know which.
Rules of thumb:
- Get the current instant with
datetime.now(UTC)(UTCis an alias oftimezone.utc, 3.11+). datetime.utcnow()andutcfromtimestamp()are deprecated since 3.12: they return naive values that many functions then misinterpret as local time.- Convert between zones with
dt.astimezone(tz); the instant stays the same, the wall-clock reading changes. - Use
dt.replace(tzinfo=tz)only to attach a zone to a naive value you know the meaning of; it changes the instant.
You can't compare or subtract naive and aware values: Python raises TypeError, which is a good thing. Equality comparison just returns False.
timezone(timedelta(hours=2)) is a fixed offset. Fixed offsets don't know about daylight saving; for real places, use zoneinfo.ZoneInfo, covered next.
On a naive datetime, astimezone() assumes it's in the system's local time. Calling dt.astimezone() with no argument converts an aware value to local time.
Example
from datetime import UTC, datetime, timedelta, timezone
naive = datetime(2025, 3, 1, 9, 0)
aware = datetime(2025, 3, 1, 9, 0, tzinfo=UTC)
print(naive.tzinfo, aware.tzinfo) # None UTC
try:
aware - naive
except TypeError as e:
print(e) # can't subtract offset-naive and offset-aware datetimes
print(aware == naive) # False
tokyo = timezone(timedelta(hours=9))
print(aware.astimezone(tokyo)) # 2025-03-01 18:00:00+09:00
print(aware.astimezone(tokyo) == aware) # True: same instant
moved = aware.replace(tzinfo=tokyo) # different instant!
print(moved - aware) # -1 day, 15:00:00Remember
- Aware datetimes are exact instants; naive ones are ambiguous
- Use
datetime.now(UTC);utcnow()is deprecated since 3.12 astimezoneconverts;replace(tzinfo=...)reinterprets
Watch out for
- Using
replace(tzinfo=...)to convert between zones, which shifts the actual instant.