乐趣区

关于java:SpringBoot中时间格式化的5种方法

在咱们日常工作中,工夫格式化是一件常常遇到的事儿,所以本文咱们就来盘点一下 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 接口,执行后果如下:

从上述后果能够看出,工夫字段 createtimeupdatetime 的显示方式是很“凌乱”的,并不合乎咱们的浏览习惯,也不能间接展现给前端的用户应用,这时候,咱们就须要对工夫进行格式化解决了。

工夫格式化的办法总共蕴含以下 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 类型的后果,而咱们之前的 createtimeupdatetime 字段都是 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;
}

执行后果如下所示:

DateTimeFormatterSimpleDateFormat 在应用上的区别是 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 种为后端格式化的办法,SimpleDateFormatDateTimeFormatter 格式化的办法更实用一般的 Java 我的项目,其中 SimpleDateFormat 是非线程平安的,而 DateTimeFormatter 是线程平安的,但它们都不是 Spring Boot 我的项目中最优的工夫格式化计划。

如果是 Spring Boot 的我的项目,举荐应用第 4 种全局工夫格式化或第 5 种部分工夫格式化的形式,这两种实现形式都无需批改外围业务代码,只须要简略的配置一下,就能够实现工夫的格式化性能了。

参考 & 鸣谢

www.jianshu.com/p/49fb78bca621

baike.baidu.com/item/ 世界时 /692237

关注公号「Java 中文社群」查看更多有意思、涨常识的 SpringBoot 文章。

退出移动版