Java 8 新的日期API

前言

Java 8 中引入了新的一套日期API,相对于之前的Date或者Calendar类,这套API更好的解决了日期和时间的问题。

我们来简单看下。

正文

LocalDateTime,LocalDate,LocalTime

开始使用日期API时,最先碰到的也是这三个类。

他们的静态工厂of方法可以创建日期实例。如下代码:

1
2
3
4
5
6
7
//1. 第一部分
//创建一个日期
LocalDate date=LocalDate.of(2018,11,22);
//时分秒
LocalTime time=LocalTime.of(13,45,20);
//LocalDateTime对象
LocalDateTime dateTime=LocalDateTime.of(2018,Month.NOVEMBER,22,13,45,20);

是不是很简单。

要注意这三个类都是final的,即不可被改变的。

LocalDate 表示年月日,LocalTime表示时分秒,LocalDateTime表示年月日时分秒。

他们三个之间的转换也是比较容易的。如下:

1
2
3
4
5
6
7
8
9
10
11
//2. 第二部分
//使用localDate和localTime构造一个LocalDateTime
LocalDateTime dateTime1=LocalDateTime.of(date,time);
//使用LocalDate构造一个LocalDateTime
LocalDateTime dateTime2=date.atStartOfDay();//这一天的00:00:00
LocalDateTime dateTime3=date.atTime(LocalTime.of(12,12,12));//指定这一天的时间
//使用LocalTime构造LocalDateTime
LocalDateTime dateTime4=time.atDate(LocalDate.of(2018,11,22));//指定日期
//通过LocalDateTime获取LocalDate和LocalTime
LocalDate date1=dateTime.toLocalDate();
LocalTime time1=dateTime.toLocalTime();

对于固定的日期,我们可以获取它的时间信息,也是比较容易的。如具体年月日的数值,等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//3. 第三部分
//获取日期年份
int year=dateTime.getYear();
int year1=dateTime.get(ChronoField.YEAR);
//获取日期月份
Month month=dateTime.getMonth();
int month1=month.getValue();
int month2=dateTime.get(ChronoField.MONTH_OF_YEAR);
//获取当月第几天
int day=dateTime.getDayOfMonth();
int day1=dateTime.get(ChronoField.DAY_OF_MONTH);
//获取星期几
DayOfWeek dow=dateTime.getDayOfWeek();
//获取该月有几天
int len=date.lengthOfMonth();
//获取小时数
int hour=dateTime.getHour();
//获取分钟
int minute=dateTime.getMinute();
//获取秒数
int second=dateTime.getSecond();

他们还有一些常用的方法,如时间和字符串时间之间的转化、判断闰年、获取当前时间信息、时间的比较、时间的加减天数(年数等)等方法。

相比于我们单独封装处理Date,或者Calendar类,更简便和安全了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//4. 第四部分
//是不是闰年
boolean leap=date.isLeapYear();
//获取当前时间信息
LocalDateTime localDateTimeNow=LocalDateTime.now();
LocalDate localDateNow=LocalDate.now();
LocalTime localTimeNow=LocalTime.now();
//字符串转时间
LocalDate localDate1=LocalDate.parse("2014-03-18");
LocalTime localTime1=LocalTime.parse("13:45:20");
LocalDateTime localDateTime1=LocalDateTime.parse("2018/11/22 11:22:33", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));
//时间转换为字符串
String localDateStr=localDate1.toString();
String localTimeStr=localTime1.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
String localDateTimeStr=localDateTime1.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//当前时间减10天
LocalDateTime localDateTime2=localDateTime1.minusDays(10);
//当前时间加1年
LocalDateTime localDateTime3=localDateTime1.plusYears(1);
//当前时间加1个月
LocalDateTime localDateTime4=localDateTime1.minus(-1, ChronoUnit.MONTHS);
//当前时间加1个月
LocalDateTime localDateTime5=localDateTime1.plus(1, ChronoUnit.MONTHS);
//更改日期时间,返回新的对象,原对象不会变化
LocalDate localDate=date1.with(ChronoField.MONTH_OF_YEAR,9);
//日期时间比较
boolean flag=localDateTime2.isAfter(localDateTime3);
boolean flag1=localDateTime2.isBefore(localDateTime3);
boolean flag2=localDateTime2.isEqual(localDateTime3);

这里面对于时间的加减(plus,minus方法),赋值(with方法)都会生成新的LocalDateTime对象,不会对原来的对象做修改。

Period、Duration 类

这两个类都可以表示日期时间的差值,Period的话表示年月日,如两个时间差1年或者-1个月,Duration 的话用来表示天时分秒,比如它可以表示两个时间差相差34.5s这样的数据。

