在 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
。