Jak iterować zakres dat w Javie?

144

W moim skrypcie muszę wykonać zestaw działań w zakresie dat, podając datę rozpoczęcia i zakończenia.
Proszę o wskazówki, jak to osiągnąć za pomocą języka Java.

for ( currentDate = starDate; currentDate < endDate; currentDate++) {

}

Wiem, że powyższy kod jest po prostu niemożliwy, ale robię to, aby pokazać Ci, co chciałbym osiągnąć.

Cache Staheli
źródło
Czyste podejście do Java 8 i 9: stackoverflow.com/a/51942109/1216775
akhil_mittal

Odpowiedzi:

198

Cóż, możesz zrobić coś takiego, używając API time-API Java 8 , specjalnie dla tego problemu java.time.LocalDate(lub równoważnych klas Joda Time dla Java 7 i starszych)

for (LocalDate date = startDate; date.isBefore(endDate); date = date.plusDays(1))
{
    ...
}

Chciałbym dokładnie polecam korzystania java.time(lub Joda Time) na wbudowanych Date/ Calendarklas.

Jon Skeet
źródło
2
Aby rozwinąć kwestię dotyczącą czasu Joda: próba samodzielnego wdrożenia tego poprawnie jest trudniejsza, niż mogłoby się wydawać, ze względu na przypadki narożne związane ze zmianami do i od czasu letniego.
Raedwald
+1 dla Jody, mam nadzieję, że kiedyś trafi na ląd w standardowym API.
gyorgyabraham
4
@gyabraham: JSR-310 wygląda w całkiem niezłym stanie dla Javy 8.
Jon Skeet
4
Potwierdzam, że ten sam kod będzie działał przy użyciu Java 8's java.time.LocalDate zamiast Joda.
Molten Ice,
3
Projekt Joda-Time jest teraz w trybie konserwacji i zaleca migrację do klas java.time. Jak wspomniano w komentarzach, kod tej odpowiedzi działa tak, jak jest w java.time, po prostu zmień swoje importinstrukcje.
Basil Bourque
146

JodaTime jest jednak fajny, ze względu na kompletność i / lub jeśli wolisz funkcje dostarczane przez API, oto standardowe podejścia do API.

Rozpoczynając z java.util.Dateprzykładami takimi jak poniżej:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date startDate = formatter.parse("2010-12-20");
Date endDate = formatter.parse("2010-12-26");

Oto starsze java.util.Calendarpodejście na wypadek, gdybyś nie korzystał jeszcze z Java8:

Calendar start = Calendar.getInstance();
start.setTime(startDate);
Calendar end = Calendar.getInstance();
end.setTime(endDate);

for (Date date = start.getTime(); start.before(end); start.add(Calendar.DATE, 1), date = start.getTime()) {
    // Do your job here with `date`.
    System.out.println(date);
}

A oto java.time.LocalDatepodejście Java8 , w zasadzie dokładnie podejście JodaTime:

LocalDate start = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate end = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

for (LocalDate date = start; date.isBefore(end); date = date.plusDays(1)) {
    // Do your job here with `date`.
    System.out.println(date);
}

Jeśli chcesz iteracyjne włącznie datę końcową, a następnie użyć !start.after(end)i !date.isAfter(end)odpowiednio.

BalusC
źródło
75

Styl Java 8 , przy użyciu klas java.time :

// Monday, February 29 is a leap day in 2016 (otherwise, February only has 28 days)
LocalDate start = LocalDate.parse("2016-02-28"),
          end   = LocalDate.parse("2016-03-02");

// 4 days between (end is inclusive in this example)
Stream.iterate(start, date -> date.plusDays(1))
        .limit(ChronoUnit.DAYS.between(start, end) + 1)
        .forEach(System.out::println);

Wynik:

2016-02-28
2016-02-29
2016-03-01
2016-03-02

Alternatywny:

LocalDate next = start.minusDays(1);
while ((next = next.plusDays(1)).isBefore(end.plusDays(1))) {
    System.out.println(next);
}

Java 9 dodała metodę dateUntil () :

