作者|王磊来源|Java中文社区(ID:javacn666)转载请联系授权(微信ID:GG_Stone)。在我们的日常工作中,时间格式化是很常见的事情,那么在这篇文章中我们就来看看SpringBoot中时间格式化的几种方法。时间问题的演示为了演示方便,我写了一个简单的SpringBoot工程,里面包含了数据库中的userinfo表。其组成结构和数据信息如下:项目目录如下:UserController实现代码如下:@RestController@RequestMapping("/user")publicclassUserController{@ResourceprivateUserMapperuserMapper;@RequestMapping("/list")publicListgetList(){returnuserMapper.getList();}}UserMapper实现代码如下:@MapperpublicinterfaceUserMapper{publicListgetList();}UserInfo实现代码如下:@DatapublicclassUserInfo{privateintid;私有字符串用户名;私人日期创建时间;privateDateupdatetime;}UserMapper.xml实现代码如下:select*fromuserinfo通过上面的编译写到这里,我们就创建了一个简单的SpringBoot项目。接下来我们使用PostMan模拟调用UserController接口。执行结果如下:从上面的结果我们可以看出,时间字段createtime和updatetime的显示方式很“乱”,不符合我们的阅读习惯,不能直接显示到前台——最终用户,这时候我们就需要对时间进行格式化,时间格式化一共有5种方法1.前端时间格式化如果后端在公司有绝对的话语权,或者后台-end比较强,我们可以强行把时间格式化的“锅”丢给前端处理。为了让这个“锅”倒的更顺畅(可惜雷哥不当厨师),我们可以为前端工程师提供一个可行的时间格式化方法,实现代码如下。JS版本时间格式化函数dateFormat(fmt,date){letret;constopt={"Y+":date.getFullYear().toString(),//年"m+":(date.getMonth()+1).toString(),//月"d+":日期。getDate().toString(),//日“H+”:date.getHours().toString(),//小时“M+”:date.getMinutes().toString(),//分“S+”:日期.getSeconds().toString()//秒数//可以添加其他格式化字符,必须转成字符串};for(letkinopt){ret=newRegExp("("+k+")").exec(fmt);if(ret){fmt=fmt.replace(ret[1],(ret[1].length==1)?(opt[k]):(opt[k].padStart(ret[1].length,“0”)))};};returnfmt;}方法调用:letdate=newDate();dateFormat("YYYY-mm-ddHH:MM:SS",date);>>>2021-07-2521:45:122,SimpleDateFormat格式化在大多数情况下,我们还是要自力更生,各扫门,这个时候我们后端程序员需要发挥自己的特长。我们提供的第一个时间格式化方法是使用SimpleDateFormat进行时间格式化。也是JDK8之前重要的时间格式化方法,其核心实现代码如下://定义时间格式化对象,定义格式化样式SimpleDateFormatdateFormat=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");//格式化时间对象Stringdate=dateFormat.format(newDate())接下来我们在这个项目中使用SimpleDateFormat来实现时间格式化,其实现代码如下:@RequestMapping("/list")publicListgetList(){//定义时间格式化对象SimpleDateFormatdateFormat=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");Listlist=userMapper.getList();//循环执行时间格式化list.forEach(item->{//使用保留字段ctime接收createtime的格式化时间(Date->String)item.setCtime(dateFormat.format(item.getCreatetime()));item.setUtime(dateFormat.format(item.getUpdatetime()));});returnlist;}程序执行结果如下:从上面的结果可以看出,时间格式化没有问题,达到了我们的预期目的,但是细心的读者会发现为什么接口的return字段已经改变?(之前的字段是createtime,现在是ctime。。。)这是因为使用#SimpleDateFormat.format方法后,返回的是String类型的结果,而我们之前的createtime和updatetime字段是Date类型的,因此他们无法收到时间格式的结果。所以这时候我们需要在实体类UserInfo中添加两个字符串类型的“时间”字段,然后隐藏之前的Data类型的时间字段。实体类UserInfo最终的实现代码如下:私有字符串用户名;@JsonIgnore//输出结果时隐藏该字段privateDatecreatetime;//时间格式化后的字段privateStringctime;@JsonIgnore//输出结果时隐藏该字段privateDateupdatetime;//时间格式化后的字段privateStringutime;}我们可以使用@JsonIgnore注解隐藏该字段,隐藏后的执行结果如下:3、DateTimeFormatterJDK8格式化后,我们可以使用DateTimeFormatter来代替SimpleDateFormat,因为SimpleDateFormat不是线程安全的,而DateTimeFormatter是线程安全的,所以如果是JDK8以上的项目,尽量使用DateTimeFormatter进行时间格式转换。DateTimeFormatter格式化代码与SimpleDateFormat类似,具体实现如下:@RequestMapping("/list")publicListgetList(){//定义时间格式化对象DateTimeFormatterdateFormat=DateTimeFormatter.ofPattern("yyyy-MM-ddHH:mm:ss");Listlist=userMapper.getList();//循环执行时间格式list.forEach(item->{//使用保留字段ctime接收createtime(Date->String)格式化的时间item.setCtime(dateFormat.format(item.getCreatetime()));item.setUtime(dateFormat.format(item.getUpdatetime()));});returnlist;}执行结果如下:DateTimeFormatter和SimpleDateFormat的区别在于DateTimeFormatter是用来格式化JDK8提供的时间类型,比如LocalDateTime,而SimpleDateFormat是用来格式化Date类型的,所以我们需要对UserInfoer实体类做如下修改:importcom.fasterxml.jackson.annotation.JsonIgnore;importlombok.Data;importjava.time.LocalDateTime;@DatapublicclassUserInfo{私有字符串用户名;@JsonIgnoreprivateLocalDateTime创建时间;私有字符串ctime;@JsonIgnore私有LocalDateTime更新时间;privateStringutime;}我们可以使用LocalDateTime来接收MySQL中的datetime类型4.全局时间格式化以上两种后端格式化的实现都有一个致命的缺点,它们在时间格式升级的时候,需要对核心业务类进行一定的修改。这相当于为了解决一个问题引入了一个新问题。有没有更简单优雅的解决方案?答案是:是的。我们不需要改动任何代码,只需要在配置文件中设置即可实现时间格式化功能。首先,我们找到SpringBoot的配置文件application.properties(或application.yml)。我们只需要在application.properties配置文件中加入如下两行配置即可:#格式化全局时间字段spring.jackson.date-format=yyyy-MM-ddHH:mm:ss#指定时区类型spring.jackson.time-zone=GMT+8这样设置后,我们就恢复原来的UserInfo和UserController。UserInfo实现代码如下:importlombok.Data;importjava.util.Date;@DatapublicclassUserInfo{privateintid;私有字符串用户名;私人日期创建时间;privateDateupdatetime;}UserController实现代码:@RequestMapping("/list")publicListgetList(){returnuserMapper.getList();}然后我们运行程序,看到的执行结果如下:从上面的结果和代码可以看出,我们只需要在程序中简单配置一下,就可以实现所有时间字段都被格式化。实现原理分析为什么所有时间字段的格式化都可以通过在配置文件中设置来实现呢?#格式化全局时间字段spring.jackson.date-format=yyyy-MM-ddHH:mm:ss#指定时区类型spring.jackson.time-zone=GMT+8这是因为Controller返回数据时,它会自动调用SpringBoot框架中内置的JSON框架Jackson,对返回的数据进行统一的JSON格式化处理。处理过程中会判断配置文件中是否设置了“spring.jackson.date-format=yyyy-MM-ddHH:mm:ss”,如果设置了,那么Jackson框架在输出时间的时候会进行时间格式化-type字段,这样我们就可以通过配置实现全局时间字段的格式化功能。为什么要指定时区类型“spring.jackson.time-zone=GMT+8”?最现实的原因是,如果我们不指定时区类型,查询时间会比预期时间少8小时,因为我们(中国)所在的时区比世界时间少8小时,而当我们设置了时区后,我们的时间查询就会和期望的时间保持一致。什么是格林威治标准时间?“GMT”在时区设置中是什么意思?GreenwichMeanTime(GMT)格林威治标准时间,又称世界时间。格林威治标准时间格林威治标准时间是原英国伦敦南郊皇家格林威治天文台的所在地,是地球本初子午线的标示地,也是世界上计算时间和经度的起点。以其航海历史而闻名,作为本初子午线的标准点,格林威治标准时间就是以此命名的。这里地势险要,风景优美,既有历史,又有风土人情。它也是泰晤士河畔伦敦的东部门户。不仅天文学家使用格林威治标准时间,新闻中也经常使用该术语。我们知道到处都有当地时间。如果使用当地时间来记录世界上的重要事件,将会变得复杂和不便。而且以后很容易出错。因此,天文学家提出了一种方便且为大家所接受的记录方式,那就是以格林威治标准当地时间为标准。从本初子午线的平均午夜算起的平均太阳时。也称为格林威治标准时间或格林威治标准时间。正常时间和世界标准时间之间的差异等于该地点的地理经度。它在1960年之前被广泛用作基本时间测量系统。由于地球自转速度一度被认为是匀速的,所以1960年以前世界时被认为是匀速时间。由于地球自转速度变化的影响,不是匀速时间系统,与原子时没有理论关系或机械时间,它们只能通过观察来比较。后来世界时先后被历书时和原子时所取代,但在日常生活、天文导航、大地测量和航天飞行中仍然是必需的;同时,世界时反映了地球自转速率的变化,是地球自转参数之一。仍然是天文学和地球物理学的基础数据。5.部分时间格式化在某些场景下,我们不需要对全局时间进行统一处理。在这种情况下,我们可以使用注解来格式化部分时间字段。我们需要在实体类UserInfo中添加@JsonFormat注解,这样才能实现时间格式化功能。实现代码如下:importcom.fasterxml.jackson.annotation.JsonFormat;importlombok.Data;importjava.util.Date;@DatapublicclassUserInfo{privateintid;私有字符串用户名;//格式化创建时间字段@JsonFormat(pattern="yyyy-MM-ddhh:mm:ss",timezone="GMT+8")privateDatecreatetime;privateDateupdatetime;}修改代码后,我们运行项目,执行结果如下:从上面的结果可以看出,时间的格式也可以通过注解来实现。它的实现原理和第四种时间格式的实现原理类似,都是在返回数据前对相应的字段进行时间格式处理。小结在本文中,我们介绍了5种时间格式化的实现方式,第一种是前端时间格式化方式,后4种是后端格式化方式。SimpleDateFormat和DateTimeFormatter格式化方法更适合普通Java项目,其中SimpleDateFormat不是线程安全的,而DateTimeFormatter是线程安全的,但都不是SpringBoot项目中最优的时间格式化方案。如果是SpringBoot项目,建议使用第四种全局时间格式或者第五种本地时间格式方式。这两种实现方式都不需要修改核心业务代码,只需要简单的配置就可以完成时间格式化功能了。