pandas-dev/pandas · error · TypeError
Start and end cannot both be tz-aware with different timezon
Error message
Start and end cannot both be tz-aware with different timezones
What it means
Raised by _infer_tz_from_endpoints (used by date_range and similar generators) when both `start` and `end` are tz-aware but their tzinfos disagree. The function calls timezones.infer_tzinfo which raises an AssertionError on mismatch; that is caught and re-raised as a TypeError with this message. The function's Raises contract documents this behavior.
Source
Thrown at pandas/core/arrays/datetimes.py:3087
Parameters
----------
start : Timestamp
end : Timestamp
tz : tzinfo or None
Returns
-------
tz : tzinfo or None
Raises
------
TypeError : if start and end timezones do not agree
"""
try:
inferred_tz = timezones.infer_tzinfo(start, end)
except AssertionError as err:
# infer_tzinfo raises AssertionError if passed mismatched timezones
raise TypeError(
"Start and end cannot both be tz-aware with different timezones"
) from err
inferred_tz = timezones.maybe_get_tz(inferred_tz)
tz = timezones.maybe_get_tz(tz)
if tz is not None and inferred_tz is not None:
if not timezones.tz_compare(inferred_tz, tz):
raise AssertionError("Inferred time zone not equal to passed time zone")
elif inferred_tz is not None:
tz = inferred_tz
return tz
def _maybe_normalize_endpoints(
start: _TimestampNoneT1, end: _TimestampNoneT2, normalize: boolView on GitHub (pinned to 3b7651241d)
Solutions
- Align both endpoints to the same tz before calling date_range: `end = end.tz_convert(start.tz)`.
- Drop tz from both sides if you want a tz-naive range.
- Construct the range in UTC and convert the result: `pd.date_range(..., tz='UTC').tz_convert(target)`.
Example fix
// before rng = pd.date_range(start=ts_est, end=ts_utc) // after rng = pd.date_range(start=ts_est, end=ts_utc.tz_convert(ts_est.tz))
Defensive patterns
Strategy: validation
Validate before calling
import pandas as pd
def aligned_endpoints(start, end):
stz = getattr(start, 'tzinfo', None) if start is not None else None
etz = getattr(end, 'tzinfo', None) if end is not None else None
if stz is not None and etz is not None and stz != etz:
end = end.tz_convert(stz)
return start, end Type guard
def endpoints_tz_compatible(start, end) -> bool:
stz = getattr(start, 'tzinfo', None) if start is not None else None
etz = getattr(end, 'tzinfo', None) if end is not None else None
return stz is None or etz is None or stz == etz Try / catch
try:
rng = pd.date_range(start=start, end=end)
except TypeError as e:
if 'different timezones' in str(e):
end = end.tz_convert(start.tz)
rng = pd.date_range(start=start, end=end)
else:
raise Prevention
- Normalize all date_range endpoints to one tz at ingest.
- Use UTC throughout internally; convert only at presentation.
When it happens
Trigger: Calling `pd.date_range(start=pd.Timestamp('2020-01-01', tz='US/Eastern'), end=pd.Timestamp('2020-01-02', tz='UTC'))`. Any API that internally calls _infer_tz_from_endpoints with mismatched-aware endpoints.
Common situations: Building endpoints from different sources (one from a DB in UTC, one user-supplied in local tz). Hardcoded start tz vs. dynamic end tz. Misconfigured tz on one side of a date window.
Related errors
- Inferred time zone not equal to passed time zone
- DatetimeIndex has mixed timezones
- data is already tz-aware {inferred_tz}, unable to set specif
- cannot supply both a tz and a dtype with a tz
- Cannot pass both a timezone-aware dtype and tz=None
AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11).
Data as JSON: /api/errors/7c32a1b9b3b37322.
Report an issue: GitHub.