pandas-dev/pandas · error · TypeError

Cannot convert tz-naive timestamps, use tz_localize to…

Error message

Cannot convert tz-naive timestamps, use tz_localize to localize

What it means

Raised by DatetimeArray.tz_convert when self.tz is None. tz_convert is for changing from one tz to another and presupposes that the wall times are anchored to UTC; for tz-naive data that anchor does not exist, so you must first attach one via tz_localize. The docstring example shows the expected tz-aware usage.

Solutions

  1. Use tz_localize first to attach the source tz, then tz_convert: `s.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')`.
  2. If you know the source wall time was already UTC: `s.dt.tz_localize('UTC')` (and stop if that is the desired output).
  3. Check awareness before calling: `if s.dt.tz is None: s = s.dt.tz_localize('UTC')`.

Example fix

// before
s = pd.to_datetime(df['ts']).dt.tz_convert('US/Eastern')

// after
s = pd.to_datetime(df['ts']).dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Defensive patterns

Strategy: validation

Validate before calling

import pandas as pd

def convert_tz(series, target):
    if series.dt.tz is None:
        series = series.dt.tz_localize('UTC')
    return series.dt.tz_convert(target)

Type guard

def is_tz_aware_series(s) -> bool:
    return getattr(getattr(s, 'dtype', None), 'tz', None) is not None

Try / catch

try:
    out = s.dt.tz_convert(target)
except TypeError as e:
    if 'tz-naive' in str(e):
        out = s.dt.tz_localize('UTC').dt.tz_convert(target)
    else:
        raise

Prevention

When it happens

Trigger: Calling `naive_dti.tz_convert('US/Eastern')`, `naive_series.dt.tz_convert('UTC')`, or any tz_convert on data whose `.dt.tz` is None. Common when reading CSV (which yields naive datetime64) and immediately trying to convert to a business tz.

Common situations: Loading a CSV with `pd.read_csv(parse_dates=[...])` yields tz-naive data; calling tz_convert instead of tz_localize. Assuming to_datetime gives UTC-anchored data (it does not — it gives naive wall times).

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/d760087ddc054a9f. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/arrays/datetimes.py:944

        ... )

        >>> dti
        DatetimeIndex(['2014-08-01 09:00:00+02:00',
                       '2014-08-01 10:00:00+02:00',
                       '2014-08-01 11:00:00+02:00'],
                        dtype='datetime64[us, Europe/Berlin]', freq='h')

        >>> dti.tz_convert(None)
        DatetimeIndex(['2014-08-01 07:00:00',
                       '2014-08-01 08:00:00',
                       '2014-08-01 09:00:00'],
                        dtype='datetime64[us]', freq='h')
        """  # noqa: E501
        tz = timezones.maybe_get_tz(tz)

        if self.tz is None:
            # tz naive, use tz_localize
            raise TypeError(
                "Cannot convert tz-naive timestamps, use tz_localize to localize"
            )

        # No conversion since timestamps are all UTC to begin with
        dtype = tz_to_dtype(tz, unit=self.unit)
        return self._simple_new(self._ndarray, dtype=dtype)

    @dtl.ravel_compat
    def tz_localize(
        self,
        tz,
        ambiguous: TimeAmbiguous = "raise",
        nonexistent: TimeNonexistent = "raise",
    ) -> Self:
        """
        Localize tz-naive Datetime Array/Index to tz-aware Datetime Array/Index.

        This method takes a time zone (tz) naive Datetime Array/Index object

View on GitHub (pinned to 3b7651241d)