2016-07-06 8 views
24

Gibt es eine Möglichkeit, alle Daten zwischen zwei Daten in der neuen java.time API zu bekommen?Java 8 LocalDate - Wie bekomme ich alle Daten zwischen zwei Daten?

Lassen Sie uns sagen, dass ich diesen Teil des Codes haben:

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

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

Jetzt muss ich alle Termine zwischen startDate und endDate.

Ich dachte, die daysBetween der beiden Termine zu bekommen und iterieren:

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

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

Gibt es eine bessere Art und Weise die Daten zu bekommen?

+0

Sie können auch das Startdatum erhöhen, solange es kleiner als das Enddatum ist. (Angenommen, Start ist immer kleiner als Ende, um damit zu beginnen.) – Fildor

+0

Ich glaube nicht, dass es einen anderen Weg als iterate gibt. –

+0

Ich könnte mir vorstellen, dass es einen ausgefallenen Java8-Stream geben könnte, um eine Liste dieser Daten zu erhalten ... aber ich kenne diese neue API nicht sehr gut. – Fildor

Antwort

28

Angenommen, Sie in erster Linie über den Datumsbereich zu durchlaufen will, wäre es sinnvoll, eine DateRange Klasse zu erstellen, die iterable ist. Das würde erlauben Ihnen, schreiben Sie an:

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

Etwas wie:

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

Es wäre sinnvoll, gleich hinzuzufügen & hashcode Methoden, Getter, haben vielleicht eine statische Fabrik + privaten Konstruktor die Codierung Stil passen der Java-Zeit-API usw.

+0

Es ist ein sauberer Ansatz, um die Logik in einer eigenen Klasse zu haben. Danke, dass Sie mich in diese Richtung weisen! – Patrick

+0

@Flown konnte ich (siehe den Kommentar zu der ToList-Methode). Dies ist ein Auszug aus einer größeren Klasse und ich denke, der Grund war nicht Leistung (nicht sicher, ob es einen wirklichen Unterschied macht, um ehrlich zu sein). – assylias

+0

@assylias Ich sah den Kommentar später und löschte meinen Kommentar. Glaubst du wirklich, dass Leistung in diesem Fall ein Problem ist? – Flown

6

Sie können die .isAfter und .plusDays verwenden, um dies über eine Schleife zu tun. Ich würde nicht besser sagen, da ich nicht eine riesige Menge an Forschung in das Thema getan habe, aber ich kann zuversichtlich sagen, dass es die Java 8 API verwendet und ist eine leichte Alternative.

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

Ausgabe

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

Example Here

+0

Warum nicht eine 'for' Schleife? Sie haben eine initiale Anweisung, eine Bedingung und eine Inkrementierungsoperation, die genau dem Anwendungsfall der 'for'-Schleife entspricht, dh' for (LocalDate startDate = LocalDate.now(), endDate = startDate.plusMonths (1);! StartDate) .isAfter (endDate); startDate = startDate.plusDays (1)) {System.out.println (startDate); } ' – Holger

+0

@Holger Ich werde dir die umgekehrte Frage stellen. Warum eine 'for' Schleife? Was haben Sie von einer For-Schleife? Leistung oder Präferenz? –

+2

Wie gesagt, ist die For-Schleife das beabsichtigte syntaktische Konstrukt für diesen Anwendungsfall. Ein gut definiertes Muster für Anfangswert, Bedingung und Inkrement macht den Code klar. Diese drei verwandten Dinge sind auch immer zusammen, unabhängig von der Größe des Schleifenkörpers. In einer "while" -Schleife gibt es keine Möglichkeit, zwischen der Schleifeninkrementierungsoperation, die normalerweise irgendwo am Ende liegt, und der tatsächlichen Aktion innerhalb des Schleifenkörpers zu unterscheiden, ferner müssen die Schleifenvariablen außerhalb der Schleife deklariert werden, was zu a führt größerer Umfang als beabsichtigt. – Holger

30

Zuerst können Sie eine TemporalAdjuster verwenden Sie den letzten Tag des Monats zu erhalten. Als nächstes bietet die Stream API Stream::iterate welches das richtige Werkzeug für Ihr Problem ist.

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); 
+0

Danke für Ihre sehr gute Lösung. Würde es benutzen, wenn ich die Liste noch brauche. – Patrick

+2

@Patrick Sie haben nicht angegeben, wie die Ergebnisse aussehen sollen, es war nur eine Annahme. Sie müssen die Ergebnisse auch nicht speichern, stattdessen können Sie ['Stream :: forEach '] (https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream) verwenden. html # forEach-java.util.function.Consumer-), um die Ergebnisse zu verarbeiten. – Flown

+1

Es sollte ".limit (ChronoUnit.DAYS.between (start, end) + 1)" sein, um den letzten Tag jedes Monats einzubeziehen. – user405935

3

Sie könnten eine stream von LocalDate Objekte erstellen. Ich hatte dieses Problem und ich auch veröffentlicht meine Lösung als java-timestream on github.

Ihrem Beispiel verwenden ...

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

Es ist mehr oder weniger gleichwertig zu anderen Lösungen, die hier vorgeschlagen, aber es kümmert sich um alle von dem Datum math und zu wissen, wann man aufhören soll. Sie können bestimmte oder relative Enddaten angeben und angeben, wie viel Zeit jede Iteration überspringt (die Standardeinstellung ist ein Tag).

1

In meiner Zeitbibliothek Time4J habe ich einen optimierten Spliterator geschrieben, um einen Stream von Kalenderdaten mit guten Parallelisierungseigenschaften zu erstellen.Abgestimmt auf Ihren Anwendungsfall:

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

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

Dieser kurze Ansatz kann ein interessanter Startpunkt sein, wenn Sie auch Interesse an ähnlichen Funktionen wie Taktintervalle pro Kalendertag (partitioniert Streams) oder eine anderen Intervall Merkmale und will umständlich vermeiden handschriftlichen Code, siehe auch die API von DateInterval.

Verwandte Themen