start.datesUntil(end.plusDays(1)).forEach(System.out::println);
Martin Andersson
źródło
1
czy możesz podać wielokrotności? na przykład: tylko w poniedziałek lub czwartek lub obu
Dosta
26

Jest to zasadniczo ta sama odpowiedź, której udzielił BalusC, ale nieco bardziej czytelna dzięki pętli while zamiast pętli for:

Calendar start = Calendar.getInstance();
start.setTime(startDate);

Calendar end = Calendar.getInstance();
end.setTime(endDate);

while( !start.after(end)){
    Date targetDay = start.getTime();
    // Do Work Here

    start.add(Calendar.DATE, 1);
}
Chris M.
źródło
3
To nie zadziała, jeśli logika obejmuje instrukcje „continue”, podczas gdy wersja BalusC pętli for działa z instrukcjami continue.
Sanjiv Jivan
6

Apache Commons

    for (Date dateIter = fromDate; !dateIter.after(toDate); dateIter = DateUtils.addDays(dateIter, 1)) {
        // ...
    }
Mikrofon
źródło
+1, IMHO, to jest najczystsze, gdy pracujesz ze starym kodem. Po prostu dodaj dodatkowy statyczny import, addDays(..)a będzie on jeszcze krótszy.
Priidu Neemre
4
private static void iterateBetweenDates(Date startDate, Date endDate) {
    Calendar startCalender = Calendar.getInstance();
    startCalender.setTime(startDate);
    Calendar endCalendar = Calendar.getInstance();
    endCalendar.setTime(endDate);

    for(; startCalender.compareTo(endCalendar)<=0;
          startCalender.add(Calendar.DATE, 1)) {
        // write your main logic here
    }

}
kushal agrawal
źródło
3
public static final void generateRange(final Date dateFrom, final Date dateTo)
{
    final Calendar current = Calendar.getInstance();
    current.setTime(dateFrom);

    while (!current.getTime().after(dateTo))
    {
        // TODO

        current.add(Calendar.DATE, 1);
    }
}
kayz1
źródło
3

Możemy przeprowadzić migrację logiki do różnych metod dla Java 7, Java 8 i Java 9 :

public static List<Date> getDatesRangeJava7(Date startDate, Date endDate) {
    List<Date> datesInRange = new ArrayList<>();
    Calendar startCalendar = new GregorianCalendar();
    startCalendar.setTime(startDate);
    Calendar endCalendar = new GregorianCalendar();
    endCalendar.setTime(endDate);
    while (startCalendar.before(endCalendar)) {
        Date result = startCalendar.getTime();
        datesInRange.add(result);
        startCalendar.add(Calendar.DATE, 1);
    }
    return datesInRange;
}

public static List<LocalDate> getDatesRangeJava8(LocalDate startDate, LocalDate endDate) {
    int numOfDays = (int) ChronoUnit.DAYS.between(startDate, endDate);
    return IntStream.range(0, numOfDays)
            .mapToObj(startDate::plusDays)
            .collect(Collectors.toList());
}

public static List<LocalDate> getDatesRangeJava9(LocalDate startDate, LocalDate endDate) {
    return startDate.datesUntil(endDate).collect(Collectors.toList());
}

Następnie możemy wywołać te metody jako:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date startDate = formatter.parse("2010-12-20");
Date endDate = formatter.parse("2010-12-26");
List<Date> dateRangeList = getDatesRangeJava7(startDate, endDate);
System.out.println(dateRangeList);

LocalDate startLocalDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate endLocalDate = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
List<LocalDate> dateRangeList8 = getDatesRangeJava8(startLocalDate, endLocalDate);
System.out.println(dateRangeList8);
List<LocalDate> dateRangeList9 = getDatesRangeJava8(startLocalDate, endLocalDate);
System.out.println(dateRangeList9);

Wynik byłby następujący:

[Pon. 20 grudnia 00:00:00 IST 2010, wt. 21 grudnia 00:00:00 IST 2010, śr. 22 grudnia 00:00:00 IST 2010, czw. 23 grudnia 00:00:00 IST 2010, pt. 24 grudnia 00: 00:00 IST 2010, sob. 25 grudnia 00:00:00 IST 2010]

