DATETIME取值范围为'1000-01-01 00:00:00'到'9999-12-31 23:59:59',TIMESTAMP为'1970-01-01 00:00:01'到'2038-01-19 03:14:07',前者支持历史与远期时间,后者受限于Unix时间戳上限。

DATETIME 和 TIMESTAMP 的取值范围差异直接影响建表决策
MySQL 里存时间,DATETIME 和 TIMESTAMP 看似都能用,但一不小心就踩进范围陷阱。比如你存一个 1970 年前的生日(如 '1969-12-31 23:59:59'),TIMESTAMP 直接报错:Incorrect datetime value——它只支持 '1970-01-01 00:00:01' 到 '2038-01-19 03:14:07'(Unix 时间戳整型上限)。而 DATETIME 是纯字符串逻辑,范围宽得多:'1000-01-01 00:00:00' 到 '9999-12-31 23:59:59'。
所以:要存历史档案、出生日期、合同起始日这类可能超 1970 或跨 2038 的时间,必须选 DATETIME;仅记录“最近几年的操作日志”,且需自动更新,TIMESTAMP 才有优势。
TIMESTAMP 自动时区转换是双刃剑
TIMESTAMP 存值时会从当前会话时区转为 UTC,读取时再转回会话时区;DATETIME 完全不碰时区,原样存原样取。这导致几个典型现象:
- 同一行数据,在上海查是
'2024-05-20 14:30:00',在纽约查变成'2024-05-20 02:30:00'(如果用TIMESTAMP) - 服务器时区改了(比如从
SYSTEM改成'+08:00'),所有已存的TIMESTAMP值显示时间会变,但实际 UTC 值没动 - 用
CONVERT_TZ()处理DATETIME是显式可控的,而TIMESTAMP的隐式转换容易让人误以为“数据变了”
如果你的应用部署在多时区节点(如用户端全球分布、服务端混用云厂商不同区域实例),又没统一强制设置 time_zone 会话变量,TIMESTAMP 会悄悄引入时间错乱。
自动初始化与更新行为不能只看 DEFAULT
两者都支持 DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP,但底层机制不同:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
TIMESTAMP字段默认开启“自动初始化 + 自动更新”,哪怕你只写DEFAULT CURRENT_TIMESTAMP,它也会默默加上ON UPDATE CURRENT_TIMESTAMP(除非显式写成ON UPDATE CURRENT_TIMESTAMP(0)或禁用) -
DATETIME从 MySQL 5.6.5 起才支持这些特性,且必须显式声明,不会自动补全 - 一张表里只能有一个
TIMESTAMP字段能设DEFAULT CURRENT_TIMESTAMP(旧版本限制,新版本放宽但仍有隐式约束)
常见翻车场景:想让 created_at 和 updated_at 都自动更新,结果两个都是 TIMESTAMP,第二个字段的 DEFAULT 被忽略或报错;换成 DATETIME 就没这限制,写法更直白。
存储空间和索引效率差异微小但真实存在
TIMESTAMP 占 4 字节,DATETIME 占 5–8 字节(MySQL 5.6.4+ 支持毫秒精度后,DATETIME(3) 是 6 字节,DATETIME(6) 是 8 字节);单条记录省 1–4 字节,海量数据下磁盘和内存占用有差别,但通常不是瓶颈。
真正要注意的是:当你要对时间字段建索引做范围查询(比如 WHERE created_at BETWEEN ? AND ?),TIMESTAMP 因为内部转 UTC,查询条件也会被时区转换——如果应用层没注意传入时间的时区上下文,可能查不到数据或查偏。而 DATETIME 索引完全按字面值匹配,行为确定。
时区配置、范围边界、自动行为这三个点串起来,才是决定用哪个的关键。别只看文档说“TIMESTAMP 节省空间”就拍板,先想清楚你的数据有没有 1970 年前的值、是否需要跨时区一致显示、有没有多个时间戳字段要自动更新。










