绝大多数业务场景应选timestamp,但需存2038年后、1970年前时间或要求时区无关固定显示时必须用datetime;timestamp会随会话时区自动转换显示值,datetime则静态存储且默认值支持有版本限制。

直接说结论:绝大多数业务场景该选 TIMESTAMP,但只要满足以下任一条件,就必须用 DATETIME:
- 需要存 2038 年之后的时间(比如合同截止日、长期计划)
- 需要存 1970 年之前的时间(比如历史事件、出生日期)
- 明确要求“存什么就显示什么”,且不希望时区切换影响读取结果
什么时候 TIMESTAMP 会悄悄改你的数据?
这不是 bug,是设计行为。只要你执行了 SET time_zone = '+00:00' 或 SET time_zone = '+08:00',再查同一行 TIMESTAMP 字段,显示值就会变——底层 UTC 值没动,只是展示层按当前会话时区重新计算了。
常见踩坑场景:
- 开发本地设
time_zone = '+08:00',插入'2026-07-01 14:00:00',查出来是北京时间 - 运维在生产库执行
SET time_zone = 'UTC'做巡检,同一行查出来变成'2026-07-01 06:00:00',误以为数据被篡改 - 应用连接池未统一设置
time_zone,不同请求看到的时间不一致
解决办法不是禁用时区切换,而是接受它 —— TIMESTAMP 的价值正在于这种“动态适配”。如果你需要的是固定字符串式时间,那就别用它。
DATETIME 看似老实,但默认值陷阱很隐蔽
DATETIME 不支持 CURRENT_TIMESTAMP 作为列默认值(MySQL 5.6.5+ 才支持),而且即使写了 DEFAULT CURRENT_TIMESTAMP,也不会像 TIMESTAMP 那样自动触发 ON UPDATE CURRENT_TIMESTAMP。
容易出错的操作:
- 创建表时写
created_at DATETIME DEFAULT CURRENT_TIMESTAMP→ 在旧版本 MySQL 中直接报错 - 想让更新时自动刷新时间,却漏写
ON UPDATE CURRENT_TIMESTAMP→ 更新后字段纹丝不动 - 用字符串插入(如
'2026-07-01 14:11:00')没问题,但用NOW()插入后,后续修改时若没显式赋值,它不会变
记住:DATETIME 的“静态”是双刃剑——你控得住它,但也得自己管到底。
混合用法才是高阶实践
真实业务中,一个表里同时存在两类时间需求很常见: - 用户看到的“下单时间”,必须是固定北京时间(DATETIME)
- 后台审计用的“创建时间”,要跨时区可比对(TIMESTAMP)
推荐写法:
CREATE TABLE orders ( order_time DATETIME NOT NULL, -- 用户视角,北京本地时间 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );
关键点:
-
order_time由应用层传入明确的带时区时间(比如服务端统一转成'2026-07-01 14:11:00'再插入) -
created_at和updated_at完全交给数据库,不依赖应用逻辑 - 两者共存不冲突,MySQL 允许同一张表混用
2038 年问题不是未来的事,现在就得检查
TIMESTAMP 的上限是 '2038-01-19 03:14:07' UTC。当前已是 2026 年,意味着你设计的系统如果生命周期超过 12 年,就必须考虑这个限制。
实际风险点:
- 订单有效期设为“下单后 15 年”,2026 年下单 → 到期时间是 2041 年 →
TIMESTAMP存不下,静默截断为'0000-00-00 00:00:00'(取决于 SQL mode) - 员工合同结束日填
'2045-12-31'→ 插入失败或变零值,查不到数据 -
INSERT INTO t VALUES ('2040-01-01')不报错,但 SELECT 出来是空或非法值,排查极难
别等上线后才发现——建表前先跑一条 SELECT FROM_UNIXTIME(2147483647) 确认上限,再对照业务时间范围做判断。











