dt.year报错因列非datetime类型,需先用pd.to_datetime(..., errors='coerce')转换并确认dtype为datetime64[ns];dt.year/month/day返回int64(NaT处day为NaN);应向量化操作而非循环;时区列需先统一再取值。

为什么 dt.year 报错:不是 datetime 类型
直接对普通字符串或 object 类型列用 dt.year,会抛出 AttributeError: Can only use .dt accessor with datetimelike values。Pandas 不认它为时间,自然没 dt 可调。
必须先转成 datetime 类型,再访问:
- 用
pd.to_datetime()转换,推荐加errors='coerce',把非法日期(如"2023-99-99")转成NaT,避免整个列失败 - 转换后检查是否成功:
df["date_col"].dtype应该是datetime64[ns],不是object - 如果原始列含时区(如
"2023-01-01T12:00:00+08:00"),dt.year仍可用,但要注意dt.tz_localize()或dt.tz_convert()才能统一时区再取值
dt.year / dt.month / dt.day 的行为差异
这三个访问器返回的都是 int64 Series,但语义和边界处理不同:
-
dt.year返回完整年份(如2023),无歧义 -
dt.month返回 1–12 的整数,不是从 0 开始;"2023-01-15"→1,不是0 -
dt.day返回当月第几天(1–28/29/30/31),遇到NaT会返回NaN(float64 类型),注意后续计算可能因类型混合报错 - 它们都不支持链式格式化(比如
dt.month.astype(str).str.zfill(2)才能得到"01"),别指望dt.month_str这种不存在的属性
性能陷阱:别在循环里反复调 dt.year
有人习惯写 for idx, row in df.iterrows(): year = row["date"].year,这比向量化慢几十倍——dt.year 是专为整列设计的向量化操作。
正确做法只调一次,生成新列:
df["year"] = df["date_col"].dt.year df["month"] = df["date_col"].dt.month
如果只要某一年份的行,用布尔索引:df[df["date_col"].dt.year == 2023],而不是 df.query("date_col.dt.year == 2023")(query() 不支持 dt 访问器)。
时区感知列取 dt.hour 会出什么问题
虽然你问的是年月日,但实际中常顺带用到 dt.hour——这时时区就关键了。
- 一个
datetime64[ns, UTC]列,dt.hour返回的是 UTC 时间小时;如果想按本地时区(比如北京时间)取小时,得先dt.tz_convert("Asia/Shanghai") - 未设时区的 datetime 列(
datetime64[ns])默认视为“本地时间”,但 Pandas 不知道你本地是哪,dt.tz_localize()才能打上时区标签 - 混用时区列和无时区列做运算(比如相减),会触发
TypeError: Cannot subtract tz-naive and tz-aware datetime-like objects
年月日看似不受时区影响,但跨日界时(比如 UTC 时间 2023-01-01 23:00,在北京时间已是 2023-01-02 07:00),dt.date 和 dt.floor("D") 的结果就可能差一天——这点容易被忽略。










