关于java.time这个包,应该来说已经有不少的教程和文章了。不过最近处理了很多日期相关的问题,修改了项目中的时间日期工具类,之后又将整个工具类用新的time包重写。总结一下发布在此,展示一些常规操作。
总体简介
真要好好了解的话还是应该去看整个包的javadoc。不过这里还是要从为何要增加这个新的日期-时间API来说起。java对于时间的处理最早伊始于java.util.Date类,然后是jdk1.1开始使用的java.util.Calendar类,最后直到现在的java.time。
更换到Calendar类(以及DateFormat类)的主要原因是Date类包含了日期信息的存储、转换、显示,承载的职责过于复杂。更换到time包的主要原因是日期设置不够友好、时区支持不够好而且属性均可变导致线程不安全。这些也间接说明了新的time API的几个优秀特质:日期时间分离、各个类职责明确、线程安全、完善的时区支持等。
java.time包大量吸收了优秀的第三方库Joda-Time的内容,因此相关操作和Joda-Time非常相似。
基本类和接口
Instant
时间线上的瞬时点,可以通俗地认为就是时间戳,底层是对System.currentTimeMillis()的封装,主要由自1970-01-01零点为基准的以long表示的秒数和以int表示的时刻纳秒数(0 - 999,999,999)两部分组成。在与Date类进行转换是时经常需要用到。
LocalDate
表示不带时间的本地日期,time包将日期与时间分离,使得很多操作更加简便。
LocalTime
表示不带日期的本地时间。
LocalDateTime
表示本地日期时间,本质上由LocalDate和LocalTime组成,使用频率会比较高。
ChronoUnit
这是个枚举类,代表了标准的日期-时间单位。包含了纳秒,秒,分钟,周,年,世纪等一共16个类型。相关的标准接口为TemporalUnit。常用于获取日期和时间的某个属性。
ChronoField
同样是枚举类,代表了标准的用于操作日期和时间的领域(不知道怎么翻译更好)。包含了分钟的秒、小时的分钟、一天的小时等等,虽然翻译地不太好,但是原有的Calendar类也是有相同概念的内容的,只要看到了应该不难理解。相关的标准接口为TemporalField。常用于日期的计算、变换。
Duration
代表了时间的间隔,持有的内容精确到纳秒,常用于计算时间差。
Period
代表了日期的间隔,以年月日来表示。也可以使用相关LocalDate类中的until方法来达到相同的计算效果。
时区、位移相关
虽然时区处理是time包的优势,但这部分用到的不多,我本人也不是特别熟,就简单罗列一下。时区相关主要有ZonedDateTime、ZoneId、Clock等,位移相关主要有OffsetTime、OffsetDateTime、ZoneOffset等。
时间格式化处理类,用于时间的格式化和解析,这个类本身带有大量的静态标准值提供快速使用。
TemporalAdjuster
首先这是一个接口,表示时间的调整、计算,下面的示例有很大一部分与它有关,使用时可以配合lambda进行更加简洁优雅的表示。包内提供了一个标准的实现类TemporalAjusters,直接给予了许多通用的时间变换方法,尤其是针对星期这一方面。
使用示例
下面分类展示一些日期处理的典型代码。
与java.util.Date的转换
1 2 3 4 5 6 7 8 9 10
| LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalTime localTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
Date date = Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
时间日期的构造
1 2 3 4 5 6 7 8 9 10 11
| Instant now = Instant.now(); LocalDate currentDate = LocalDate.now(); LocalTime currentTime = LocalTime.now(); LocalDateTime currentDateTime = LocalDateTime.now();
LocalDate date = LocalDate.of(year, month, dayOfMonth); LocalTime time = LocalTime.of(hour, minute, second, nanoOfSecond); LocalDateTime localDateTime = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond);
LocalDateTime localDateTime = LocalDateTime.parse("2007-12-03T10:15:30");
|
获取相关属性
1 2 3 4 5 6 7 8 9 10 11 12
| localDate.getYear(); localDate.getMonthValue(); localDate.getDayOfMonth();
localTime.getHour(); localTime.getMinute(); localTime.getSecond();
localDate.isLeapYear();
localDate.range(DAY_OF_MONTH).getMaximum();
|
日期时间调整运算
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 32 33
| localDate.plusDays(days); localDate.plus(days, ChronoUnit.DAYS);
localTime.plusMinutes(minutes); localTime.plus(minutes, ChronoUnit.MINUTES);
localDate.with(temporal -> temporal .plus(years, ChronoUnit.YEARS) .plus(months, ChronoUnit.MONTHS) .plus(days, ChronoUnit.DAYS));
localDateTime.with(temporal -> temporal .with(ChronoField.MINUTE_OF_HOUR,0) .with(ChronoField.SECOND_OF_MINUTE,0) .with(ChronoField.MILLI_OF_SECOND,0));
localDateTime.with(temporal -> temporal .plus(Duration.ofHours(1)) .with(ChronoField.MINUTE_OF_HOUR,0) .with(ChronoField.SECOND_OF_MINUTE,0) .with(ChronoField.MILLI_OF_SECOND,0) .minus(Duration.ofMillis(1)));
localDate.with(TemporalAdjusters.firstDayOfMonth());
localDate.with(TemporalAdjusters.firstDayOfNextMonth());
localDate.with(temporal -> { Temporal tmp = temporal.minus(1, MONTHS); long maxdays = tmp.range(DAY_OF_MONTH).getMaximum(); return tmp.with(DAY_OF_MONTH, maxdays); });
|
日期时间比较、间隔
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| localDate.equals(other); localTime.equals(other); localDate.isEqual(other)
localDate.isBefore(other) localDate.isAfter(other)
Duration duration = Duration.between(localDateTime, other); duration.getSeconds(); duration.getNano();
duration.toNanos(); duration.toHours(); duration.toDays();
Period period = Period.between(LocalDate, other); period.getYears(); period.getMonths(); period.getDays();
period.toTotalMonths();
|
日期格式化
1 2 3 4 5
| DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(localDateTime);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); String strDate = formatter.format(localDateTime);
|
小结
经过这几个常用的简短示例,相信可以快速建立对新的time API的理解。对于这个时间包,最重要的就是理解上述的基本类和接口,所有的操作都围绕这些展开。
时区相关的内容本篇暂不讨论,但是时区是一个比较容易出问题的地方,生产环境一定要注意系统的时区设置,防止发生错误。此外,对于星期相关的处理可以参考TemporalAdjusters提供的相关实现。