[2010-12-20, 2010-12-21, 2010-12-22, 2010-12-23, 2010-12-24, 2010-12-25]

[2010-12-20, 2010-12-21, 2010-12-22, 2010-12-23, 2010-12-24, 2010-12-25]

akhil_mittal
źródło
1
Straszne Datei Calendarklasy zostały wyparte przez klasy java.time wiele lat temu. W szczególności zastąpione przez Instanti ZonedDateDate.
Basil Bourque
1
Lubię sposoby Java 8 i 9. W przypadku Javy 6 i 7 polecam korzystanie z biblioteki ThreeTen Backport, a następnie w taki sam sposób jak w Javie 8. Ładnie demonstrujesz, jak ten sposób jest jaśniejszy i bardziej przyjazny programistom.
Ole VV
2

Oto kod Java 8. Myślę, że ten kod rozwiąże Twój problem. Miłego kodowania

    LocalDate start = LocalDate.now();
    LocalDate end = LocalDate.of(2016, 9, 1);//JAVA 9 release date
    Long duration = start.until(end, ChronoUnit.DAYS);
    System.out.println(duration);
     // Do Any stuff Here there after
    IntStream.iterate(0, i -> i + 1)
             .limit(duration)
             .forEach((i) -> {});
     //old way of iteration
    for (int i = 0; i < duration; i++)
     System.out.print("" + i);// Do Any stuff Here
jatin Goyal
źródło
To najlepsze i łatwe podejście, które możesz zastosować.
jatin Goyal
1

Dlaczego nie użyć epoki i łatwo przejść przez pętlę.

long startDateEpoch = new java.text.SimpleDateFormat("dd/MM/yyyy").parse(startDate).getTime() / 1000;

    long endDateEpoch = new java.text.SimpleDateFormat("dd/MM/yyyy").parse(endDate).getTime() / 1000;


    long i;
    for(i=startDateEpoch ; i<=endDateEpoch; i+=86400){

        System.out.println(i);

    }
mridul4c
źródło
1

Możesz napisać podobną klasę (implementując interfejs iteratora) i iterować po niej.

public class DateIterator implements Iterator<Date>, Iterable<Date>
{

 private Calendar end = Calendar.getInstance();
 private Calendar current = Calendar.getInstance();

 public DateIterator(Date start, Date end)
 {
     this.end.setTime(end);
     this.end.add(Calendar.DATE, -1);
     this.current.setTime(start);
     this.current.add(Calendar.DATE, -1);
 }

 @Override
 public boolean hasNext()
 {
     return !current.after(end);
 }

 @Override
 public Date next()
 {
     current.add(Calendar.DATE, 1);
     return current.getTime();
 }

 @Override
 public void remove()
 {
     throw new UnsupportedOperationException(
        "Cannot remove");
 }

 @Override
 public Iterator<Date> iterator()
 {
     return this;
 }
}

i używaj go jak:

Iterator<Date> dateIterator = new DateIterator(startDate, endDate);
while(dateIterator.hasNext()){
      Date selectedDate = dateIterator .next();

}
jdev
źródło
1

Możesz spróbować tego:

OffsetDateTime currentDateTime = OffsetDateTime.now();
for (OffsetDateTime date = currentDateTime; date.isAfter(currentDateTime.minusYears(YEARS)); date = date.minusWeeks(1))
{
    ...
}
Pankaj Singh
źródło
0

Pomoże Ci to rozpocząć 30 dni wstecz i przejść do dzisiejszej daty. możesz łatwo zmienić zakres dat i kierunek.

private void iterateThroughDates() throws Exception {
    Calendar start = Calendar.getInstance();
    start.add(Calendar.DATE, -30);
    Calendar end = Calendar.getInstance();
    for (Calendar date = start; date.before(end); date.add(Calendar.DATE, 1))
        {
        System.out.println(date.getTime());
        }
}
Januka samaranyake
źródło