背景介绍
项目中为兼容多时区方案,MySQL设计数据库表时,对于时间的设置,最开始才用timeStamp类型
发现一个问题,MySQL TIMESTAMP 2038 限制(Y2038 问题)
概念介绍
UTC 是世界协调时间 ,不随地区变化的全球统一时间
时区是人为定义的,会因为国家规定、政策、夏令时而改变
Y2038时间限制的根源
TIMESTAMP类型的底层存储逻辑是:将时间转换为 UTC 时间戳,并以32 位有符号整数存储表达时间有限
32 位有符号整数的最大值:
2^31 - 1 = 2147483647秒对应 UTC 时间:
2038-01-19 03:14:07超出该值后,32 位整数会溢出为负数,导致时间回绕到
1901-12-13 20:45:52 UTC
所以在MySQL数据库设计中避免使用TIMESTAMP类型
其他数据库有无此限制
| 数据库 | 是否有 2038 限制(原生时间类型) | 关键说明 |
|---|---|---|
| MySQL | ✅ 有(TIMESTAMP 类型) | 普通 TIMESTAMP 基于 32 位整数,DATETIME/ BIGINT 无限制 |
| MariaDB | ✅ 有(普通 TIMESTAMP) | 10.1+ 新增 TIMESTAMP WITH TIME ZONE(64 位),无 2038 限制 |
| PostgreSQL | 无 | 时间类型基于 64 位整数,范围覆盖 BC 4713 ~ AD 294276 |
| Oracle | 无 | DATE/TIMESTAMP 基于字节存储(非时间戳),范围 BC 4712 ~ AD 9999 |
| SQL Server | 无 | datetime/datetime2 无 2038 限制,smalldatetime 上限 2079(非 2038 问题) |
| SQLite | 无(默认) | 无专用时间类型,手动用 32 位整数存时间戳才会触发,64 位 / 字符串存储则无 |
| DB2 | 无 | TIMESTAMP 范围 0001-01-01 ~ 9999-12-31,无 2038 限制 |
结论,常见数据库中只有MySQL数据库会有此类问题
使用DateTime类型数据类型
MySQL连接配置
XML
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/your_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
目的: 无论数据库服务器在哪里,都让 JDBC 连接设为 serverTimezone=UTC
Spring Boot 全局使用 UTC
XML
spring:
jackson:
time-zone: UTC
date-format: yyyy-MM-dd'T'HH:mm:ss
目的: 防止 Jackson 自动转换时区,返回给前端的所有时间都是 UTC
java
@Configuration
public class TimeZoneConfig {
@PostConstruct
public void setDefaultTimeZone() {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
}
**目的:**需要设置 JVM 默认时区为 UTC
后台使用类型
推荐使用LocalDateTime,但Date也可以(不带时区,支持UTC)