DATETIME和TIMESTAMP本质不同:TIMESTAMP存UTC时间戳(4字节,2038年溢出),自动时区转换;DATETIME存本地时间字符串(8字节,范围1000–9999年),无时区转换。

DATETIME 和 TIMESTAMP 看起来都能存“年月日时分秒”,但选错一个,上线后就可能出时间错乱、跨时区数据对不上、2038年后写不进数据等问题——根本不是“随便用哪个都行”。
时区行为差异:为什么改个时区,TIMESTAMP 值就变了?
TIMESTAMP 存的是 UTC 时间戳(本质是 int),每次 INSERT 或 SELECT 都会自动做时区转换;DATETIME 是纯字符串式存储,写进去什么样,查出来就什么样。
- 插入时:
TIMESTAMP把客户端传的'2026-08-12 10:00:00'按当前会话时区(比如+08:00)转成 UTC(02:00:00)再存;DATETIME直接原样存'2026-08-12 10:00:00' - 查询时:
TIMESTAMP从 UTC 反向转回当前会话时区显示;DATETIME不转,永远显示插入时那个字符串 - 后果:如果数据库迁移后把
time_zone从+08:00改成+00:00,所有TIMESTAMP字段显示时间会集体“提前8小时”,而DATETIME完全不变
存储范围与 2038 年问题:TIMESTAMP 到 2038 年就停摆
TIMESTAMP 底层用 4 字节有符号整数存 Unix 时间戳,最大值是 2147483647 秒,对应 UTC 时间 2038-01-19 03:14:07。超过这个时间,MySQL 不报错,但会静默截断为 0000-00-00 00:00:00 或 1970-01-01 00:00:01(取决于版本和 SQL mode)。
-
DATETIME范围是1000-01-01 00:00:00到9999-12-31 23:59:59,完全不担心 2038 - 如果你存的是“用户生日”“合同到期日”“历史档案时间”,必须用
DATETIME;“创建时间”“更新时间”这类事件时间点,TIMESTAMP更合适 - 注意:即使你用 MySQL 8.0+,
TIMESTAMP的 4 字节限制也没变,别被“支持微秒”误导
默认值与自动更新行为:TIMESTAMP 更“主动”,但也更危险
TIMESTAMP 字段在没显式指定值时,默认会变成 CURRENT_TIMESTAMP;加上 ON UPDATE CURRENT_TIMESTAMP 后,每次 UPDATE 都会自动刷新——这很省事,但也容易误触。
-
DATETIME默认是NULL(除非设了DEFAULT),不会自动填、不会自动更新 -
TIMESTAMP的自动行为只对第一个字段生效;表里有两个TIMESTAMP字段且都设了DEFAULT CURRENT_TIMESTAMP,会直接报错 - 手动
UPDATE t SET updated_at = updated_at这种语句,也会触发ON UPDATE,导致时间被重写——这点常被忽略
字节占用与精度:别只看“小就是好”
TIMESTAMP 占 4 字节,DATETIME 占 8 字节(MySQL 5.6.4+ 支持微秒后,DATETIME 实际是 5~8 字节,但官方文档仍标为 8;TIMESTAMP 固定 4 字节)。两者都支持微秒(.fraction),精度一致。
- 空间节省对单表几万行意义不大,别拿“省4字节”当决策依据
- 真正影响性能的是索引大小和缓存效率——但两者在 B+ 树索引中表现几乎无差别
- 如果业务要兼容未来几十年,或者需要存远古/遥远未来时间,
DATETIME是唯一选择
实际建表时,最稳妥的做法是:用 TIMESTAMP 存“事件发生时刻”(如 created_at / updated_at),用 DATETIME 存“业务含义明确的时间点”(如 expire_at / birth_date)。混用或强行统一,反而更容易埋坑。


















