Comment parcourir une plage de dates en Java?

145

Dans mon script, je dois effectuer un ensemble d'actions à travers une plage de dates, étant donné une date de début et de fin.
Veuillez me fournir des conseils pour y parvenir en utilisant Java.

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

}

Je sais que le code ci-dessus est tout simplement impossible, mais je le fais pour vous montrer ce que j'aimerais réaliser.

Cache Staheli
la source
Approche propre Java 8 et 9: stackoverflow.com/a/51942109/1216775
akhil_mittal

Réponses:

198

Eh bien, vous pouvez faire quelque chose comme ça en utilisant l'API time de Java 8 , pour ce problème en particulier java.time.LocalDate(ou les classes Joda Time équivalentes pour Java 7 et plus ancien)

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

Je bien vous recommandons d' utiliser java.time(ou Joda Time) sur le haut- Date/ Calendarclasses.

Jon Skeet
la source
2
Pour développer le point sur Joda Time: essayer de l'implémenter correctement vous-même est plus difficile qu'on pourrait le penser en raison des cas critiques autour des changements vers et depuis l'heure d'été.
Raedwald
+1 pour Joda, j'espère qu'un jour il atteindra sa terre dans l'API standard.
gyorgyabraham
4
@gyabraham: JSR-310 semble en assez bonne forme pour Java 8.
Jon Skeet
4
Peut confirmer que ce même code fonctionnera en utilisant java.time.LocalDate de Java 8 au lieu de Joda.
Molten Ice
3
Le projet Joda-Time est maintenant en mode maintenance et recommande la migration vers les classes java.time. Comme mentionné dans les commentaires, le code de cette réponse fonctionne tel quel dans java.time, changez simplement vos importdéclarations.
Basil Bourque
148

JodaTime est bien, cependant, dans un souci d'exhaustivité et / ou si vous préférez les installations fournies par l'API, voici les approches API standard.

Au début avec des java.util.Dateinstances comme ci-dessous:

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

Voici l' java.util.Calendarapproche héritée au cas où vous ne seriez pas encore sur 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);
}

Et voici l' java.time.LocalDateapproche de Java8 , essentiellement l'approche 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);
}

Si vous souhaitez itérer la date de fin incluse , utilisez respectivement !start.after(end)et !date.isAfter(end).

BalusC
la source
75

Style Java 8 , utilisant les classes 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);

Production:

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

Alternative:

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

Java 9 a ajouté la méthode datesUntil () :

start.datesUntil(end.plusDays(1)).forEach(System.out::println);
Martin Andersson
la source
1
pouvez-vous mettre des valeurs multiples? par exemple: seulement lundi ou jeudi ou les deux
livraison
26

C'est essentiellement la même réponse que BalusC a donnée, mais un peu plus lisible avec une boucle while à la place d'une boucle 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.
la source
3
Cela ne fonctionnera pas si la logique implique des instructions "continue", alors que la version boucle for de BalusC fonctionne avec des instructions continues.
Sanjiv Jivan
6

Apache Commons

    for (Date dateIter = fromDate; !dateIter.after(toDate); dateIter = DateUtils.addDays(dateIter, 1)) {
        // ...
    }
Mike
la source
+1, à mon humble avis, c'est le plus propre lorsque vous travaillez avec l'ancien code. Ajoutez simplement une importation statique supplémentaire pour addDays(..)et cela devient encore plus court.
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
la source
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
la source
3

Nous pouvons migrer la logique vers différentes méthodes pour Java 7, Java 8 et 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());
}

Ensuite, nous pouvons invoquer ces méthodes comme:

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

Le résultat serait:

[Mon Dec 20 00:00:00 IST 2010, Tue Dec 21 00:00:00 IST 2010, Wed Dec 22 00:00:00 IST 2010, Thu Dec 23 00:00:00 IST 2010, Fri Dec 24 00: 00:00 IST 2010, Sat Dec 25 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
la source
1
Les classes terribles Dateet Calendaront été supplantées par les classes java.time il y a des années. Plus précisément, remplacé par Instantet ZonedDateDate.
Basil Bourque
1
J'aime les manières Java 8 et 9. Pour Java 6 et 7, je recommande d'utiliser la bibliothèque ThreeTen Backport , puis de la même manière que dans Java 8. Vous démontrez bien comment cette méthode est plus claire et plus conviviale pour les programmeurs.
Ole VV
2

Voici le code Java 8. Je pense que ce code résoudra votre problème. Codage heureux

    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
la source
C'est la meilleure et la plus simple approche que vous pouvez suivre.
jatin Goyal
1

Pourquoi ne pas utiliser epoch et boucler facilement.

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
la source
1

Vous pouvez écrire une classe comme celle-ci (implémentation de l'interface d'itérateur) et effectuer une itération dessus.

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

et utilisez-le comme:

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

}
jdev
la source
1

Vous pouvez essayer ceci:

OffsetDateTime currentDateTime = OffsetDateTime.now();
for (OffsetDateTime date = currentDateTime; date.isAfter(currentDateTime.minusYears(YEARS)); date = date.minusWeeks(1))
{
    ...
}
Pankaj Singh
la source
0

Cela vous aidera à commencer 30 jours en arrière et à boucler jusqu'à la date d'aujourd'hui. vous pouvez facilement changer la plage de dates et la direction.

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
la source