Java8 日期时间类整理
一、概述
1、时间日期类简介
当我们开始使⽤Java操作⽇期和时间的时候,会有⼀些棘⼿。你也许会通过
System.currentTimeMillis()
来返回1970年1⽉1⽇到今天的毫秒数。或者使⽤ Date类来操作⽇期;当遇到加减⽉份、天数的时候 你⼜需要⽤到Calendar类; 当需要格式化⽇期的时候需要使⽤java.text.DateFormat
类
在 Java 8 之前,我们处理日期时间需求时,使用 Date
、Calender
和
SimpleDateFormat
,来声明时间戳、使用日历处理日期和格式化解析日期时间。但是,这
些类的 API 的缺点比较明显,比如可读性差、易用性差、使用起来冗余繁琐,还有线程安
全问题。
2、Date类型存在的问题
- 结构定义混乱
- java.util Date包含日期时间
- java.sql Date包含日期
- java.text 时间格式化
- API不易用
- 非线程安全
- 可变,SimpleDateFormate
- 国际化
Date
如果不格式化,打印出的日期可读性差- Calendar TimeZone
1 | Tue Sep 10 09:34:04 CST 2020 |
3、Calendar与Date
1 | public class DateTest { |
二、Java8新的日期时间类
1、LocalDate
只会获取年月日
- 创建
LocalDate
1 | //获取当前年月日 |
- 获取年、月、日、星期几
1 | //获取年 |
2、LocalTime
只会获取几点几分几秒
- 创建
LocalTime
1 | LocalTime localTime = LocalTime.of(13, 51, 10); |
- 获取时分秒
1 | //获取小时 |
3、LocalDateTime
获取年月日时分秒,等于LocalDate+LocalTime
- 创建
LocalDateTime
1 | LocalDateTime localDateTime = LocalDateTime.now(); |
- 获取
LocalDate
1 | LocalDate localDate2 = localDateTime.toLocalDate(); |
- 获取
LocalTime
1 | LocalTime localTime2 = localDateTime.toLocalTime(); |
4、Instant
获取秒数
- 创建
Instant
对象
1 | Instant instant = Instant.now(); |
- 获取秒数
1 | long currentSecond = instant.getEpochSecond(); |
- 获取毫秒数
1 | long currentMilli = instant.toEpochMilli(); |
如果只是为了获取秒数或者毫秒数,使用
System.currentTimeMillis()
来得更为方便
三、日期时间的修改与计算
**LocalDate
、LocalTime
、LocalDateTime
、Instant
**为不可变对象,修改这些对象对象会返回一个副本
1、时间修改
1 | LocalDateTime localDateTime = LocalDateTime.of(2020, Month.SEPTEMBER, 10, |
另外比如有些时候想知道这个月的最后一天是几号、下个周末是几号,通过提供的时间和日期API可以很快得到答案,比如通过firstDayOfYear()
返回了当前日期的第一天日期
1 | System.out.println("//本月的第一天"); |
Java 8 中有一个专门的类 Period 定义了日期间隔,通过 Period.between
得到了两个 LocalDate
的差,返回的是两个日期差几年零几月零几天。如果希望得知两个日期之间差几天,直接调用Period
的 getDays()
方法得到的只是最后的“零几天”,而不是算总的间隔天
1 | public static void main(String[] args) throws Exception { |
2、SimpleDateFormat的坑
2.1 线程不安全
定义的 static 的 SimpleDateFormat 可能会出现线程安全问题,需要每个线程单独new或者通过 ThreadLocal 来存放 SimpleDateFormat
1 | private static ThreadLocal<SimpleDateFormat> threadSafeSimpleDateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); |
2.2 解析的字符串和格式不匹配的容忍高
1 | public static void main(String[] args) throws Exception { |
3、格式化日期(常用)
1 | LocalDate localDate = LocalDate.of(2020, 10, 10); |
DateTimeFormatter
默认提供了多种格式化方式,如果默认提供的不能满足要求,可以通过DateTimeFormatter
的ofPattern
方法创建自定义格式化方式
解析时间
1 | LocalDate localDate1 = LocalDate.parse("20201010", DateTimeFormatter.BASIC_ISO_DATE); |
和SimpleDateFormat
相比,DateTimeFormatter
是线程安全的,可以定义为 static 使用,最后,DateTimeFormatter 的解析比较严格,需要解析的字符串和格式不匹配时,会直接报错。下面是静态定义的解析格式
1 | private static DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder() |
四、日期时间类时区
1、Date类
- Date 并无时区问题,世界上任何一台计算机使用 new Date() 初始化得到的时间都一样。因为,Date中保存的是 UTC 时间,UTC 是以原子钟为基础的统一时间,不以太阳参照计时,并无时区划分
- Date 中保存的是一个时间戳,代表的是从 1970 年 1 月 1 日 0 点(Epoch 时间)到现在的毫秒数。尝试输出 Date(0):
1 | /** |
2、日期时间的两种保存方式
- 以 UTC 保存,保存的时间没有时区属性,是不涉及时区时间差问题的世界统一时间。我们通常说的时间戳,或 Java 中的 Date 类就是用的这种方式(推荐)
- 以字面量保存,比如
年/月/日 时:分:秒
,一定要同时保存时区信息。只有有了时区信息,我们才能知道这个字面量时间真正的时间点,否则它只是一个给人看的时间表示,只在当前时区有意义。Calendar 是有时区概念的,所以我们通过不同的时区初始化 Calendar,得到了不同的时间
下面是两个时区错乱例子
1 | // 对于同一个时间表示,比如 2020-01-02 22:00:00,不同时区的人转换成 Date会得到不同的时间(时间戳): |
3、Java8新日期类解决时区问题
Java 8 推出了新的时间日期类 ZoneId
、ZoneOffset
、LocalDateTime
、ZonedDateTime
和 DateTimeFormatter
,处理时区问题更简单清晰
1 | private static void right() { |
五、LocalDateTime在SpringBoot中的应用
1、将LocalDateTime字段以时间戳的方式返回给前端 添加日期转化类
1 | public class LocalDateTimeConverter extends JsonSerializer<LocalDateTime> { |
并在LocalDateTime
字段上添加@JsonSerialize(using = LocalDateTimeConverter.class)
注解,如下:
1 | .class) (using = LocalDateTimeConverter |
2、将LocalDateTime字段以指定格式化日期的方式返回给前端
在LocalDateTime
字段上添加@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
注解即可,如下:
1 | "yyyy-MM-dd HH:mm:ss") (shape=JsonFormat.Shape.STRING, pattern= |
3、对前端传入的日期进行格式化
在LocalDateTime
字段上添加@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
注解即可,如下:
1 | "yyyy-MM-dd HH:mm:ss") (pattern = |
4、前后端日期时间转化问题
- 方式一
在实体类上加@DatetimeFormat
与@JsonFormat
注解
@DatetimeFormat
将前台日期字符串转换成Date格式 @DateTimeFormat(pattern="yyyy-MM-dd")
@JsonFormat
将服务器端Date日期转换成指定字符串格式 @JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8")
两个需要同时加,否则会有时区的问题
- 方式二
在applicition.properties中添加如下配置
1 | #时间戳统一转换 |
或者在application.yml中添加如下配置
1 | #时间戳统一转换 |
5、全局日期格式转换器
配置从页面接收的String和json格式的日期转换为Date类型,包括从Bean类转换,从此不再纠结用@DateTimeFormat(patten = "yyyyy-MM-dd")
还是@DateTimeFormat(patten = "yyyyy-MM-dd HH:mm")
下面是配置String类型表单传参转Date的转换器
1 | //Converter<S,T> S: 代表的是源,将要转换的数据类型 T:目标类型,将会转成什么数据类型 |
配置Json数据转Date的全局日期转换器
1 | public class GlobalJsonDateConvert extends StdDateFormat { |
最后将配置bean交给Spring管理
1 |
|
参考文章: