Java 日期范围

Java 日期范围,用户希望返回两个给定端点之间的日期流,可以使用 Java 9 为 LocalDate 类新增的 datesUntil 方法。较之 java.util.Datejava.util.Calendar 以及 java.sql.Timestamp 类,Java 8 引入的 Date-Time API 是一种巨大改进。

Java 日期范围 问题描述

用户希望返回两个给定端点之间的日期流。

Java 日期范围 解决方案

使用 Java 9 为 LocalDate 类新增的 datesUntil 方法。

Java 日期范围 具体实例

较之 java.util.Datejava.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 方法返回的只是两个天数字段之间的天数,而非两个日期之间的总天数(getMonthsgetYears 等方法同样如此)。如上所示,由于开始日期和结束日期的天数相同,虽然月份不同,结果仍然是一个大小为 0 的范围。
为解决这个问题,应采用实现 TemporalUnit 接口的 ChronoUnit 枚举,它定义了 DAYSMONTHS 等多个枚举常量。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)

不传入 PerioddatesUntil 方法实际上相当于将日期增量设置为一天,即 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 操作。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程