1
2
3
4
5
6
7
8
9
//5. 第五部分
//计算两个时间差
Duration d1=Duration.between(localDateTime2,localDateTime3);
long days1=d1.toDays();
Duration d2=Duration.between(localDateTime4,localDateTime5);
long hours=d2.toHours();
//计算相差时间,结果10天
Period period=Period.between(LocalDate.of(2014,3,8),LocalDate.of(2014,3,18));
int days2=period.getDays();
Instant和 ZoneId

从计算机的角度来看,建模时间最自然的格式是表示一个持续时间段上某个点的单一大整型数。这也是新的 java.time.Instant 类对时间建模的方式,基本上它是以Unix元年时间(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的秒数进行计算。
而每个ZoneId由该地区的ID标识。地区ID都为“{区域}/{城市}”的格式,这些地区集合的设定都由英特网编号分配机构(IANA)的时区数据库提供。

看下它们的用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//6. 第六部分
//机器时间
//以下均表示3s时间
Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3,0);
Instant.ofEpochSecond(2,1000000);
Instant.ofEpochSecond(4,-1000000);
//当前时间的时间戳
Instant.now();
//localDateTime转换为instant
Instant instantFormDateTime=localDateTime1.toInstant(ZoneOffset.UTC);
//获取本地区的zoneId
ZoneId romeZone= TimeZone.getDefault().toZoneId();
//instant转localDateTime
Instant instant1=Instant.now();
LocalDateTime timeFromInstant=LocalDateTime.ofInstant(instant1,romeZone);
//将时区设置为欧洲罗马城市。
ZoneId romeZone1 = ZoneId.of("Europe/Rome");

一般传统的Date和LocalDate之间的转换会用到它们(Instant,ZoneId)。

其他

在一些项目中,应用Date还是很多的。我们想使用新的日期API,又不太想改动源代码。
可以写一些转换的工具类,或者使用Java8的一些日期API对Date进行处理。

如Date转化为LocalDateTime,LocalDateTime转换为Date等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Java8 LocalDateTime对象转Date对象
* @param localDateTime
* @return
*/
public static Date localDateTime2Date(LocalDateTime localDateTime){
//获取zoneId
ZoneId zone = ZoneId.systemDefault();
//将localDateTime转换为Instant对象
Instant instant = localDateTime.atZone(zone).toInstant();
return Date.from(instant);
}

/**
* Date对象 转 Java8 LocalDateTime对象
* @param date
* @return
*/
public static LocalDateTime date2LocalDateTime(Date date){
//根据date拿到Instant
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
//转换为LocalDateTime
return LocalDateTime.ofInstant(instant, zone);
}

将字符串日期格式化为LocalDateTime,或者将LocalDateTime转换为字符串时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 将日期格式化为指定的格式
* @param date
* @return
*/
public static String localDateTime2String(Date date){
LocalDateTime localDateTime=date2LocalDateTime(date);
return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(localDateTime);
}

/**
* 将string时间格式转化为LocalDateTime
* @param string
* @return
*/
public static LocalDateTime string2LocalDateTime(String string){
DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(string,df);
}

有了这些比较基础的方法,我们某些日期便可以使用Java8处理。比如项目使用Date,在判断闰年,或者时间的加减等,可以封装Java8工具类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 获取两个日期的时间间隔
* @param date1
* @param date2
* @return
*/
public static long diffDays(Date date1,Date date2){
LocalDateTime localDateTime1=date2LocalDateTime(date1);
LocalDateTime localDateTime2=date2LocalDateTime(date2);
return Duration.between(localDateTime1,localDateTime2).toDays();
}
/**
* 当期日期增加多少天后的日期,负数为减少多少天
* @param date
* @param days
* @return
*/
public static Date addDays(Date date,int days){
LocalDateTime localDateTime1=date2LocalDateTime(date);
LocalDateTime localDateTime2=localDateTime1.minusDays(days);
return localDateTime2Date(localDateTime2);
}
/**
* 判断是否是闰年
* @param date
* @return
*/
public static boolean isLeapYear(Date date){
LocalDateTime localDateTime=date2LocalDateTime(date);
return localDateTime.toLocalDate().isLeapYear();
}

这样对于Date的处理,均使用LocalDateTime处理,虽然整个项目把Date改造成LocalDateTime较困难,但是工具类相当于黑匣子,这样慢慢使用LocalDateTime去处理Date,也是蛮不错的一次体验。

总结

Java8 新的日期API比较优秀的地方是更直观了,使用更简洁。而且不用担心变量污染问题,想想一般的Date,对日期进行操作,如果不小心没有创建新对象,会把传入的Date改变掉,是很不安全的。

而且它也没有时间从1900年起那种莫名其妙的限制,而且获取到的月份值是1-12,不是0-11,也是符合自然的。

其实Java 8 的日期API不单单完成了对于Java日期的优化,其更多的方法更像是一种工具API,如判断是不是闰年,求两日期之差等等常用方法,都被封装在了Java8的新的日期API里面了。




-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道