本文最后更新于:2020年12月30日 凌晨
简介
Java8 面世以来已经 6 年了,许多人也开始使用起了 lambda
,Stream<T>
,Optional<T>
之类的新的语言特性,然而对于 Java8 提供的新的时间 API
虽然据说比旧版本的 Date
好很多,但并没有得到完全的使用。一方面是为了兼容旧的系统,另一方面 Java8 的时间 API 似乎太过于强大了,让人有些不知所措,不知道应该从何下手。再加上因为对 Date
,Calendar
的熟悉,此消彼长之下自然是懒得去修改了。
其实对于时间 API,大致的需求是一样的
- 创建/修改/比较/转换简单
- 对遗留系统的时间可以集成/转换
- 主流框架对其要有支持
API
常用的类
LocalDate
一个不可变(线程安全)的日期对象,用且表示 年-月-日 的时间,默认 toString()
格式是 yyyy-MM-dd
。
基本操作
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
| final LocalDate now = LocalDate.now();
System.out.println("现在:" + now);
final LocalDate localDatePlusDayOne = now.plusDays(1); System.out.println("一天后:" + localDatePlusDayOne);
final LocalDate localDateMinusDayOne = now.minusDays(1); System.out.println("一天前:" + localDateMinusDayOne);
final int nowEvenBigger = now.compareTo(localDatePlusDayOne); System.out.println("当前时间更大么?" + nowEvenBigger);
final int dayOfMonth = now.getDayOfMonth(); System.out.println("当前月的天数:" + dayOfMonth);
final int dayOfMonthForChronoField = now.get(ChronoField.DAY_OF_MONTH); System.out.println("当前月的天数(通过 get() 获取):" + dayOfMonthForChronoField);
final long between = ChronoUnit.DAYS.between(now, localDatePlusDayOne); System.out.println(between);
|
上面有些地方看不太懂不碍事,先过一遍,下面对其中的部分代码会有解释
LocalTime
一个不可变的(线程安全)的时间对象,用于表示 时:分:秒:毫秒 的时间,默认 toString()
格式是 hh:mm:ss.SSS
。
基本操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| final LocalTime now = LocalTime.now();
System.out.println("现在:" + now);
final LocalTime localTimePlusHourOne = now.plusHours(1); System.out.println("一小时后:" + localTimePlusHourOne);
final LocalTime localTimeMinusMinuteOne = now.minusMinutes(1); System.out.println(localTimeMinusMinuteOne);
final int nowEvenBigger = now.compareTo(localTimeMinusMinuteOne); System.out.println("当前时间更大么?" + nowEvenBigger);
final int hour = now.getHour(); System.out.println("当前时间的小时数:" + hour);
final int hourOfDay = now.get(ChronoField.HOUR_OF_DAY); System.out.println("当前时间的小时数(通过 get() 获取):" + hourOfDay);
final long between = ChronoUnit.HOURS.between(now, localTimePlusHourOne); System.out.println(between);
|
LocalDateTime
不可变的日期时间对象,用于表示 日-月-年 时:分:秒:毫秒 的日期时间,默认格式化格式是 yyyy-MM-ddThh:mm:ss.SSS
。
基本操作
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
| final LocalDateTime now = LocalDateTime.now();
System.out.println("现在:" + now);
final LocalDateTime localTimePlusHourOne = now.plusHours(1); System.out.println("一小时后:" + localTimePlusHourOne);
final LocalDateTime localTimeMinusMinuteOne = now.minusMinutes(1); System.out.println(localTimeMinusMinuteOne);
final int nowEvenBigger = now.compareTo(localTimeMinusMinuteOne); System.out.println("当前时间更大么?" + nowEvenBigger);
final int hour = now.getHour(); System.out.println("当前时间的小时数:" + hour);
final int hourOfDay = now.get(ChronoField.HOUR_OF_DAY); System.out.println("当前时间的小时数(通过 get() 获取):" + hourOfDay);
final long between = ChronoUnit.HOURS.between(now, localTimePlusHourOne); System.out.println(between);
final LocalDateTime start = LocalDateTime.of(yesterday, LocalTime.MIN); final LocalDateTime end = LocalDateTime.of(yesterday, LocalTime.MAX); System.out.println(start); System.out.println(end);
|
可以看到,和上面的 LocalTime 除了类型不同外,代码是完全相同的,因为 LocalDateTime 是包含 LocalDate 与 LocalTime 的。在源码中也可以看到其包含了两个属性。
1 2 3 4 5
| final LocalDate localDate = now.toLocalDate(); final LocalTime localTime = now.toLocalTime(); System.out.println("当前日期:" + localDate); System.out.println("当前时间:" + localTime);
|
OffsetDateTime
代表偏移标准 UTC 时间的日期时间不可变对象,用于表示 日-月-年 时:分:秒:毫秒时区,默认格式是 yyyy-MM-ddThh:mm:ss.SSSZoneId
基本操作
1 2 3 4 5 6 7 8 9 10 11
| final OffsetDateTime now = OffsetDateTime.now(); System.out.println(now);
ZoneId zone = now.toZonedDateTime().getZone(); System.out.println(zone);
ZonedDateTime zonedDateTime = now.atZoneSameInstant(ZoneId.of("+00:00")); System.out.println(zonedDateTime);
|
Temporal/TemporalAccessor
上面的 LocalDate, LocalTime, LocalDateTime, OffsetDateTime 的基类,并定义了一系列非常通用的方法
- minus: 减少时间
- plus: 增加时间
- with: 获取时间指定单位的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| final LocalDateTime now = LocalDateTime.now();
final LocalDateTime lastWeek = now.minus(1, ChronoUnit.WEEKS);
final LocalDateTime nextMonth = now.plus(1, ChronoUnit.MONTHS);
final int weekValue = now.get(ChronoField.ALIGNED_WEEK_OF_YEAR);
final LocalDateTime nowOfMonday = now.with(DayOfWeek.MONDAY); final LocalDateTime nowOfMonday2 = now.with(ChronoField.DAY_OF_WEEK, 1); System.out.println( "lastWeek: " + lastWeek + "\nnextMonth: " + nextMonth + "\nweekValue: " + weekValue + "\nnowOfMonday: " + nowOfMonday + "\nnowOfMonday2: " + nowOfMonday2 );
|
Period
代表两个日期的差值,默认 toString()
格式是 P([时间][单位])*
基本操作
1 2 3 4 5 6 7 8 9 10
| final LocalDate now = LocalDate.now(); final LocalDate localTimePlusMonthOne = now.plusDays(1);
final Period betweenForDay = Period.between(now, localTimePlusMonthOne);
System.out.println("相差的天数:" + betweenForDay.getDays());
System.out.println("相差的天数(使用 get() 获取):" + betweenForDay.get(ChronoUnit.DAYS));
System.out.println("相差的日期减去 1 天是不是就相同了呢?" + betweenForDay.minusDays(1).isZero());
|
Duration
代表两个日期时间的差值,默认 toString()
格式是 PT([时间][单位])*
基本操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| final LocalDateTime now = LocalDateTime.now(); final LocalDateTime localDateTimeForPlusHourOne = now.plusHours(1);
final Duration between = Duration.between(now, localDateTimeForPlusHourOne);
System.out.println("相差的时间:" + between);
final long betweenSeconds = between.getSeconds(); System.out.println("相差的时间(/秒):" + betweenSeconds);
final long betweenSecondsForGet = between.get(ChronoUnit.SECONDS); System.out.println("相差的时间(/秒):" + betweenSecondsForGet);
System.out.println("相差的时间(/毫秒):" + between.toMillis());
final List<TemporalUnit> temporalUnitList = between.getUnits(); System.out.println("相差的时间列表:" + temporalUnitList);
final Duration betweenForMinutes = Duration.between(now, now.minusMinutes(100));
System.out.println("between 的时间差更大么?" + between.compareTo(betweenForMinutes));
System.out.println("相差的绝对时间:" + betweenForMinutes.abs());
|
TemporalField/ChronoField
日期/时间单位字段,TemporalField 是接口,ChronoField 则是实现类。
基本操作
1 2 3 4 5 6 7 8 9 10 11 12
| final LocalDateTime now = LocalDateTime.now(); final ChronoField hourOfDay = ChronoField.HOUR_OF_DAY; System.out.println(hourOfDay);
final long nowForHours = hourOfDay.getFrom(now); System.out.println("当前时间的小时数:" + nowForHours);
final int hoursForGet = now.get(ChronoField.HOUR_OF_DAY); System.out.println("当前时间的小时数(get()):" + hoursForGet);
final TemporalUnit baseUnit = hourOfDay.getBaseUnit(); System.out.println(baseUnit);
|
TemporalUnit/ChronoUnit
也是时间单位,TemporalUnit 是接口,ChronoUnit 则是实现类。和上面的不同的地方在于上面的不能 ChronoField 不能对时间进行对比差值,只能根据指定单位获取时间的大小。
基本操作
1 2 3 4 5 6 7 8 9 10 11
| final ChronoUnit chronoUnit = ChronoUnit.MILLIS; System.out.println(chronoUnit);
final LocalDateTime now = LocalDateTime.now(); final LocalDateTime localDateTimePlusHourOne = now.plusHours(1); final long betweenForMillis = chronoUnit.between(now, localDateTimePlusHourOne); System.out.println("相差的毫秒数:" + betweenForMillis);
final LocalDateTime localDateTimePlusMinuteOne = now.plus(1, ChronoUnit.MINUTES); System.out.println("一分钟后的时间:" + localDateTimePlusMinuteOne);
|
日期时间格式化类,基本上没什么好说的(标准的格式化一般就足够了,毕竟显示是前端的事情,而标准的格式化确实是国际标准呢)
基本使用
1 2 3 4 5 6 7 8 9
| final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_DATE_TIME; System.out.println(dateTimeFormatter);
final LocalDateTime now = LocalDateTime.now(); System.out.println("当前日期时间:" + dateTimeFormatter.format(now));
final DateTimeFormatter customDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh时mm分ss秒"); System.out.println(customDateTimeFormatter.format(now));
|
兼容 Date
使用新的时间 API 固然很令人舒服,但有时候不得不兼容旧的 Date 类型,这时候如何转换就很重要了呢
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| final LocalDateTime now = LocalDateTime.now();
final Instant instant = now.toInstant(ZoneOffset.UTC); System.out.println(instant);
final Date date = Date.from(instant); System.out.println("当前时间:" + date);
final Instant dateToInstant = date.toInstant(); System.out.println("两次转换后的时间还是相同的吧?" + dateToInstant.equals(instant));
final ZoneId zoneId = ZoneId.systemDefault(); final LocalDateTime toLocalDateTime = LocalDateTime.ofInstant(dateToInstant, zoneId); System.out.println("转换后的日期时间:" + toLocalDateTime);
|
主流框架支持
现如今大部分的包应该都支持 Java8 时间 API 了吧
例如:
- Jackson:jackson-datatype-jsr310
- Mybatis:mybatis-typehandlers-jsr310
那么,关于 Java8 新的时间 API 的使用暂且到这里了,想到什么吾辈再补充啦(=´∇ `=)