Java 8 LocalDate - 如何获取两个日期之间的所有日期?

Java 8 LocalDate - How do I get all dates between two dates?

在新 java.time API 中是否可以获取 两个日期 之间的所有日期?

假设我有这部分代码:

@Test
public void testGenerateChartCalendarData() {
    LocalDate startDate = LocalDate.now();

    LocalDate endDate = startDate.plusMonths(1);
    endDate = endDate.withDayOfMonth(endDate.lengthOfMonth());
}

现在我需要 startDateendDate 之间的所有日期。

我想获取两个日期的 daysBetween 并迭代:

long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);

for(int i = 0; i <= daysBetween; i++){
    startDate.plusDays(i); //...do the stuff with the new date...
}

有没有更好的方法来获取日期?

首先,您可以使用 TemporalAdjuster 获取该月的最后一天。接下来 Stream API 提供 Stream::iterate 这是解决您的问题的正确工具。

LocalDate start = LocalDate.now();
LocalDate end = LocalDate.now().plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
List<LocalDate> dates = Stream.iterate(start, date -> date.plusDays(1))
    .limit(ChronoUnit.DAYS.between(start, end))
    .collect(Collectors.toList());
System.out.println(dates.size());
System.out.println(dates);

假设您主要想迭代日期范围,那么创建一个可迭代的 DateRange class 是有意义的。那将允许你写:

for (LocalDate d : DateRange.between(startDate, endDate)) ...

类似于:

public class DateRange implements Iterable<LocalDate> {

  private final LocalDate startDate;
  private final LocalDate endDate;

  public DateRange(LocalDate startDate, LocalDate endDate) {
    //check that range is valid (null, start < end)
    this.startDate = startDate;
    this.endDate = endDate;
  }

  @Override
  public Iterator<LocalDate> iterator() {
    return stream().iterator();
  }

  public Stream<LocalDate> stream() {
    return Stream.iterate(startDate, d -> d.plusDays(1))
                 .limit(ChronoUnit.DAYS.between(startDate, endDate) + 1);
  }

  public List<LocalDate> toList() { //could also be built from the stream() method
    List<LocalDate> dates = new ArrayList<> ();
    for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
      dates.add(d);
    }
    return dates;
  }
}

添加 equals 和 hashcode 方法、getter 是有意义的,也许有一个静态工厂 + 私有构造函数来匹配 Java 时间 API 等的编码风格

您可以创建 streamLocalDate 个对象。我也遇到了这个问题,我将我的解决方案发布为 java-timestream on github.

使用你的例子...

LocalDateStream
    .from(LocalDate.now())
    .to(1, ChronoUnit.MONTHS)
    .stream()
    .collect(Collectors.toList());

它或多或少等同于此处提出的其他解决方案,但它负责所有日期数学计算并知道何时停止。您可以提供特定的或相对的结束日期,并告诉它跳过每次迭代的时间(上面的默认值为一天)。

在我的时间库Time4J中,我编写了一个优化的拆分器来构建具有良好并行化特性的日历日期流。适应您的用例:

LocalDate start = ...;
LocalDate end = ...;

Stream<LocalDate> stream = 
  DateInterval.between(start, end) // closed interval, else use .withOpenEnd()
    .streamDaily()
    .map(PlainDate::toTemporalAccessor);

如果您还对相关功能感兴趣,例如每个日历日期的时钟间隔(分区流)或其他间隔功能,并且希望避免笨拙的手写代码,那么这种简短的方法可能是一个有趣的起点,另请参阅 API 共 DateInterval

Java 9

在 Java 9 中,LocalDate class 增强了 LocalDate.datesUntil(LocalDate endExclusive) 方法,returns 日期范围内的所有日期作为Stream<LocalDate>.

List<LocalDate> dates = startDate.datesUntil(endDate).collect(Collectors.toList());

ThreeTen-Extra library has a LocalDateRangeclass 可以完全满足您的要求:

LocalDateRange.ofClosed(startDate, endDate).stream()
        .forEach(/* ...do the stuff with the new date... */);

tl;博士

在 Java 9 及更高版本中 , using a stream from datesUntil 的优点进行扩展。

today                                 // Determine your beginning `LocalDate` object.
.datesUntil(                          // Generate stream of `LocalDate` objects.
    today.plusMonths( 1 )             // Calculate your ending date, and ask for a stream of dates till then.
)                                     // Returns the stream.
.collect( Collectors.toList() )       // Collect your resulting dates in a `List`. 
.toString()                           // Generate text representing your found dates.

[2018-09-20, 2018-09-21, 2018-09-22, 2018-09-23, 2018-09-24, 2018-09-25, 2018-09-26, 2018-09-27, 2018-09-28, 2018-09-29, 2018-09-30, 2018-10-01, 2018-10-02, 2018-10-03, 2018-10-04, 2018-10-05, 2018-10-06, 2018-10-07, 2018-10-08, 2018-10-09, 2018-10-10, 2018-10-11, 2018-10-12, 2018-10-13, 2018-10-14, 2018-10-15, 2018-10-16, 2018-10-17, 2018-10-18, 2018-10-19]

LocalDate::datesUntil

从 Java 9 开始,您可以请求日期流。调用 LocalDate::datesUntil.

首先确定今天的日期。这需要一个时区。对于任何给定的时刻,日期因地区而异。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
LocalDate today = LocalDate.now( z ) ;

确定结束日期。

LocalDate stop = today.plusMonths( 1 ) ;

请求从开始到结束的日期流。

Stream< LocalDate > stream = today.datesUntil( today.plusMonths( 1 ) );

从该流中提取日期,将它们收集到 List 中。

List< LocalDate > datesForMonthFromToday = stream.collect( Collectors.toList() );

打印我们的日期列表,以标准 ISO 8601 格式生成文本。

System.out.println( datesForMonthFromToday );

关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time 类.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* 类.

在哪里获取java.time类?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

您可以使用范围内所有日期的 Range functionality in Google's Guava library. After defining the DiscreteDomain over LocalDate instances you could get a ContiguousSet

LocalDate d1 = LocalDate.parse("2017-12-25");
LocalDate d2 = LocalDate.parse("2018-01-05");

DiscreteDomain<LocalDate> localDateDomain = new DiscreteDomain<LocalDate>() {
    public LocalDate next(LocalDate value) { return value.plusDays(1); }
    public LocalDate previous(LocalDate value) { return value.minusDays(1); }
    public long distance(LocalDate start, LocalDate end) { return start.until(end, ChronoUnit.DAYS); }
    public LocalDate minValue() { return LocalDate.MIN; }
    public LocalDate maxValue() { return LocalDate.MAX; }
};

Set<LocalDate> datesInRange = ContiguousSet.create(Range.closed(d1, d2), localDateDomain);