简体   繁体   English

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

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

Is there a usablility to get all dates between two dates in the new java.time API? 是否可以在新的java.time API中获取两个日期之间的所有日期

Let's say I have this part of code: 假设我有这部分代码:

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

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

Now I need all dates between startDate and endDate . 现在我需要startDateendDate之间的所有日期。

I was thinking to get the daysBetween of the two dates and iterate over: 我想要获取两个日期之间的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...
}

Is there a better way to get the dates? 有更好的方法来获取日期吗?

Assuming you mainly want to iterate over the date range, it would make sense to create a DateRange class that is iterable. 假设您主要想迭代日期范围,那么创建一个可迭代的DateRange类是有意义的。 That would allow you to write: 那会让你写:

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

Something like: 就像是:

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;
  }
}

It would make sense to add equals & hashcode methods, getters, maybe have a static factory + private constructor to match the coding style of the Java time API etc. 添加equals和hashcode方法,getters,可能有一个静态工厂+私有构造函数来匹配Java时间API的编码风格等是有意义的。

First you can use a TemporalAdjuster to get the last day of the month. 首先,您可以使用TemporalAdjuster来获取该月的最后一天。 Next the Stream API offers Stream::iterate which is the right tool for your problem. 接下来, 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);

Java 9 Java 9

In Java 9, the LocalDate class was enhanced with the LocalDate.datesUntil(LocalDate endExclusive) method, which returns all dates within a range of dates as a Stream<LocalDate> . 在Java 9中, LocalDate类使用LocalDate.datesUntil(LocalDate endExclusive)方法进行了增强,该方法以Stream<LocalDate>形式返回日期范围内的所有日期。

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

You can use the .isAfter and .plusDays to do this over a loop. 您可以使用.isAfter和.plusDays在循环中执行此操作。 I would not say better as I haven't done a huge amount of research into the topic but I can confidently say it uses the Java 8 API and is a slight alternative. 我不会说更好,因为我没有对该主题进行大量研究,但我可以自信地说它使用Java 8 API并且是一个轻微的替代方案。

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

Output 产量

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

Example Here 这里的例子

You could create a stream of LocalDate objects. 您可以创建LocalDate对象stream I had this problem too and I published my solution as java-timestream on github. 我也有这个问题,我在github上将我的解决方案发布为java-timestream。

Using your example... 用你的例子......

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

It's more or less equivalent to other solutions proposed here, but it takes care of all of the date math and knowing when to stop. 它或多或少等同于此处提出的其他解决方案,但它会处理所有日期数学并知道何时停止。 You can provide specific or relative end dates, and tell it how much time to skip each iteration (the default above is one day). 您可以提供特定或相对结束日期,并告诉它跳过每次迭代的时间(上面的默认值是一天)。

The ThreeTen-Extra library has a LocalDateRange class that can do exactly what you're requesting: ThreeTen-Extra库有一个LocalDateRange类,可以完全按照您的要求执行:

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

In my time library Time4J , I have written an optimized spliterator to construct a stream of calendar dates with good parallelization characteristics. 在我的Time4J时间库中,我编写了一个优化的spliterator来构建具有良好并行化特征的日历日期流。 Adapted to your use-case: 适应您的用例:

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

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

This short approach can be an interesting start point if you are also interested in related features like clock intervals per calendar date (partitioned streams) or other interval features and want to avoid awkward hand-written code, see also the API of DateInterval . 如果您还对每个日历日期的时钟间隔(分区流)或其他间隔功能等相关功能感兴趣并希望避免使用笨拙的手写代码,则此简短方法可能是一个有趣的起点,另请参阅DateInterval的API。

tl;dr TL;博士

Expanding on the good Answer by Singh , using a stream from datesUntil in Java 9 and later. 扩展了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, 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] [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 stream LocalDate::datesUntil

As of Java 9, you can ask for a stream of dates. 从Java 9开始,您可以要求提供日期流。 Call LocalDate::datesUntil . 调用LocalDate::datesUntil

Start by determining today's date. 首先确定今天的日期。 That requires a time zone. 这需要一个时区。 For any given moment, the date varies around the globe by zone. 对于任何给定的时刻,日期在全球范围内因地区而异。

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

Determine your ending date. 确定结束日期。

LocalDate stop = today.plusMonths( 1 ) ;

Ask for stream of dates from beginning to ending. 询问从开始到结束的日期流。

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

Pull the dates from that stream, collecting them into a List . 从该流中提取日期,将它们收集到List

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

Print our list of dates, generating text in standard ISO 8601 format. 打印我们的日期列表,生成标准ISO 8601格式的文本。

System.out.println( datesForMonthFromToday );

About java.time 关于java.time

The java.time framework is built into Java 8 and later. java.time框架内置于Java 8及更高版本中。 These classes supplant the troublesome old legacy date-time classes such as java.util.Date , Calendar , & SimpleDateFormat . 这些类取代了麻烦的旧遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

The Joda-Time project, now in maintenance mode , advises migration to the java.time classes. 现在处于维护模式Joda-Time项目建议迁移到java.time类。

To learn more, see the Oracle Tutorial . 要了解更多信息,请参阅Oracle教程 And search Stack Overflow for many examples and explanations. 并搜索Stack Overflow以获取许多示例和解释。 Specification is JSR 310 . 规范是JSR 310

You may exchange java.time objects directly with your database. 您可以直接与数据库交换java.time对象。 Use a JDBC driver compliant with JDBC 4.2 or later. 使用符合JDBC 4.2或更高版本的JDBC驱动程序 No need for strings, no need for java.sql.* classes. 不需要字符串,不需要java.sql.*类。

Where to obtain the java.time classes? 从哪里获取java.time类?

The ThreeTen-Extra project extends java.time with additional classes. ThreeTen-Extra项目使用其他类扩展了java.time。 This project is a proving ground for possible future additions to java.time. 该项目是未来可能添加到java.time的试验场。 You may find some useful classes here such as Interval , YearWeek , YearQuarter , and more . 您可以在这里找到一些有用的类,比如IntervalYearWeekYearQuarter ,和更多

You could use the Range functionality in Google's Guava library. 您可以在Google的Guava库中使用Range功能。 After defining the DiscreteDomain over LocalDate instances you could get a ContiguousSet of all dates in the 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM