SpringBoot数据存储时区选择,符合国际化和特定时区方案

摘要:本文主要介绍在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"
}
相关推荐
卷无止境12 小时前
独立开发者的"富矿地带":哪些垂直领域值得你押注一辈子?
后端·python
谢亮_vipxieliang12 小时前
Spring 事务失效的常见场景
java·开发语言·数据库·spring boot
狼爷12 小时前
从零用 Java 构建 AI Agent 框架:JavaManus 设计与实现深度解析
后端·langchain·aigc
郑州光合科技余经理12 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
专业程序开发源13 小时前
springboot简历管理系统81389-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·php·课程设计
专业程序开发源13 小时前
springboot社区养老系统44071-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·django·php·课程设计
小呆呆66613 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
周杰伦fans13 小时前
8GB显存下模型量化实战指南
人工智能·后端·c#
余槐i14 小时前
数据没回滚也不报错:@Transactional 自调用失效的 3 种复现与修复
spring boot·mysql·多线程·spring 事务
小蒜学长14 小时前
基于SpringBoot的佳新超市管理系统设计与实现系统(代码+数据库+LW)
java·数据库·spring boot·后端·佳新超市管理系统