web-dev-qa-db-fra.com

Java 8 LocalDate - Comment obtenir toutes les dates comprises entre deux dates?

Existe-t-il une possibilité d’obtenir toutes les dates entre deux dates dans la nouvelle API Java.time?

Disons que j'ai cette partie de code:

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

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

Maintenant, j'ai besoin de toutes les dates comprises entre startDate et endDate.

Je pensais obtenir la daysBetween des deux dates et la parcourir:

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

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

Y a-t-il un meilleur moyen d'obtenir les dates?

43
Patrick

En supposant que vous souhaitiez principalement effectuer une itération sur la plage de dates, il serait logique de créer une classe DateRange qui soit itérable. Cela vous permettrait d'écrire:

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

Quelque chose comme:

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

Il serait logique d'ajouter des méthodes equals & hashcode, des getters, peut-être une fabrique statique fabrique + un constructeur privé pour correspondre au style de codage de l'API de temps Java, etc.

58
assylias

Premièrement, vous pouvez utiliser une variable TemporalAdjuster pour obtenir le dernier jour du mois. Ensuite, l’API Stream offre Stream::iterate qui est le bon outil pour votre problème.

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);
58
Flown

Java 9

En Java 9, la classe LocalDate a été améliorée avec la méthode LocalDate.datesUntil (LocalDate endExclusive) , qui renvoie toutes les dates dans une plage de dates sous la forme d'un Stream<LocalDate>.

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

Vous pouvez utiliser les méthodes .isAfter et .plusDays pour effectuer cette opération en boucle. Je ne dirais pas que mieux, car je n'ai pas fait beaucoup de recherches sur le sujet, mais je peux affirmer avec confiance qu'il utilise l'API Java 8 et qu'il s'agit d'une alternative légère.

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

Sortie

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

Exemple ici

8
N.J.Dawson

Vous pouvez créer une stream de LocalDate objets. J'ai eu ce problème aussi et j'ai publié ma solution en tant que Java-timestream sur github.

En utilisant votre exemple ...

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

C'est plus ou moins équivalent aux autres solutions proposées ici, mais cela prend en charge tous les calculs de date et de savoir quand s'arrêter. Vous pouvez fournir des dates de fin spécifiques ou relatives et lui indiquer le temps nécessaire pour ignorer chaque itération (la valeur par défaut est un jour).

6
Todd

La bibliothèque ThreeTen-Extra a une classe LocalDateRange qui peut faire exactement ce que vous demandez:

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

tl; dr

Développer le bon Answer by Singh , en utilisant un flux de datesUntil en Java 9 et versions ultérieures.

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 flux

A partir de Java 9, vous pouvez demander un flux de dates. Appelez LocalDate::datesUntil .

Commencez par déterminer la date du jour. Cela nécessite un fuseau horaire. Pour un moment donné, la date varie dans le monde entier par zone.

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

Déterminez votre date de fin.

LocalDate stop = today.plusMonths( 1 ) ;

Demandez un flux de dates du début à la fin.

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

Tirez les dates de ce flux, en les rassemblant dans une List

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

Imprimez notre liste de dates en générant le texte au format standard ISO 8601 .

System.out.println( datesForMonthFromToday );

À propos de Java.time

Le cadre Java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciennes classes gênantes héritées _ date-heure telles que Java.util.Date , Calendar _, & SimpleDateFormat .

Le projet Joda-Time , désormais en mode { mode maintenance }, conseille la migration vers les classes Java.time .

Pour en savoir plus, consultez le Oracle Tutorial . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .

Vous pouvez échanger des objets Java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou une version ultérieure. Pas besoin de chaînes, pas besoin de Java.sql.* classes.

Où obtenir les classes Java.time? 

Le projet ThreeTen-Extra étend Java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour d'éventuels ajouts à Java.time. Vous pouvez y trouver des classes utiles telles que Interval _, YearWeek , YearQuarter _ et plus _.

1
Basil Bourque

Dans ma bibliothèque de temps Time4J , j’ai écrit un séparateur optimisé pour créer un flux de dates avec de bonnes caractéristiques de parallélisation. Adapté à votre cas d'utilisation:

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

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

Cette approche succincte peut constituer un point de départ intéressant si vous êtes également intéressé par des fonctionnalités connexes telles que les intervalles d'horloge par date de calendrier (flux partitionnés) ou d'autres fonctionnalités d'intervalle et que vous souhaitez éviter un code manuscrit fastidieux, voir aussi l'API de DateInterval .

1
Meno Hochschild

Vous pouvez utiliser la fonctionnalité Range de la bibliothèque Guava de Google. Après avoir défini les instances DiscreteDomain sur LocalDate, vous pouvez obtenir un ContiguousSet de toutes les dates de la plage.

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);
0
M. Justin