SpringBoot时间格式化的5种方法!

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

在我们日常工作中,时间格式化是一件经常遇到的事儿,所以本文我们就来盘点一下 Spring Boot 中时间格式化的几种方法。

时间问题演示

为了方便演示,我写了一个简单 Spring Boot 项目,其中数据库中包含了一张 userinfo 表,它的组成结构和数据信息如下:

项目目录是这样的:

UserController 实现代码如下:

@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate 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;@JsonIgnoreprivate LocalDateTime createtime;private String ctime;@JsonIgnoreprivate 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 中创建对象的 5 种方法!


SpringBoot 优雅的参数效验!


面霸篇:MQ 的 5 大关键问题详解



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544669.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#文件加密和解密

下载 CSDN下载&#xff1a;https://download.csdn.net/download/myinc/9913318 Github&#xff1a;GitHub 如果没有积分&#xff0c;也可以关注我获取哟~【文件加密】 // * 最近看了一下加密算法&#xff0c;对加密文件突然很感兴趣&#xff0c;就研究了一下&#xff1a;…

SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!

大家好&#xff0c;我是磊哥。今天我们来聊一聊在基于SpringBoot前后端分离开发模式下&#xff0c;如何友好的返回统一的标准格式以及如何优雅的处理全局异常。首先我们来看看为什么要返回统一的标准格式&#xff1f;为什么要对SpringBoot返回统一的标准格式在默认情况下&#…

zabbix企业应用之监控docker容器资源情况

关于docker的监控&#xff0c;无论开源的CAdvisor、Data Dog还是我自己写的监控&#xff08;http://dl528888.blog.51cto.com/2382721/1635951&#xff09;&#xff0c;不是通过docker的stats api就是使用socket来进行。单独看一个主机的监控项还行&#xff0c;比如只查看容器t…

使用了synchronized,竟然还有线程安全问题!

线程安全问题一直是系统亘古不变的痛点。这不&#xff0c;最近在项目中发了一个错误使用线程同步的案例。表面上看已经使用了同步机制&#xff0c;一切岁月静好&#xff0c;但实际上线程同步却毫无作用。关于线程安全的问题&#xff0c;基本上就是在挖坑与填坑之间博弈&#xf…

序列图| 软件工程

什么是时序图&#xff1f; (What is Sequence Diagram?) Sequence Diagram is a "Connection Diagram" that represents a single structure or storyline executing in a system. It is the second most used UML diagram behind the class diagram. Sequence Diag…

终极解密输入网址按回车到底发生了什么?

详解输入网址点击回车&#xff0c;后台到底发生了什么。透析 HTTP 协议与 TCP 连接之间的千丝万缕的关系。掌握为何是三次握手四次挥手&#xff1f;time_wait 存在的意义是什么&#xff1f;全面图解重点问题&#xff0c;再也不用担心面试问这个问题。大致流程URL 解析&#xff…

unity, 相机空间 与 相机gameObject的局部空间

在unity里 相机空间 与 相机gameObject的局部空间 不重合。 Camera.worldToCameraMatrix的文档中有这样一句话&#xff1a; Note that camera space matches OpenGL convention: cameras forward is the negative Z axis. This is different from Unitys convention, where for…

Winform实现漂亮动画-小火车

一、起因 最近在做一个Winform的项目&#xff0c;其中需要一些加载动画&#xff0c;所以就搜索了一下找些思路&#xff0c;以下链接是本文的参考。 参考&#xff1a;Jeremie Martinez &#xff08;译文链接&#xff09; 注&#xff1a;原文中并没有给出图片资源&#xff0c;图…

synchronized 加锁 this 和 class 的区别!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;synchronized 是 Java 语言中处理并发问题的一种常用手段&#xff0c;它也被我们亲切的称之为“Java 内置锁”&#xff0c;由…

C# WinForm窗体四周阴影效果

一、起因 关于winform窗体无边框的问题很简单&#xff0c;只需要设置winform的窗体属性即可&#xff1a; FormBorderStyle FormBorderStyle.None; 但是这中无边框窗口实现的效果和背景完全没有层次的感觉&#xff0c;所以能加上阴影&#xff0c;突出窗口显示的感觉。 二、…

synchronized 优化手段之锁膨胀机制!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;synchronized 在 JDK 1.5 之前性能是比较低的&#xff0c;在那时我们通常会选择使用 Lock 来替代 synchronized。然而这个情…

NTFS USN的Create和工具代码汇总

1、 因为之前把相关代码放在了GitHub上&#xff0c;后来突然有人帮忙改了些个BUG&#xff0c;非常感谢 760193107&#xff0c;所以就写了个完整点的例子&#xff0c;希望对别人有所帮助。 GitHub项目地址 2、错误码&#xff1a;ERROR_JOURNAL_NOT_ACTIVE 在测试时&#xff…

在Java中,负数的绝对值不一定是正数!

作者 l Hollis来源 l Hollis&#xff08;ID&#xff1a;hollischuang&#xff09;绝对值是指一个数在数轴上所对应点到原点的距离&#xff0c;所以&#xff0c;在数学领域&#xff0c;正数的绝对值是这个数本身&#xff0c;负数的绝对值应该是他的相反数。这几乎是每个人都知道…

自己写着玩(二)

转载于:https://www.cnblogs.com/wangmengmeng/p/4572611.html

实战:隐藏SpringBoot中的私密数据!

这几天公司在排查内部数据账号泄漏&#xff0c;原因是发现某些实习生小可爱居然连带着账号、密码将源码私传到GitHub上&#xff0c;导致核心数据外漏&#xff0c;孩子还是没挨过社会毒打&#xff0c;这种事的后果可大可小。说起这个我是比较有感触的&#xff0c;之前我TM被删库…

JS的条形码和二维码生成

一、前言 最近做项目用到了JS生成条形码和二维码&#xff0c;内容不多&#xff0c;整理一下方便使用。 2018年7月5日更新&#xff1a; 二维码生成时&#xff0c;如果长度太长会有异常&#xff1a; Uncaught Error: code length overflow. (1604>1056) 创建的时候&#…

synchronized 中的 4 个优化,你知道几个?

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;synchronized 在 JDK 1.5 时性能是比较低的&#xff0c;然而在后续的版本中经过各种优化迭代&#xff0c;它的性能也得到了前…

31Exchange Server 2010跨站点部署-搬迁Exchange服务器到分支机构

16.4 将EX07和EX08搬迁到上海分支机构首先我在上海分支机机构站点下创建一个CAS阵列&#xff0c;命令如下&#xff1a;下面获取下当前域中的CAS阵列信息16.4.1搬迁CAS,HT服务器1、从广州总部NLB群集删除EX07主机2、修改EX07的IP地址为分支机构IP地址 192.168.20.27(上海分支机构…

@Autowired的这些骚操作,你都知道吗?

前言最近review别人代码的时候&#xff0c;看到了一些Autowired不一样的用法&#xff0c;觉得有些意思&#xff0c;特定花时间研究了一下&#xff0c;收获了不少东西&#xff0c;现在分享给大家。也许Autowired比你想象中更强大。1. Autowired的默认装配我们都知道在spring中Au…

C# Winform 使用二维码

关于C# Winform 程序中使用二维码的使用记录&#xff1a; 1、使用 Nuget 安装 ZXing.Net 程序包&#xff1b; 2、调用代码&#xff1a; private void button1_Click(object sender, EventArgs e) {BarcodeWriter writer new BarcodeWriter();writer.Format BarcodeFormat…