Skip to content

AttributeError: Can only use .dt accessor with datetimelike values

Does your column look like dates to you, but not to Pandas?

s = pd.Series(['2026-01-05', '2026-02-11'])

s.dt.year
AttributeError: Can only use .dt accessor with datetimelike values

The dates are readable, correctly formatted, and unambiguous. They are also text. Check the dtype and Pandas will tell you so:

s.dtype
<StringDtype(storage='python', na_value=nan)>

.dt is not a parser. It is a doorway to methods that only exist on datetime values, so it refuses anything that has not been converted yet. Convert first:

pd.to_datetime(s).dt.year
0    2026
1    2026
dtype: int32

Why the column was text in the first place

Almost always because it arrived that way. read_csv does not guess at dates unless you ask:

pd.read_csv('sales.csv', parse_dates=['when'])

That is the fix I would reach for first, since it converts once at load time rather than everywhere the column is used. If the format is unusual, pd.to_datetime(s, format='%d/%m/%Y') is both faster and safer than letting Pandas infer — inference on ambiguous days and months is how 03/04 silently becomes the wrong date.

Excel files are the exception worth knowing: read_excel converts real date cells for you, so a column that looks like text after loading a spreadsheet usually was text in the spreadsheet.

The version that feels unfair

Here is the one that sends people to search engines, because the column is not text at all:

pd.Series([pd.Timestamp('2026-01-05').date()]).dt.year
AttributeError: Can only use .dt accessor with datetimelike values

Those are Python datetime.date objects, which are genuinely dates by any reasonable reading. But Pandas stores them as generic objects rather than as its own datetime64 values, and .dt works on datetime64. The fix is the same to_datetime call, which converts them properly.

This is why .dt.date deserves a moment of thought before you use it. It returns exactly this kind of column — real dates that .dt will then refuse. If you only want to drop the time part while keeping datetime behavior, .dt.normalize() sets the time to midnight and leaves you with datetime64.

Checking before you convert

When a conversion fails on one row in ten thousand, errors='coerce' turns the failures into NaT so you can find them instead of guessing:

converted = pd.to_datetime(s, errors='coerce')
converted[converted.isna()]

An empty result means everything parsed. A non-empty one shows you the rows to look at, which is usually a footnote, a placeholder like "n/a", or a date written differently from its neighbors.

The mistakes I see most often

Assuming a successful load means a parsed date. read_csv succeeded, the column prints beautifully, and it is still text. The dtype is the only thing that settles it.

Reaching for astype('datetime64[ns]'). It works on clean data and raises unhelpfully on anything else. to_datetime has format, errors and dayfirst, and is the tool built for the job.

Using .dt where .str was wanted. If the column really is text and you only want the first four characters, s.str[:4] answers without converting anything. .dt is for date arithmetic, not for slicing strings that look like dates.

Practice

See to_datetime in Pandas for converting columns, .dt.date in Pandas for pulling the date part out, and read_csv in Pandas for parse_dates at load time.