外观
使用 INT 存储时间戳的问题
约 789 字大约 3 分钟
2025-06-19
问题描述
项目中 MySQL 数据库使用 INT(11) 来存储日期的时间戳格式,但在 2038-01-19 11:14:07 之后的日期无法存储,提示字段超出范围(Out of range value for column)。
问题分析
INT 类型的存储范围
字段类型为 INT,长度 11,带符号型。
注意:
INT(11)中的11只是显示宽度,与存储大小无关。INT(1)和INT(11)存储的数值范围完全相同。
| 类型 | 字节 | 有符号范围 | 无符号范围 | 对应日期 |
|---|---|---|---|---|
| INT(有符号) | 4 | -2147483648 ~ 2147483647 | - | 最大到 2038-01-19 11:14:07 |
| INT(无符号) | 4 | - | 0 ~ 4294967295 | 最大到 2106-02-07 14:28:15 |
| BIGINT | 8 | ±9.2×10¹⁸ | - | 足够远 |
2038 年问题
有符号 INT 的最大值为 2147483647,转换为日期为 2038-01-19 11:14:07,这就是著名的「2038 年问题」(Year 2038 Problem)。
解决方案
如需存储 2038-01-19 11:14:07 之后的日期,可采用以下方案:
方案一:将字段改为 UNSIGNED
ALTER TABLE table_name MODIFY COLUMN timestamp_col INT(10) UNSIGNED NOT NULL;无符号 INT 最大值为 4294967295,对应日期为 2106-02-07 14:28:15,可满足长期需求。
方案二:将字段类型改为 BIGINT
ALTER TABLE table_name MODIFY COLUMN timestamp_col BIGINT UNSIGNED NOT NULL;BIGINT 范围极大,可存储到约 2920 亿年后的时间戳,完全够用。
方案三:使用 DATETIME 类型
ALTER TABLE table_name MODIFY COLUMN timestamp_col DATETIME NOT NULL;DATETIME 支持范围为
'1000-01-01 00:00:00'到'9999-12-31 23:59:59'。
方案四:存储为字符串格式
ALTER TABLE table_name MODIFY COLUMN date_str VARCHAR(10) NOT NULL;
-- 存储格式:YYYY-MM-DDINT 时间戳 vs DATETIME 对比
INT 时间戳的优点
- 存储空间小:4 字节(BIGINT 为 8 字节)vs DATETIME 的 5 字节
- 排序查询效率高:整数比较比字符串/日期比较更快
- 方便时间计算:加减秒数直接做数学运算
- 规避时区问题:时间戳始终是 UTC,不受服务器时区影响
- 便于前端格式化:在 PHP/JS 等业务层进行格式转换
INT 时间戳的缺点
- 可读性差:无法直观看到日期时间,如
1700000000 - 范围受限:INT 有上限(2038 年问题),需用 BIGINT 解决
- 缺乏内置函数支持:无法直接使用
DATE_FORMAT()等日期函数 - 时区转换需手动处理:虽然规避了时区问题,但也意味着需要自己在业务层处理时区
DATETIME 的优点
- 可读性强:直接显示
2023-11-15 10:30:00 - 内置函数丰富:支持
DATE_FORMAT()、DATEDIFF()等 - 范围极大:
1000-01-01到9999-12-31 - 支持时区:MySQL 8.0+ 支持
DATETIME与时区关联
DATETIME 的缺点
- 存储空间略大:5 字节(INT)vs 4 字节(INT)
- 比较稍慢:字符串/日期比较比整数比较慢
建议
| 场景 | 推荐方案 |
|---|---|
| 短期项目(2038 年前) | INT UNSIGNED |
| 长期项目 | BIGINT UNSIGNED 或 DATETIME |
| 需要直观展示 | DATETIME |
| 需要高性能排序 | BIGINT |
| 涉及时区处理 | DATETIME WITH TIMEZONE(MySQL 8.0+) |
