最轻量安全提取datetime64成分的方式是用.dt.year等属性,支持向量化、nat友好、避免strftime报错,时区需先localize再convert,慎用round以免改变原始精度。

用 dt 访问器提取年月日时分秒,别直接调 strftime
直接对 datetime64 列用 .dt.year、.dt.month 这类属性是最轻量、最安全的提取方式。它底层走的是向量化操作,不触发 Python 循环,也不依赖字符串解析。
常见错误是先转成字符串再切片或正则匹配,比如 df['ts'].dt.strftime('%Y-%m').str[:4]——这不仅慢,还会在遇到 NaT 时抛 AttributeError: Can only use .str accessor with string values。
-
.dt.year、.dt.dayofweek、.dt.quarter等返回整数数组,天然支持布尔索引和分组 -
.dt.is_month_start、.dt.is_leap_year这类布尔属性,比手写条件判断更准确(比如自动处理闰年) - 注意
.dt.day是「当月第几天」(1–31),不是「当年第几天」;后者是.dt.dayofyear
dt 属性在 NaT 值下的行为:空值不报错,但返回 NaN 或 NaT
.dt 访问器对缺失时间戳(NaT)是友好的:所有数值型属性(如 .dt.hour)返回 NaN,日期型属性(如 .dt.date)返回 NaT。这和 .dt.strftime() 不同——后者遇到 NaT 直接崩。
- 如果后续要导出 Excel 或存 CSV,
NaN可被识别为空,NaT在 pandas 里也能正常序列化 - 但若用
.dt.time提取时间对象,NaT会变成NaT,不能直接传给某些数据库驱动(如 SQLAlchemy 的 TIME 类型可能拒收) - 需要填充空值时,优先用
.fillna(pd.NaT)或.fillna(0),别用.replace({pd.NaT: 0})——后者可能误伤合法的0值
时区感知时间戳必须先 dt.tz_localize 或 dt.tz_convert 才能安全取 .dt.hour
带时区的时间戳(datetime64[ns, UTC])可以直接用 .dt.hour,但结果是本地时区下的小时数。如果原始数据没时区信息(datetime64[ns]),却误以为是 UTC,直接取 .dt.hour 会导致全表偏移。
- 未有时区:用
.dt.tz_localize('UTC')声明它是 UTC 时间,再.dt.tz_convert('Asia/Shanghai')转成本地时间后取.dt.hour - 已有时区但想统一为北京时间:跳过
tz_localize,直接.dt.tz_convert('Asia/Shanghai') - 性能敏感场景慎用
.dt.tz_convert:它会拷贝整个数组,大数据量下比纯数值属性慢一个数量级
别把 .dt.round() 当提取工具用,它改的是原始值
.dt.round('D') 或 .dt.floor('H') 返回的是新的时间戳(如把 2023-01-01 14:33:22 变成 2023-01-01 00:00:00),不是提取某个成分。它常被误用于“归到当天”,但实际改变了原始精度。
- 真要“只保留日期部分”,用
.dt.date(返回 Pythondate对象)或.dt.normalize()(返回datetime64,时部分归零) -
.dt.round('MS')(Month Start)会把 1 月 15 日变成 1 月 1 日,但 12 月 31 日会变成下一年 1 月 1 日——这个边界行为容易被忽略 - 如果只是为分组做准备,
groupby(df['ts'].dt.date)比先round再groupby更省内存
NaT 兼容性是跨项目迁移时最容易出问题的两个点,尤其当数据来自不同系统、有的带时区有的不带,有的用 None 有的用 NaT。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











