首页 / Pandas 入门教程 / 日期时间基础:Timestamp 与 to_datetime

Pandas 入门教程

日期时间基础:Timestamp 与 to_datetime

本教程共 54 篇 · 第 44 篇 · 更新于 2026-08-11 · 约 8 分钟阅读

PandasPandas 入门教程Timestampto_datetime日期时间Period

本节目标:认识 pandas 的时间类型 Timestamp,学会用 to_datetime 把各种格式的日期字符串变成真正的时间,并掌握格式化输出和 Period 时间段。

为什么字符串不够用

日期数据最常以字符串形式出现:“2024-01-15”、“2024/1/15”、“20240115”……看起来能读,但字符串有几个硬伤:

  1. 不能比较。字符串比较按字典序,“2024-1-2” 和 “2024-1-10”,字典序里 “2024-1-2” 更大,但按日期它更小。
  2. 不能运算。“2024-01-15” 加 3 天等于什么?字符串没有答案。
  3. 格式混乱。同一天有十几种写法,程序没法统一处理。

所以 pandas 提供了专门的时间类型。拿到数据的第一步,就是尽早把日期字符串转成真正的时间。

pandas 的四个时间概念

pandas 把”时间”拆成四个概念,先混个脸熟:

概念标量类数组类一句话
时间点TimestampDatetimeIndex某个具体时刻,如 2024-01-15 08:30
时间差TimedeltaTimedeltaIndex一段时长,如 3 天 2 小时
时间段PeriodPeriodIndex一段区间,如 2024 年 1 月
日期偏移DateOffset日历运算,如”下个工作日”

本节先讲时间点 Timestamp 和它的批量版本;时间差、偏移留给后面章节。

Timestamp:单个时间点

Timestamp 是 pandas 的标量时间类型,表示一个精确的时间点。创建方式很灵活:

import pandas as pd

# 字符串创建
print(pd.Timestamp("2024-01-15"))
# 2024-01-15 00:00:00

# 位置参数:年、月、日
print(pd.Timestamp(2024, 1, 15))

# 带时间
print(pd.Timestamp("2024-01-15 08:30:00"))

# 从 Python 原生 datetime 转换
import datetime
print(pd.Timestamp(datetime.datetime(2024, 1, 15)))

拿到 Timestamp 之后,年、月、日、星期几这些信息都能直接取:

import pandas as pd

t = pd.Timestamp("2024-01-15 08:30:00")

print(t.year)        # 2024
print(t.month)       # 1
print(t.day)         # 15
print(t.hour)        # 8
print(t.dayofweek)   # 0,周一(0=周一,6=周日)
print(t.day_name())  # Monday
print(t.quarter)     # 1,第一季度
print(t.is_month_start)  # False,不是月初

底层上,Timestamp 由 NumPy 的 datetime64 类型支撑,一组 Timestamp 存进 Series 后,dtype 显示为 datetime64[us]us 是微秒(microsecond)。pandas 3.0 起解析普通字符串默认用微秒精度,只有字符串自带纳秒精度时才会用 ns

时间序列里没有”缺失日期”的概念,缺失的时间统一用 NaT(Not a Time)表示,作用和 NaN 一样。

批量转换:to_datetime

实际数据里日期是成列的,不会一个一个转。pd.to_datetime 是批量转换的主力,输入什么类型,输出什么类型:

import pandas as pd

# 列表 -> DatetimeIndex(时间索引)
print(pd.to_datetime(["2024-01-01", "2024-01-02", "2024-01-03"]))
# DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03'], dtype='datetime64[us]', freq=None)

# 单个字符串 -> Timestamp
print(pd.to_datetime("2024-01-01"))
# 2024-01-01 00:00:00

# Series -> Series(列转时间类型)
s = pd.Series(["2024-01-01", "2024-01-02"])
print(pd.to_datetime(s))
# 0   2024-01-01
# 1   2024-01-02
# dtype: datetime64[us]

读 CSV 时也可以直接用 parse_dates 参数让 pandas 在读入阶段就解析日期列(§11),效果和读完后 to_datetime 一样。

指定格式:format

大部分字符串 pandas 能自动识别。但格式太刁钻,或者想加快解析速度,可以手动指定 format。格式代码和 Python 的 strftime 一致:%Y 四位年、%m 两位月、%d 两位日、%H 小时、%M 分钟。

import pandas as pd

# "日月年"的顺序,必须显式指定格式
print(pd.to_datetime("12-11-2010 10:00", format="%d-%m-%Y %H:%M"))
# 2010-11-12 10:00:00

这里 “12” 是日、“11” 是月,不指定格式会被当成”月-日-年”解析成 2010-12-11,语义完全不同。

Warning

pandas 2.0 起,一个列表里混着多种格式的字符串,默认会直接报错,不再自动逐个猜。要么保证格式统一,要么显式传 format="mixed"pd.to_datetime(["2024-01-01", "2024/01/02"], format="mixed")。格式统一时指定 format 还能明显提速。

