摘要:本文主要介绍在SpringBoot
项目中数据时区存储的讨论,并介绍在常用的数据库中,应该选择什么样的类型来存储时间;下面的案例采用SpringBoot + MybatisPlus
。
背景
项目适应国际化,应该采用何种方式让不同时区的人看到各自时区的时间转换数据。
Java
常用的时间类型对比
特性 | Date | LocalDateTime | ZonedDateTime |
---|---|---|---|
时区信息 | 隐含 UTC(实际设计模糊) | 无时区 | 明确包含时区 |
可变性 | 可变(线程不安全) | 不可变(线程安全) | 不可变(线程安全) |
设计合理性 | 旧 API,存在缺陷 | 新 API,推荐使用 | 新 API,推荐使用 |
适用场景 | 兼容旧代码 | 本地时间(无时区依赖) | 跨时区时间 |
推荐使用LocalDateTime
存储的时间不带时区,更推荐的是是使用
UTC
标准时区,业务方根据当前时区转换。 如果确定只有中国,那么就存储+8
的中国时间,就不用UTC
了。
- 创建UTC时间:
LocalDateTime.now(ZoneOffset.UTC)
- 如果固定是+8的中国时区:
ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).toLocalDateTime()
如果代码中使用的是ZoneDateTime
那么你需要在存储数据库的时候使用
LocalDateTime
,其他地方使用ZoneDateTime
各种数据库时间类型推荐选择
数据库 | 不带时区的时间类型 | 说明 |
---|---|---|
MySQL | DATETIME | 范围大,无时区,避免 TIMESTAMP |
PostgreSQL | TIMESTAMP WITHOUT TIME ZONE | 明确无时区,支持高精度 |
Oracle | DATE(基础)或 TIMESTAMP(高精度) | 均不带时区,TIMESTAMP 精度更高 |
SQL Server | DATETIME2(优先)或 DATETIME | DATETIME2 精度更高,无时区 |
Kingbase | TIMESTAMP WITHOUT TIME ZONE | 兼容 PostgreSQL,无时区 |
实际使用案例
SpringBoot+Mybatis-Plus
案例
数据库表Mysql
sql
CREATE TABLE `page` (
`id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
PageEntity
less
@Data
@TableName("page")
public class PageEntity {
@TableId
private Long id;
private String name;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
PageMapper
java
@Mapper
public interface PageMapper extends BaseMapper<PageEntity> {
}
PageUtcRes + PageUtc8Res
PageUtcRes
kotlin
@Data
public class PageUtcRes {
private Long id;
private String name;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
private LocalDateTime createTime;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
private LocalDateTime updateTime;
}
PageUtc8Res
kotlin
@Data
public class PageUtc8Res {
private Long id;
private String name;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}
PageController
scss
@RestController
@RequestMapping("page")
public class PageController {
@Autowired
private PageMapper pageMapper;
@GetMapping("add")
public PageUtcRes add(){
PageEntity pageEntity = new PageEntity();
pageEntity.setId(IdWorker.getId());
pageEntity.setName(UUID.randomUUID().toString());
pageEntity.setCreateTime(LocalDateTime.now(ZoneOffset.UTC));
pageEntity.setUpdateTime(LocalDateTime.now(ZoneOffset.UTC));
pageMapper.insert(pageEntity);
PageUtcRes pageUtcRes = new PageUtcRes();
BeanUtils.copyProperties(pageEntity, pageUtcRes);
return pageUtcRes;
}
@GetMapping(value = "get")
public PageUtcRes get(Long id) {
PageEntity pageEntity = pageMapper.selectById(id);
PageUtcRes pageUtcRes = new PageUtcRes();
BeanUtils.copyProperties(pageEntity, pageUtcRes);
return pageUtcRes;
}
@GetMapping("addUtc8")
public PageUtc8Res addUtc8(){
PageEntity pageEntity = new PageEntity();
pageEntity.setId(IdWorker.getId());
pageEntity.setName(UUID.randomUUID().toString());
pageEntity.setCreateTime(ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).toLocalDateTime());
pageEntity.setUpdateTime(ZonedDateTime.now(ZoneId.of("Asia/Shanghai")).toLocalDateTime());
pageMapper.insert(pageEntity);
PageUtc8Res pageUtc8Res = new PageUtc8Res();
BeanUtils.copyProperties(pageEntity, pageUtc8Res);
return pageUtc8Res;
}
@GetMapping(value = "getUtc8")
public PageUtc8Res getUtc8(Long id) {
PageEntity pageEntity = pageMapper.selectById(id);
PageUtc8Res pageUtc8Res = new PageUtc8Res();
BeanUtils.copyProperties(pageEntity, pageUtc8Res);
return pageUtc8Res;
}
}
结果
只有中国用默认utc+8
http://localhost:8080/page/addUtc8
json
{
"id": 1946371991577559042,
"name": "9b29f0fc-36aa-4b5f-88b4-6401d5323184",
"createTime": "2025-07-19 08:50:19",
"updateTime": "2025-07-19 08:50:19"
}
http://localhost:8080/page/getUtc8?id=1946371991577559042
json
{
"id": 1946371991577559042,
"name": "9b29f0fc-36aa-4b5f-88b4-6401d5323184",
"createTime": "2025-07-19 08:50:19",
"updateTime": "2025-07-19 08:50:19"
}
国际化utc
时间
http://localhost:8080/page/add
json
{
"id": 1946377758359744513,
"name": "b9fc00d8-ebc5-4126-945b-9268f12efc93",
"createTime": "2025-07-19T01:13:14Z",
"updateTime": "2025-07-19T01:13:14Z"
}
http://localhost:8080/page/get?id=1946377758359744513
json
{
"id": 1946377758359744513,
"name": "b9fc00d8-ebc5-4126-945b-9268f12efc93",
"createTime": "2025-07-19T01:13:14Z",
"updateTime": "2025-07-19T01:13:14Z"
}