在 MySQL 中,DATETIME 和 TIMESTAMP 都用于存储日期时间,但它们在 存储方式、范围、时区处理、存储空间 等核心方面有显著区别。以下是详细对比:
核心区别总结
| 特性 | DATETIME | TIMESTAMP |
|---|---|---|
| 时间范围 | 1000-01-01 00:00:00 到9999-12-31 23:59:59 | 1970-01-01 00:00:01 UTC 到2038-01-19 03:14:07 UTC |
| 时区处理 | 按写入值原样存储 (无时区转换) | 存入时转为 UTC 时间, 读取时转回 当前时区 |
| 存储空间 | 5 字节(MySQL 5.6.4+) | 4 字节 |
| 自动更新 | ❌ 不支持 | ✅ 支持(通过 ON UPDATE CURRENT_TIMESTAMP) |
| NULL 处理 | NULL 存入 NULL | 未显式赋值时自动填充当前时间 |
| 2038 年问题 | ❌ 不受影响 | ✅ 2038 年后溢出(范围限制) |
详细解析
1. 时间范围与存储空间
-
DATETIME- 范围:公元 1000 年 ~ 9999 年(跨千年)。
- 空间:MySQL 5.6.4+ 后固定 5 字节(旧版本 8 字节)。
-
TIMESTAMP- 范围:1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC(著名的 2038 年问题)。
- 空间:固定 4 字节(存储 Unix 时间戳整数)。
⚠️ 2038 年问题:
TIMESTAMP在 2038 年将溢出(类似 Y2K 问题),而DATETIME无此风险。
2. 时区行为(最核心区别!)
| 操作 | DATETIME | TIMESTAMP |
|---|---|---|
| 写入数据 | 直接存储客户端提交的时间字符串 (无视时区) | 将客户端时间转为 UTC 时间存储 |
| 读取数据 | 原样返回存储的字符串 (无时区转换) | 根据当前会话时区转回本地时间返回 |
示例:写入 2023-10-01 12:00:00(客户端时区为 UTC+8)
INSERT INTO table (dt_col, ts_col) VALUES ('2023-10-01 12:00:00', '2023-10-01 12:00:00');
| 操作 | DATETIME 值 | TIMESTAMP 值(存储的 UTC) |
|---|---|---|
| 存储结果 | 2023-10-01 12:00:00 | 2023-10-01 04:00:00(UTC) |
| 查询时(会话时区 UTC+8) | 返回 2023-10-01 12:00:00 | 返回 2023-10-01 20:00:00 |
| 查询时(会话时区 UTC-5) | 返回 2023-10-01 12:00:00 | 返回 2023-10-01 07:00:00 |
✅ 关键结论:
- 若需跨时区统一时间(如国际化系统),用
TIMESTAMP。- 若需存储绝对时间(如生日、合同签订时间),用
DATETIME。
3. 自动更新特性
TIMESTAMP支持自动初始化/更新:CREATE TABLE example ( created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 插入时自动填充当前时间 updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时自动修改 );DATETIME需手动实现类似功能:CREATE TABLE example ( created DATETIME DEFAULT NULL, updated DATETIME DEFAULT NULL ); -- 依赖程序或触发器更新
4. NULL 处理差异
TIMESTAMP列未显式赋值时:- 若未声明
DEFAULT值,MySQL 会自动填充当前时间(即使插入NULL)。
INSERT INTO table (ts_col) VALUES (NULL); -- 实际存入当前时间- 若未声明
DATETIME列:- 严格按写入值存储(支持
NULL)。
- 严格按写入值存储(支持
选择建议
✅ 优先用 TIMESTAMP 的场景:
- 需要自动记录行创建/修改时间(如
created_at,updated_at)。 - 处理跨时区应用(如全球用户的登录时间记录)。
- 对存储空间敏感(高频写入的大表)。
✅ 优先用 DATETIME 的场景:
- 存储超过 2038 年的时间(如历史档案、长期合同)。
- 存储与时区无关的绝对时间(如出生日期、事件计划时间)。
- 需要完整支持
NULL语义(明确区分未设置时间)。
经典示例
场景:用户订单表
CREATE TABLE orders (
id INT PRIMARY KEY,
order_time DATETIME, -- 订单生成时间(绝对时间,不受时区影响)
paid_time TIMESTAMP -- 支付成功时间(自动记录,支持时区转换)
);
-- 插入数据(北京时间 UTC+8)
INSERT INTO orders (id, order_time, paid_time)
VALUES (1, '2030-12-31 23:59:59', NULL);
-- 支付成功后更新
UPDATE orders SET paid_time = NOW() WHERE id = 1;
order_time:存入2030-12-31 23:59:59(即使服务器时区变更,值不变)。paid_time:- 存储为 UTC 时间(如
2030-12-31 15:59:59 UTC)。 - 查询时根据会话时区转换显示(如 UTC+8 显示为
2031-01-01 00:59:59)。
- 存储为 UTC 时间(如
总结
| 维度 | 选 DATETIME | 选 TIMESTAMP |
|---|---|---|
| 时间范围 | 1000年~9999年 都可选择 | 1970~2038 年内 |
| 时区需求 | 存储绝对时间(如生日) | 需自动时区转换(如用户操作时间) |
| 存储开销 | 不敏感(5 字节) | 敏感(4 字节) |
| 自动更新 | 不需要 | 需要(如 updated_at) |
| NULL 处理 | 需要明确存储 NULL | 可接受自动填充当前时间 |
💡 终极建议:
- 93% 的场景中
TIMESTAMP更适合记录时间戳(如created_at)。- 涉及历史或未来超长期时间时,必须用
DATETIME。
630

被折叠的 条评论
为什么被折叠?



