python/cpython · error · TypeError
cannot mix naive and timezone-aware time
Error message
cannot mix naive and timezone-aware time
What it means
datetime.__sub__ computes a base timedelta from wall-clock differences, then adjusts for utcoffset when the two tzinfos differ. If either utcoffset() returns None (a naive datetime mixed against an aware one under differing tzinfo), the subtraction is undefined and raises TypeError('cannot mix naive and timezone-aware time').
Source
Thrown at Lib/_pydatetime.py:2372
if isinstance(other, timedelta):
return self + -other
return NotImplemented
days1 = self.toordinal()
days2 = other.toordinal()
secs1 = self._second + self._minute * 60 + self._hour * 3600
secs2 = other._second + other._minute * 60 + other._hour * 3600
base = timedelta(days1 - days2,
secs1 - secs2,
self._microsecond - other._microsecond)
if self._tzinfo is other._tzinfo:
return base
myoff = self.utcoffset()
otoff = other.utcoffset()
if myoff == otoff:
return base
if myoff is None or otoff is None:
raise TypeError("cannot mix naive and timezone-aware time")
return base + otoff - myoff
def __hash__(self):
if self._hashcode == -1:
if self.fold:
t = self.replace(fold=0)
else:
t = self
tzoff = t.utcoffset()
if tzoff is None:
self._hashcode = hash(t._getstate()[0])
else:
days = _ymd2ord(self.year, self.month, self.day)
seconds = self.hour * 3600 + self.minute * 60 + self.second
self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)
return self._hashcode
# Pickle support.View on GitHub (pinned to bc6749cc3b)
Solutions
- Make the naive side aware with its true zone: naive.replace(tzinfo=timezone.utc)
- Or strip awareness after converting: aware.astimezone(timezone.utc).replace(tzinfo=None)
- For durations, compare .timestamp() values instead of subtracting objects
- Fix custom tzinfo subclasses so utcoffset() never returns None for aware use
Example fix
// before age = datetime.now(timezone.utc) - created_at_naive # after age = datetime.now(timezone.utc) - created_at_naive.replace(tzinfo=timezone.utc)
Defensive patterns
Strategy: validation
Validate before calling
from datetime import datetime, timezone
def duration_between(a: datetime, b: datetime) -> float:
return a.timestamp() - b.timestamp() # naive assumed local; aware exact Type guard
from datetime import datetime
def both_same_awareness(a, b) -> bool:
return (a.tzinfo is None) == (b.tzinfo is None) Try / catch
try:
delta = a - b
except TypeError:
delta = a.replace(tzinfo=None) - b.replace(tzinfo=None) # wall-clock fallback Prevention
- Attach the true zone to naive DB timestamps before arithmetic
- Standardize storage as aware UTC
- Use .timestamp() for cross-system durations
When it happens
Trigger: aware_dt - naive_dt or naive_dt - aware_dt where the naive side has tzinfo None; also aware datetimes whose custom tzinfo.utcoffset() returns None (a naive-behaving tzinfo) mixed with real ones.
Common situations: Computing durations between DB naive timestamps and current aware time: now(timezone.utc) - row['created_at']; custom tzinfo subclasses that return None from utcoffset; mixed ORM sessions with and without timezone support.
Related errors
- tz argument must be an instance of tzinfo
- cannot compare naive and aware datetimes
- offset must be a timedelta
- name must be a string
- type 'datetime.timezone' is not an acceptable base type
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/6ed54d5338807496.
Report an issue: GitHub.