在咱们日常工作中,工夫格式化是一件常常遇到的事儿,所以本文咱们就来盘点一下 Spring Boot 中工夫格式化的几种办法。
工夫问题演示
为了不便演示,我写了一个简略 Spring Boot 我的项目,其中数据库中蕴含了一张 userinfo 表,它的组成构造和数据信息如下:
我的项目目录是这样的:
UserController 实现代码如下:
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserMapper userMapper;
@RequestMapping("/list")
public List<UserInfo> getList() {return userMapper.getList();
}
}
UserMapper 实现代码如下:
@Mapper
public interface UserMapper {public List<UserInfo> getList();
}
UserInfo 实现代码如下:
@Data
public class UserInfo {
private int id;
private String username;
private Date createtime;
private Date updatetime;
}
UserMapper.xml 实现代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="getList" resultType="com.example.demo.model.UserInfo">
select * from userinfo
</select>
</mapper>
通过以上内容的编写,咱们就制作出了一个简略的 Spring Boot 我的项目了。接下来,咱们应用 PostMan 来模仿调用 UserController 接口,执行后果如下:
从上述后果能够看出,工夫字段 createtime
和 updatetime
的显示方式是很“凌乱”的,并不合乎咱们的浏览习惯,也不能间接展现给前端的用户应用,这时候,咱们就须要对工夫进行格式化解决了。
工夫格式化的办法总共蕴含以下 5 种。
1. 前端工夫格式化
如果后端在公司中领有相对的话语权,或者是后端比拟强势的状况下,咱们能够将工夫格式化的这个“锅”强行甩给前端来解决。
为了让这个“锅”甩的更平顺一些(磊哥不做厨师都惋惜了),咱们能够给前端工程师提供切实可行的工夫格式化办法,实现代码如下。
JS 版工夫格式化
function dateFormat(fmt, date) {
let ret;
const opt = {"Y+": date.getFullYear().toString(), // 年
"m+": (date.getMonth() + 1).toString(), // 月
"d+": date.getDate().toString(), // 日
"H+": date.getHours().toString(), // 时
"M+": date.getMinutes().toString(), // 分
"S+": date.getSeconds().toString() // 秒
// 有其余格式化字符需要能够持续增加,必须转化成字符串
};
for (let k in opt) {ret = new RegExp("(" + k + ")").exec(fmt);
if (ret) {fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
};
};
return fmt;
}
办法调用:
let date = new Date();
dateFormat("YYYY-mm-dd HH:MM:SS", date);
>>> 2021-07-25 21:45:12
2.SimpleDateFormat 格式化
大多数状况下,咱们还是须要自力更生,各扫门前雪的,这个时候咱们后端程序员就须要施展本人的专长了,咱们提供的第 1 个工夫格式化的办法是应用 SimpleDateFormat
来进行工夫格式化,它也是 JDK 8 之前重要的工夫格式化办法,它的外围实现代码如下:
// 定义工夫格式化对象和定义格式化款式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 格式化工夫对象
String date = dateFormat.format(new Date())
接下来咱们应用 SimpleDateFormat
来实现一下本我的项目中的工夫格式化,它的实现代码如下:
@RequestMapping("/list")
public List<UserInfo> getList() {
// 定义工夫格式化对象
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<UserInfo> list = userMapper.getList();
// 循环执行工夫格式化
list.forEach(item -> {// 应用预留字段 ctime 接管 createtime 格式化的工夫 (Date->String)
item.setCtime(dateFormat.format(item.getCreatetime()));
item.setUtime(dateFormat.format(item.getUpdatetime()));
});
return list;
}
程序执行后果如下:
从上述后果能够看出,工夫格式化没有任何问题,以及到底咱们料想的目标了。但仔细的读者会发现,为什么接口的返回字段咋变了呢?(之前的字段是 createtime
当初却是 ctime
…)
这是因为应用 #SimpleDateFormat.format
办法之后,它返回的是一个 String
类型的后果,而咱们之前的 createtime
和 updatetime
字段都是 Date
类型的,因而它们是不能接管工夫格式化得后果的。
所以此时咱们就须要在实体类 UserInfo
新增两个字符串类型的“工夫”字段,再将之前 Data
类型的工夫字段进行暗藏,最终实体类 UserInfo 的实现代码如下:
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private int id;
private String username;
@JsonIgnore // 输入后果时暗藏此字段
private Date createtime;
// 工夫格式化后的字段
private String ctime;
@JsonIgnore // 输入后果时暗藏此字段
private Date updatetime;
// 工夫格式化后的字段
private String utime;
}
咱们能够应用 @JsonIgnore
注解将字段进行暗藏,暗藏之后的执行后果如下:
3.DateTimeFormatter 格式化
JDK 8 之后,咱们能够应用 DateTimeFormatter
来代替 SimpleDateFormat
,因为 SimpleDateFormat
是非线程平安的,而 DateTimeFormatter
是线程平安的,所以如果是 JDK 8 以上的我的项目,尽量应用 DateTimeFormatter
来进行工夫格式化。
DateTimeFormatter
格式化的代码和 SimpleDateFormat
相似,具体实现如下:
@RequestMapping("/list")
public List<UserInfo> getList() {
// 定义工夫格式化对象
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
List<UserInfo> list = userMapper.getList();
// 循环执行工夫格式化
list.forEach(item -> {// 应用预留字段 ctime 接管 createtime 格式化的工夫 (Date->String)
item.setCtime(dateFormat.format(item.getCreatetime()));
item.setUtime(dateFormat.format(item.getUpdatetime()));
});
return list;
}
执行后果如下所示:DateTimeFormatter
和 SimpleDateFormat
在应用上的区别是 DateTimeFormatter
是用来格式化 JDK 8 提供的工夫类型得,如 LocalDateTime
,而 SimpleDateFormat
是用来格式化 Date
类型的,所以咱们须要对 UserInfoer 实体类做如下的批改:
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserInfo {
private int id;
private String username;
@JsonIgnore
private LocalDateTime createtime;
private String ctime;
@JsonIgnore
private LocalDateTime updatetime;
private String utime;
}
咱们能够应用 LocalDateTime
来接管 MySQL 中的 datetime
类型。
4. 全局工夫格式化
以上两种后端格式化的实现都有一个致命的毛病,它们在进行工夫格式化的时候,都须要对外围业务类做肯定的批改 ,这就相当为了解决一个问题,又引入了一个新的问题,那有没有简略一点、优雅一点的解决方案呢?
答案是:有的。咱们能够不改任何代码,只须要在配置文件中设置一下就能够实现工夫格式化的性能了。
首先,咱们找到 Spring Boot 的配置文件 application.properties(或 application.yml),只须要在 application.properties 配置文件中增加以下两行配置:
# 格式化全局工夫字段
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
# 指定工夫区域类型
spring.jackson.time-zone=GMT+8
这样设置之后,咱们将原始的 UserInfo 和 UserController 进行还原。
UserInfo 实现代码如下:
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private int id;
private String username;
private Date createtime;
private Date updatetime;
}
UserController 实现代码:
@RequestMapping("/list")
public List<UserInfo> getList() {return userMapper.getList();
}
而后咱们运行程序,看到的执行后果如下:
从以上后果和代码能够看出,咱们只须要在程序中简略配置一下,就能够实现所有工夫字段的格式化了。
实现原理剖析
为什么在配置文件中设置一下,就能够实现所有工夫字段的格式化了呢?
# 格式化全局工夫字段
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
# 指定工夫区域类型
spring.jackson.time-zone=GMT+8
这是因为 Controller 在返回数据时,会主动调用 Spring Boot 框架中内置的 JSON 框架 Jackson,对返回的数据进行对立的 JSON 格式化解决,在解决的过程中它会判断配置文件中是否设置了“spring.jackson.date-format=yyyy-MM-dd HH:mm:ss”,如果设置了,那么 Jackson 框架在对工夫类型的字段输入时就会执行工夫格式化的解决,这样咱们就通过配置来实现全局工夫字段的格式化性能了。
为什么要指定工夫区域类型“spring.jackson.time-zone=GMT+8”呢?
最事实的起因是,如果咱们不指定工夫区域类型,那么查问进去的工夫就会比预期的工夫少 8 个小时,这因为咱们(中国)所处的工夫区域比世界工夫少 8 个小时导致的,而当咱们设置了时区之后,咱们的工夫查问才会和预期工夫保持一致。
GMT 是什么?
工夫区域设置中的“GMT”是什么意思?
Greenwich Mean Time (GMT) 格林尼治工夫 ,也叫做世界工夫。
格林尼治工夫
格林尼治是英国伦敦南郊原皇家格林尼治天文台所在地,地球本初子午线的标界处,世界计算工夫和经度的终点。以其海事历史、作为本初子午线的规范点、以及格林尼治工夫以其命名而闻名于世。这里地势险要,风景秀丽,兼具历史和中央风情,也是伦敦在泰晤士河的西方门户。
不光是天文学家应用格林尼治工夫,就是在新闻报刊上也经常出现这个名词。咱们晓得各地都有各地的中央工夫。如果对国内上某一重大事件,用中央工夫来记录,就会感到简单不便.而且未来日子一长容易搞错。因而,天文学家就提出一个大家都能承受且又不便的记录办法,那就是以格林尼治的中央工夫为规范。
以本初子午线的平子夜起算的平太阳时。又称格林尼治平时或格林尼治工夫。各地的中央平时与世界时之差等于该地的地理经度。1960 年以前曾作为根本工夫计量零碎被广泛应用。因为地球自转速率曾被认为是平均的, 因而在 1960 年以前, 世界时被认为是一种平均时。因为地球自转速度变动的影响,它不是一种平均的工夫零碎,它与原子时或力学时都没有任何实践上的关系, 只有通过观测能力对它们进行比拟。起初世界时先后被历书时和原子时所取代,但在日常生活、地理导航、大地测量和宇宙航行等方面仍属必须;同时,世界时反映地球自转速率的变动,是地球自转参数之一,仍为天文学和地球物理学的根本材料。
5. 局部工夫格式化
某些场景下,咱们不须要对全局的工夫都进行对立的解决,这种状况咱们能够应用注解的形式来实现局部工夫字段的格式化。
咱们须要在实体类 UserInfo 中增加 @JsonFormat
注解,这样就能够实现工夫的格式化性能了,实现代码如下:
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private int id;
private String username;
// 对 createtime 字段进行格式化解决
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss", timezone = "GMT+8")
private Date createtime;
private Date updatetime;
}
批改完代码之后,咱们运行我的项目执行后果如下:
从上述后果能够看出,应用注解的形式也能够实现工夫的格式化。它的实现原理和第 4 种工夫格式化的实现原理相似,都是在返回数据之前,对相应的字段进行工夫格式化的解决。
总结
本文咱们介绍了 5 种工夫格式化的实现办法,其中第 1 种为前端工夫格式化的办法,后 4 种为后端格式化的办法,SimpleDateFormat
和 DateTimeFormatter
格式化的办法更实用一般的 Java 我的项目,其中 SimpleDateFormat
是非线程平安的,而 DateTimeFormatter
是线程平安的,但它们都不是 Spring Boot 我的项目中最优的工夫格式化计划。
如果是 Spring Boot 的我的项目,举荐应用第 4 种全局工夫格式化或第 5 种部分工夫格式化的形式,这两种实现形式都无需批改外围业务代码,只须要简略的配置一下,就能够实现工夫的格式化性能了。
参考 & 鸣谢
www.jianshu.com/p/49fb78bca621
baike.baidu.com/item/ 世界时 /692237
关注公号「Java 中文社群」查看更多有意思、涨常识的 SpringBoot 文章。