摘要
Java从1.0版本开始,就提供了时间操作的接口,主要围绕java.util.Date和java.util.Calendar接口进行了定义和封装。但两者在易用性和稳定性方面一直被java开发者诟病。从其根本原因来说,这两个接口在设计的最初就没有支撑全面的日期和时间模型。鉴于已知的问题,JDK8提供了新的日期、时间模型。本文就从新旧接口的利弊点进行讨论,并给出了新接口的常用场景。
01
JDK早期版本的时间接口
1.1 早期版本存在的问题
Date类定义模糊,提供的功能让人模糊:
v Date从字面意思来说是日期的意思,但它却包含了小时,分钟和秒的信息。
v java.util.Date和java.sql.Date重名。
v Date类是可变的,可能被更改内容。
时间操作和时区操作没有分开:
v Date包含时区的信息,但大部分情况下我们并不需要时区信息。时区操作非常棘手,通常导致错误的结果。
v Date选择默认时区。
接口提供的功能存在缺陷:
v 日期格式化SimpleDateFormat并不是线程安全的。
v 月份从0开始索引,让人费解。
1.2 时间接口列表
![d32c044bfaf69f6a2e4d2bcb82e03816.png](https://i-blog.csdnimg.cn/blog_migrate/4a5b63d41a1e06cf3678364bae79253f.png)
02
新的时间接口
为了解决老版本的问题,JDK8提供了新的时间操作接口(如下表)。接口将时间、日期的功能分开,并将时区的操作和时间操作分开。
![47124f32236eb34ee807eda1991ea7c4.png](https://i-blog.csdnimg.cn/blog_migrate/447f7dc6f07e0c8d11065b5518232869.png)
03
实用场景
3.1 新的时间接口与旧接口的相互转换
将LocalDate转成Date,时间里面没有时分秒的信息:
public static Date asDate(LocalDate localDate) {
return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
}
LocalDateTime包含日期和时间信息,但是没有时区。转换的时候需要将设置时区:
public static Date asDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}
Date转换成LocalDate,丢失了时区,时分秒的信息:
public static LocalDate asLocalDate(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
}
Date转换成LocalDateTime,丢失了时区信息。
public static LocalDateTime asLocalDateTime(Date date) {
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
}
3.2 新接口的时间格式化
时间格式pattern可以自定义,例如"yyyy-MM-dd HH:mm:ss"。
其中yyyy代表年,MM代表月份,dd代表这个月的第几天,HH代表24进制的小时,mm代表分,ss代表秒。
public static LocalDateTime parseLocalDateTime(String dateTimeStr, String pattern) {
return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern(pattern));
}
public static String formatLocalDateTime(LocalDateTime datetime, String pattern) {
return datetime.format(DateTimeFormatter.ofPattern(pattern));
}
3.3 新接口的时间间隔
通过时间接口,可以灵活的操作日期。例如可以计算昨天的日期是多少,N天前、N天后倒入日期。
// 获取当前日期
LocalDateTime now = LocalDateTime.now();
// 获取昨天的日期
LocalDateTime yesterday = now.plusDays(-1);
// 向前数300天
LocalDateTime minusDays = now .minusDays(300);
// 10天之后
LocalDateTime plusDays = date.plusDays(10);
// 去年的今天
LocalDateTime minusDays = date.minusYears(1);
// 两个月前的今天
LocalDateTime minusDays = date.minusMonths(2);
也可以轻松的计算两个日期之间的时间间隔。
// 计算两个日期相隔的天数
long betweenDays = ChronoUnit.DAYS.between(nowDate, localDate);
// 计算两个时间间隔的秒数
Duration.between(startDate, endDate).get(ChronoUnit.SECONDS);
ChronoUnit.SECONDS.between(startDate, endDate);
04
总结
由于篇幅有限,以上只列举了一些常用的操作方式。
JDK新的时间操作接口,一方面给我们提供了更加便利、清晰和安全的操作支持,另一方面也给了我们设计上的提示。模块设计首先要保证接口的单一性原则,不要包含过多的功能,提高内聚性。其次基础模型要保证线程安全,防止多线程引起的未知错误。