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

是否可以在新的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... } 

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

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

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

就像是:

 public class DateRange implements Iterable { 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 iterator() { return stream().iterator(); } public Stream stream() { return Stream.iterate(startDate, d -> d.plusDays(1)) .limit(ChronoUnit.DAYS.between(startDate, endDate) + 1); } public List toList() { //could also be built from the stream() method List dates = new ArrayList<> (); for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) { dates.add(d); } return dates; } } 

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

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

 LocalDate start = LocalDate.now(); LocalDate end = LocalDate.now().plusMonths(1).with(TemporalAdjusters.lastDayOfMonth()); List 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); 

Java 9

在Java 9中, LocalDate类使用LocalDate.datesUntil(LocalDate endExclusive)方法进行了增强,该方法将日期范围内的所有日期作为Stream

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

您可以使用.isAfter和.plusDays在循环中执行此操作。 我不会说更好,因为我没有对该主题进行大量研究,但我可以自信地说它使用Java 8 API并且是一个轻微的替代方案。

 LocalDate startDate = LocalDate.now(); LocalDate endDate = startDate.plusMonths(1); while (!startDate.isAfter(endDate)) { System.out.println(startDate); startDate = startDate.plusDays(1); } 

产量

 2016-07-05 2016-07-06 ... 2016-08-04 2016-08-05 

这里的例子

您可以创建LocalDate对象stream 。 我也有这个问题,我在github上将我的解决方案发布为java-timestream。

用你的例子……

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

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

ThreeTen-Extra库有一个LocalDateRange类,可以完全按照您的要求执行:

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

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

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

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

TL;博士

扩展了Singh的优秀答案 ,使用来自Java 9及更高版本的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,20188 -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-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框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参阅Oracle教程 。 并搜索Stack Overflow以获取许多示例和解释。 规范是JSR 310 。

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

从哪里获取java.time类?

  • Java SE 8Java SE 9Java SE 10Java SE 11及更高版本 – 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6Java SE 7
    • 大多数java.timefunction都在ThreeTen- Backport中反向移植到Java 6和7。
  • Android的
    • 更高版本的Android捆绑java.time类的实现。
    • 对于早期的Android(<26), ThreeTenABP项目采用ThreeTen-Backport (如上所述)。 请参见如何使用ThreeTenABP ….

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目是未来可能添加到java.time的试验场。 您可以在这里找到一些有用的课程,如IntervalYearWeekYearQuarter等。

您可以在Google的Guava库中使用Rangefunction。 在LocalDate实例上定义DiscreteDomain之后,您可以获得该范围内所有日期的ContiguousSet 。

 LocalDate d1 = LocalDate.parse("2017-12-25"); LocalDate d2 = LocalDate.parse("2018-01-05"); DiscreteDomain localDateDomain = new DiscreteDomain() { 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 datesInRange = ContiguousSet.create(Range.closed(d1, d2), localDateDomain);