Java 日期范围,用户希望返回两个给定端点之间的日期流,可以使用 Java 9 为 LocalDate 类新增的 datesUntil 方法。较之 java.util.Date、java.util.Calendar 以及 java.sql.Timestamp 类,Java 8 引入的 Date-Time API 是一种巨大改进。
Java 日期范围 问题描述
用户希望返回两个给定端点之间的日期流。
Java 日期范围 解决方案
使用 Java 9 为 LocalDate 类新增的 datesUntil 方法。
Java 日期范围 具体实例
较之 java.util.Date、java.util.Calendar 以及 java.sql.Timestamp 类,Java 8 引入的 Date-Time API 是一种巨大改进。而 Java 9 新增的 datesUntil 方法解决了 Date-Time API 中一个令人头疼的问题:难以方便地创建日期流。
在 Java 8 中,创建日期流最简单的方式是以初始日期为基准,再添加一个偏移量。例如,为返回相隔一周的两个给定端点之间的所有天数,我们可能会写出如例 10-34 所示的代码。
例 10-34 返回两个日期之间的天数(存在问题)
public List<LocalDate> getDays_java8(LocalDate start, LocalDate end) {
Period period = start.until(end);
return IntStream.range(0, period.getDays()) ➊
.mapToObj(start:plusDays)
.collect(Collectors.toList());
}
➊ 实为陷阱!正确实现参见例 10-35。
程序首先计算两个日期之间的 Period,然后在二者之间创建一个 IntStream。执行程序,观察结束日期和开始日期相隔一周时的情况:
LocalDate start = LocalDate.of(2017, Month.JUNE, 10);
LocalDate end = LocalDate.of(2017, Month.JUNE, 17);
System.out.println(dateRange.getDays_java8(start, end));
// [2017-06-10, 2017-06-11, 2017-06-12, 2017-06-13,
// 2017-06-14, 2017-06-15, 2017-06-16]
上述代码看似正确,实则有误。如果将结束日期改为与开始日期相隔正好一个月,很容易就能看出问题所在:
LocalDate start = LocalDate.of(2017, Month.JUNE, 10);
LocalDate end = LocalDate.of(2017, Month.JULY, 10);
System.out.println(dateRange.getDays_java8(start, end));
// []
可以看到,程序没有返回任何值。原因在于 period.getDays 方法返回的只是两个天数字段之间的天数,而非两个日期之间的总天数(getMonths、getYears 等方法同样如此)。如上所示,由于开始日期和结束日期的天数相同,虽然月份不同,结果仍然是一个大小为 0 的范围。
为解决这个问题,应采用实现 TemporalUnit 接口的 ChronoUnit 枚举,它定义了 DAYS、MONTHS 等多个枚举常量。Java 8 的正确实现如例 10-35 所示。
例 10-35 返回两个日期之间的天数(正确实现)
public List<LocalDate> getDays_java8(LocalDate start, LocalDate end) {
Period period = start.until(end);
return LongStream.range(0, ChronoUnit.DAYS.between(start, end)) ➊
.mapToObj(start:plusDays)
.collect(Collectors.toList());
}
➊ 正确无误
我们也可以使用 iterate 方法,但需要了解两个日期之间的天数,如例 10-36 所示。
例 10-36
LocalDate的迭代
public List<LocalDate> getDaysByIterate(LocalDate start, int days) {
return Stream.iterate(start, date -> date.plusDays(1))
.limit(days)
.collect(Collectors.toList());
}
好在 Java 9 引入的新方法使问题得以简化。LocalDate 类新增了一种名为 datesUntil 的方法,其重载形式传入 Period 作为参数。datesUntil 方法的签名如下:
Stream<LocalDate> datesUntil(LocalDate endExclusive)
Stream<LocalDate> datesUntil(LocalDate endExclusive, Period step)
不传入 Period 的 datesUntil 方法实际上相当于将日期增量设置为一天,即 datesUntil(endExclusive) 等效于 datesUntil(endExclusive, Period.ofDays(1))。
采用 datesUntil 方法返回两个日期之间的天数要简单得多,如例 10-37 所示。
例 10-37 返回两个日期之间的天数(Java 9 实现)
public List<LocalDate> getDays_java9(LocalDate start, LocalDate end) {
return start.datesUntil(end) ➊
.collect(Collectors.toList());
}
public List<LocalDate> getMonths_java9(LocalDate start, LocalDate end) {
return start.datesUntil(end, Period.ofMonths(1)) ➋
.collect(Collectors.toList());
}
❶ 相当于 Period.ofDays(1)
❷ 日期增量为一个月
我们可以使用所有常规的流处理技术对 datesUntil 方法产生的 Stream 操作。
极客教程