绝大多数业务场景该用 DATETIME,除非明确需要时区自动转换、自动更新、严格控存储且接受2038年限制;必须用TIMESTAMP仅当同时满足:字段为系统事件时间点、需跨时区本地等效展示、全链路时区统一、不存2038年后时间、且表中至多一个CURRENT_TIMESTAMP字段。

直接说结论:绝大多数业务场景该用 DATETIME,除非你明确需要时区自动转换 + 自动更新 + 严格控制存储空间,且能接受 2038 年限制。
什么时候必须用 TIMESTAMP
只有满足全部以下条件时,TIMESTAMP 才是合理选择:
- 字段用途是记录“系统事件发生时间点”,比如
created_at、updated_at,且要求跨时区用户看到的是“本地等效时间” - 数据库和所有客户端都统一使用
time_zone配置(不能混用+08:00、SYSTEM、UTC) - 确认业务不会存 2038 年之后的时间(比如合同到期日、生日、历史档案时间)
- 表中最多只有一个
TIMESTAMP字段设为CURRENT_TIMESTAMP默认值或自动更新——MySQL 不允许两个及以上同时设默认值
常见误用:把用户注册时间、订单创建时间、文章发布时间等业务时间字段全设成 TIMESTAMP,结果上线后发现海外用户看到的“创建时间”比实际晚/早几小时,排查半天才发现是时区转换惹的祸。
DATETIME 的默认行为更可控
DATETIME 存什么就是什么,不转换、不隐式更新、不依赖会话时区。这意味着:
- 插入
'2026-10-15 07:14:00',无论SET time_zone = '+00:00'还是SET time_zone = '+09:00',查出来永远是2026-10-15 07:14:00 - 不会因为没显式指定
DEFAULT就偷偷塞个CURRENT_TIMESTAMP(TIMESTAMP在旧版本中会这样) - 支持从公元 1000 年到 9999 年,古籍录入、长期项目规划、法律文书有效期都能覆盖
- 小数秒精度(如
DATETIME(6))和TIMESTAMP(6)完全一致,性能差异可忽略
唯一代价是多占 1 字节(DATETIME 5 字节 vs TIMESTAMP 4 字节),在现代 SSD 和内存成本下几乎无感。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
自动更新逻辑陷阱:ON UPDATE CURRENT_TIMESTAMP
这个特性只对 TIMESTAMP 原生支持(MySQL 5.6.5+ 也支持 DATETIME,但需显式声明,且兼容性差)。但要注意:
-
TIMESTAMP字段只要定义了ON UPDATE CURRENT_TIMESTAMP,哪怕你UPDATE ... SET name='xxx'没动它,它也会被强制更新——这是很多人踩坑的根源 - 如果真需要“仅首次插入设时间、后续不更新”,必须用
DATETIME DEFAULT CURRENT_TIMESTAMP(MySQL 5.6.5+ 支持),再配合应用层控制 -
DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP在TIMESTAMP上是默认行为,容易被忽略;而DATETIME必须显式写全,反而更安全
一个典型翻车现场:用户修改收货地址,结果 created_at 字段被意外刷成当前时间,导致“创建时间晚于更新时间”这种逻辑矛盾。
时区处理不是魔法,而是责任转移
TIMESTAMP 的“自动时区转换”听着很省事,实则把时区逻辑从应用层移交给了 MySQL 层,但:
- 它只转换查询结果的显示值,不改变存储本质(仍是 UTC 秒数),应用层做时间计算前仍需确认上下文
- 一旦连接时区配置出错(比如 ORM 连接池复用时区未重置),同一行数据在不同请求里返回不同时间
- 导出数据到其他系统(如 ClickHouse、ES、CSV)时,
TIMESTAMP值可能变成 UTC 时间字符串,而应用预期是本地时间,造成二次解析错误
真正健壮的做法是:统一用 DATETIME 存储“业务约定时间”,时区转换逻辑收归应用层——由代码明确调用 CONVERT_TZ() 或用语言原生时区库处理,而不是靠数据库“悄悄帮你转”。










