最近开发过程中遇到了很多时间类处理,由于对Calender类不熟悉,我说这个类设计的烂,谁赞成,谁反对?也被推荐过joda-time类库,鉴于项目用的都是java 8了,是时候了解一下java.time包下的类了
导言
java 8 日期类的优势
用完java 8的API之后,只有一个感觉:爽,没有啰嗦的方法,很多静态工厂方法,of,from见名知意,用到后面,一些api自己都可以猜出来了,同时api更人性化,例如对比之前的获取月份方法,是从0开始,机械化的思维,反观java 8,我能通过getMonthValue直接获取,无须+1
对于joda-time,从个人角度讲,实在不想再去记忆一套api,同时从项目角度来说,我能用jdk实现的,为什么要依赖第三方jar包,这点对于实际开发来说更重要。但对于还在使用java 8以下版本的同学joda-time还是值得推荐的
API的记忆方法
在Effective java读书笔记一文中,静态方法相较构造方法有更多的优势,尤其是在提供给开发者使用时。java 8这方面做得很好,of,from,parse,format,minus,plus等等都是见名知意的方法
时间分类
java 8提供了3个基础时间类:LocalDate, LocalDateTime, LocalTime,分别代表日期,日期+时间,时间(时分秒)
同时三者之间可以部分转换,之所以称之为部分,很简单的例子是日期无法直接转换为具体的日期+时间,因为它缺少时分秒,这可以理解为一种精度损失,当然你可以通过默认值来补全
Instant表示瞬时时间,精确到毫秒,可用于记录时间戳
java 8支持通过时区id,时区偏移量来获取时间
在实际开发中我们关注的有以下几个方面的时间:
- 时间戳,既有毫秒,也有秒,秒主要是PHP等服务返回的标准时间戳
- Date,不要忘了数据库对应的实体中,使用的时间对象仍是Date
- 待格式化的字符串,很常见的需求,将字符串解析为时间,或是将时间格式化为文本
这几个方面时间的互相转换也需要关注
API
获取时间
now
now方法用于获取日期类的当前值,例如LocalDate获取当前日期,LocalTime获取当前时间的时分秒等信息
获取年月日
根据常识,仅LocalDate, LocalDateTime可以获取年月日,分别由getYear,getMonthValue,getDayOfMonth获取,时分秒的获取方式同理LocalDateTime
获取自1970-01-01T00:00:00的毫秒数,秒数,天数
不用死记硬背,只要想清楚这几个类分别代表了什么类型的时间即可推断出api1
2
3Instant.now().toEpochMilli();
LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));
LocalDate.now().toEpochDay();
ZoneOffset.ofHours(8)可以理解为北京时间位于东八区
获取前一天,一个月,一年等等
记住两个单词即可,minus表示减,plus表示加
至于你想加减些什么,首先要确定要加减的单位是什么,比如分钟,那肯定是在LocalDateTime,LocalTime里找,加年,加月同理,剩下的api就不啰嗦了,授人以鱼不如授人以渔,读者有兴趣自己探索。
时间转换
文本与时间之间的相互转换
parse用于处理从文本到日期的转换,根据DateTimeFormatter的格式解析成日期1
LocalDate date = LocalDate.parse("2019-01-11", DateTimeFormatter.ofPattern("yyyy-MM-dd"))
yyyy-MM-dd是默认的格式,可以省略第二个参数,类似的HH:mm:ss在转换为时分秒时也可以省略
将日期格式化为文本1
String text = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
时间戳与时间之间的转换
注意两点:
Instant用于表示瞬时值,它和秒,毫秒是相关联的,再将Instant转换为LocalDateTime
1
2
3long time = 1548154964271L;
Instant instant = Instant.ofEpochMilli(time);
LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());需要判断时间戳是毫秒还是秒,PHP等语言只能精确到秒,请求这类接口时需注意
给出一个转换方法示例1
2
3
4
5
6
7
8
9
10
11
12
13public static final long MAX_TIME_STAMP = 10000_000_000L;
public static final long MIN_TIME_STAMP = 1000_000_000L;
private static Instant transToInstant(long time) {
if (time < MAX_TIME_STAMP && time > MIN_TIME_STAMP) {
return Instant.ofEpochSecond(time);
}
if (time < MAX_TIME_STAMP * 1000 && time > MIN_TIME_STAMP * 1000){
return Instant.ofEpochMilli(time);
}
throw new IllegalArgumentException("illegal time value");
}
与Date类的转换
记住一点,旧版的Date与java 8中的日期类的转换桥梁是Instant1
2
3Date date = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
LocalDateTime dateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
获取时间段
Period和ChronoUnit都可以做到计算时间段,但又有区别,下面以计算两个时间时间的天数为例1
2Period period = Period.between(LocalDate.of(2017, 1, 19), LocalDate.of(2019, 5, 10));
long days = ChronoUnit.DAYS.between(LocalDate.of(2017, 1, 19), LocalDate.of(2019, 5, 10));
Period返回的是两个日期之间相差几年几月几日,以上面两个日期为例,以下三个方法分别返回: 2年, 3个月, 21天
period.getYears(),period.getMonths(),period.getDays()
ChronoUnit则计算出了两个日期直接具体的天数,结果为841天
与周相关的几个API
1 | LocalDate now = LocalDate.now(); |
打印输出为:1
2
3
4dayOfWeek = TUESDAY
nextMonday = 2019-05-06
lastDayofMonth = 2019-04-30
weekth = 5
以前是笔者开发中遇到的api,还有很多有用的api未介绍,大家可以用到时查阅文档
结束语
java 8时间类的使用写到这里告一段落,关于时区的使用,也见缝插针的介绍了下,写这篇文章对笔者最大的挑战是表达能力,api很多,我想表达的是有规律的使用api,而不是死记硬背,最后分享一个自己写的DateUtils
DateUtils
1 | /** |