解析失败怎么办:errors

数据里总有脏值。errors="coerce" 把解析不了的值变成 NaT,而不是让整个转换崩掉:

import pandas as pd

print(pd.to_datetime(["2024-01-01", "invalid", "2024-01-03"], errors="coerce"))
# DatetimeIndex(['2024-01-01', 'NaT', '2024-01-03'], dtype='datetime64[us]', freq=None)

时间戳(epoch)转换

有些系统用整数时间戳存时间,比如 Unix 时间戳:从 1970-01-01 起算的秒数。用 unit 告诉 pandas 单位是什么:

import pandas as pd

# unit="s":按秒解释
print(pd.to_datetime([1349720105, 1349806505], unit="s"))
# DatetimeIndex(['2012-10-08 18:15:05', '2012-10-09 18:15:05'], dtype='datetime64[s]', freq=None)

# unit="ms":按毫秒解释
print(pd.to_datetime([1349720105100, 1349806505100], unit="ms"))

反过来,把时间转回时间戳:(索引 - pd.Timestamp("1970-01-01")) // pd.Timedelta("1s"),或者用 astype(np.int64)

从多个列组装日期

日期被拆成了”年、月、日”三列?不用拼字符串,直接把 DataFrame 传给 to_datetime 就行:

import pandas as pd

df = pd.DataFrame({"year": [2015, 2016], "month": [2, 3], "day": [4, 5]})
print(pd.to_datetime(df))
# 0   2015-02-04
# 1   2016-03-05
# dtype: datetime64[us]

列名必须是 year、month、day 这种标准名字,可选 hour、minute、second。

格式化输出:strftime

时间转成字符串用 strftime,格式代码和前面 format 一样。对单个 Timestamp 直接调,对一列时间用 .dt.strftime

import pandas as pd

t = pd.Timestamp("2024-01-15")
print(t.strftime("%Y年%m月%d日"))
# 2024年01月15日

print(t.strftime("%A"))
# Monday

# 一列时间批量格式化
s = pd.Series(pd.date_range("2024-01-15", periods=3, freq="D"))
print(s.dt.strftime("%Y-%m-%d"))
# 0    2024-01-15
# 1    2024-01-16
# 2    2024-01-17
# dtype: str

.dt 是 Series 的日期访问器,后面跟 year、month、day_name()、strftime 等方法,作用是把”每个元素的时间属性”批量取出来。日期的星期名、季度、是否月初等,也都在 .dt 上。

日期的比较与排序

转成时间类型后,比较和排序也顺理成章了。这正是开头说”字符串不够用”的根源:

import pandas as pd

s = pd.Series(pd.to_datetime(["2024-01-10", "2024-01-02", "2024-01-05"]))

print(s < pd.Timestamp("2024-01-06"))
# 0    False
# 1     True
# 2     True
# dtype: bool

print(s.sort_values())
# 1   2024-01-02
# 2   2024-01-05
# 0   2024-01-10
# dtype: datetime64[us]

排序、去重、比较大小,时间列全都支持。配合布尔索引(§20),“筛选出 2024 年 1 月 6 日之前的记录”就是一行代码的事。

Period:表示时间段

有些数据对应的不是某个时刻,而是一整个时间段。“2024 年 1 月的销售额”,时间点是 1 月 31 日 23:59:59 还是 1 月 1 日 00:00:00?都不对,它属于整个 1 月。这种数据用 Period(时间段)更合适:

import pandas as pd

p = pd.Period("2024-01")     # 不写 freq,默认按字符串推断为月
print(p)
# 2024-01

print(p + 1)                 # 时间段可以平移
# 2024-02

# 指定频率:按天
print(pd.Period("2024-01-15", freq="D"))
# 2024-01-15

# 月时间段转成时间段内第一天
print(p.asfreq("D", "start"))
# 2024-01-01

Period 最大的好处是”能运算、能比较、天然对齐”,比如 pd.period_range("2024-01", periods=4, freq="M") 生成一串连续月份。把时间戳列变成 Period,常用 .dt.to_period

import pandas as pd

s = pd.Series(pd.date_range("2024-01-15", periods=3, freq="D"))
print(s.dt.to_period("M"))
# 0    2024-01
# 1    2024-01
# 2    2024-01
# dtype: period[M]

三个不同的日子都归到 2024-01 这个时间段,后面做”按月份分组统计”(§46 重采样)时非常顺手。

Tip

拿不准用 Timestamp 还是 Period:数据是”某时刻发生了什么”用 Timestamp;数据是”某段时间内累计的结果”(月报、季报)用 Period。

小结

这一节把时间类型的地基打好了:Timestamp 表示时刻,to_datetime 负责批量转换,strftime 负责格式化,Period 表示时间段。下一节把时间放到索引上,讲讲 DatetimeIndex 和 date_range 生成日期范围。