【问题标题】:Java 8 LocalDate - How do I get all dates between two dates?Java 8 LocalDate - 如何获取两个日期之间的所有日期?
【发布时间】:2016-11-08 07:12:57
【问题描述】:

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

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

【问题讨论】:

  • 您也可以增加开始日期,只要它小于结束日期。 (假设 start 总是小于 end 开始。)
  • 我认为除了迭代之外别无他法。
  • 我可以想象可能会有一些花哨的 Java8 流 - 获取这些日期列表的方法......但我对那个新 API 不太熟悉。

标签: java date java-8 java-time


【解决方案1】:

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

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 等的编码风格。

【讨论】:

  • 在自己的类中拥有逻辑是一种干净的方法。感谢您为我指明这个方向!
  • @Flown 我可以(请参阅对 toList 方法的评论)。这是一个更大的课程的摘录,我认为不这样做的原因是性能(说实话,不确定它是否真的有影响)。
  • @assylias 我后来看到评论并删除了我的评论。在这种情况下,您真的认为性能是一个问题吗?
  • LongStream.rangeClosed(0, ChronoUnit.DAYS.between(startDate, endDate)) .mapToObj(startDate::plusDays)
  • 两者兼而有之,尽管可读性是主观的。但是它创建了一个大小合适的流,在迭代时可能会有一些优势,但是对于像toArraycount()(显然)和并行流这样的操作来说意义重大。而且它更接近于 OP 的原始循环。
【解决方案2】:

首先,您可以使用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);

【讨论】:

  • 感谢您提供非常好的解决方案。当我仍然需要列表时会使用它。
  • @Patrick 你没有指定结果应该是什么样子,这只是一个假设。您也不必存储结果,而是可以使用Stream::forEach 来处理结果。
  • 应该是.limit(ChronoUnit.DAYS.between(start, end) + 1),包括每个月的最后一天。
  • @user405935 这个问题已经有一年多了,应该概述一个解决方案。由用户来调整这个 sn-p 以适应要求。
  • 哇!我能够用这个技巧替换一个巨大的do...while。非常感谢。
【解决方案3】:

Java 9

在 Java 9 中,LocalDate 类通过LocalDate.datesUntil(LocalDate endExclusive) 方法得到了增强,该方法将日期范围内的所有日期作为Stream&lt;LocalDate&gt; 返回。

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

【讨论】:

  • 需要注意的是datesUntil是排他的。
【解决方案4】:

tl;博士

在 Java 9 及更高版本中使用来自 datesUntil 的流扩展良好的 Answer by Singh

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 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

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

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案5】:

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

    使用您的示例...

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

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

    【讨论】:

      【解决方案6】:

      ThreeTen-Extra 库有一个 LocalDateRange 类,可以完全满足您的要求:

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

      【讨论】:

        【解决方案7】:

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

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

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

        【讨论】:

          【解决方案8】:

          您可以使用 Google 的 Guava 库中的 Range 功能。在LocalDate 实例上定义DiscreteDomain 后,您可以获得范围内所有日期的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);
          

          【讨论】:

            猜你喜欢
            • 2018-07-10
            • 2013-08-09
            • 1970-01-01
            • 2017-06-12
            • 1970-01-01
            • 2014-06-11
            • 1970-01-01
            • 1970-01-01
            • 2018-11-06
            相关资源
            最近更新 更多