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

时间:2023-01-17 21:30:53

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.

现在我需要startDate和endDate之间的所有日期。

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?

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

9 个解决方案

#1


47  

Assuming you mainly want to iterate over the date range, it would make sense to create a DateRange class that is iterable. That would allow you to write:

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

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的编码风格等是有意义的。

#2


50  

First you can use a TemporalAdjuster to get the last day of the month. Next the Stream API offers Stream::iterate which is the right tool for your problem.

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

#3


12  

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 返回。

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

#4


7  

You can use the .isAfter and .plusDays to do this over a loop. 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.

您可以使用.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);
}

Output

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

Example Here

#5


6  

You could create a stream of LocalDate objects. I had this problem too and I published my solution as java-timestream on github.

您可以创建LocalDate对象流。我也有这个问题,我在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).

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

#6


2  

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... */);

#7


1  

In my time library Time4J, I have written an optimized spliterator to construct a stream of calendar dates with good parallelization characteristics. Adapted to your use-case:

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

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,这个简短的方法可能是一个有趣的起点。

#8


1  

tl;dr

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

As of Java 9, you can ask for a stream of dates. Call LocalDate::datesUntil.

从Java 9开始,您可以要求提供日期流。调用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

The 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.

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗留日期时间类,如java.util.Date,Calendar和SimpleDateFormat。

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. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

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

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

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

Where to obtain the java.time classes?

从哪里获取java.time类?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
    • Java 9增加了一些小功能和修复。

  • Java SE 8,Java SE 9,Java SE 10,Java SE 11及更高版本 - 带有捆绑实现的标准Java API的一部分。 Java 9增加了一些小功能和修复。

  • Java SE 6 and Java SE 7
    • Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
    • 大多数java.time功能都在ThreeTen-Backport中反向移植到Java 6和7。

  • Java SE 6和Java SE 7大多数java.time功能都被反向移植到ThreeTen-Backport中的Java 6和7。

  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • 更高版本的Android捆绑java.time类的实现。

    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
    • 对于早期的Android(<26),ThreeTenABP项目采用ThreeTen-Backport(如上所述)。请参见如何使用ThreeTenABP ....

  • Android更新版本的Android捆绑java.time类的实现。对于早期的Android(<26),ThreeTenABP项目采用ThreeTen-Backport(如上所述)。请参见如何使用ThreeTenABP ....

The 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.

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

#9


0  

You could use the Range functionality in Google's Guava library. After defining the DiscreteDomain over LocalDate instances you could get a ContiguousSet of all dates in the range.

您可以在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);

#1


47  

Assuming you mainly want to iterate over the date range, it would make sense to create a DateRange class that is iterable. That would allow you to write:

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

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的编码风格等是有意义的。

#2


50  

First you can use a TemporalAdjuster to get the last day of the month. Next the Stream API offers Stream::iterate which is the right tool for your problem.

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

#3


12  

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 返回。

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

#4


7  

You can use the .isAfter and .plusDays to do this over a loop. 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.

您可以使用.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);
}

Output

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

Example Here

#5


6  

You could create a stream of LocalDate objects. I had this problem too and I published my solution as java-timestream on github.

您可以创建LocalDate对象流。我也有这个问题,我在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).

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

#6


2  

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... */);

#7


1  

In my time library Time4J, I have written an optimized spliterator to construct a stream of calendar dates with good parallelization characteristics. Adapted to your use-case:

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

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,这个简短的方法可能是一个有趣的起点。

#8


1  

tl;dr

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

As of Java 9, you can ask for a stream of dates. Call LocalDate::datesUntil.

从Java 9开始,您可以要求提供日期流。调用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

The 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.

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗留日期时间类,如java.util.Date,Calendar和SimpleDateFormat。

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. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

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

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

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

Where to obtain the java.time classes?

从哪里获取java.time类?

  • Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
    • Java 9 adds some minor features and fixes.
    • Java 9增加了一些小功能和修复。

  • Java SE 8,Java SE 9,Java SE 10,Java SE 11及更高版本 - 带有捆绑实现的标准Java API的一部分。 Java 9增加了一些小功能和修复。

  • Java SE 6 and Java SE 7
    • Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
    • 大多数java.time功能都在ThreeTen-Backport中反向移植到Java 6和7。

  • Java SE 6和Java SE 7大多数java.time功能都被反向移植到ThreeTen-Backport中的Java 6和7。

  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • 更高版本的Android捆绑java.time类的实现。

    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
    • 对于早期的Android(<26),ThreeTenABP项目采用ThreeTen-Backport(如上所述)。请参见如何使用ThreeTenABP ....

  • Android更新版本的Android捆绑java.time类的实现。对于早期的Android(<26),ThreeTenABP项目采用ThreeTen-Backport(如上所述)。请参见如何使用ThreeTenABP ....

The 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.

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

#9


0  

You could use the Range functionality in Google's Guava library. After defining the DiscreteDomain over LocalDate instances you could get a ContiguousSet of all dates in the range.

您可以在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);