¿Cómo iterar a través del rango de fechas en Java?

145

En mi script, necesito realizar un conjunto de acciones a través del rango de fechas, dada una fecha de inicio y finalización.
Por favor, deme orientación para lograr esto usando Java.

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

}

Sé que el código anterior es simplemente imposible, pero lo hago para mostrarle lo que me gustaría lograr.

Caché Staheli
fuente
Enfoque limpio Java 8 y 9: stackoverflow.com/a/51942109/1216775
akhil_mittal

Respuestas:

198

Bueno, podría hacer algo como esto usando la API de tiempo de Java 8 , específicamente para este problema java.time.LocalDate(o las clases equivalentes de Joda Time para Java 7 y anteriores)

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

Me minuciosamente recomendar el uso java.time(o tiempo Joda) durante los incorporados Date/ Calendarclases.

Jon Skeet
fuente
2
Para ampliar el punto sobre Joda Time: tratar de implementarlo correctamente usted mismo es más difícil de lo que uno podría pensar debido a casos de esquina en torno a los cambios hacia y desde el horario de verano.
Raedwald
+1 para Joda, espero que algún día llegue a su tierra en la API estándar.
gyorgyabraham
44
@ gyabraham: JSR-310 está en muy buena forma para Java 8.
Jon Skeet
44
Puede confirmar que este mismo código funcionará utilizando java.time.LocalDate de Java 8 en lugar de Joda.
Molten Ice
3
El proyecto Joda-Time ahora está en modo de mantenimiento y recomienda la migración a las clases java.time. Como se menciona en los comentarios, el código de esta respuesta funciona tal como está en java.time, solo cambie sus importdeclaraciones.
Basil Bourque
148

JodaTime es bueno, sin embargo, en aras de la integridad y / o si prefiere las instalaciones proporcionadas por API, estos son los enfoques estándar de API.

Al comenzar con java.util.Dateinstancias como a continuación:

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

Aquí está el java.util.Calendarenfoque heredado en caso de que aún no esté en 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);
}

Y aquí está el java.time.LocalDateenfoque de Java8 , básicamente exactamente el enfoque de 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 desea iterar incluyendo la fecha de finalización, use !start.after(end)y !date.isAfter(end)respectivamente.

BalusC
fuente
75

Estilo Java 8 , utilizando las clases 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);

Salida:

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

Alternativa:

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

Java 9 agregó el método dateUntil () :

start.datesUntil(end.plusDays(1)).forEach(System.out::println);
Martin Andersson
fuente
1
¿Puedes poner valores múltiples? por ejemplo: solo lunes o jueves o ambos
entrega el
26

Esta es esencialmente la misma respuesta que BalusC dio, pero un poco más legible con un ciclo while en lugar de un ciclo 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.
fuente
3
Esto no funcionará si la lógica involucra declaraciones de "continuar", mientras que la versión de bucle for de BalusC funciona con declaraciones de continuar.
Sanjiv Jivan
6

Apache Commons

    for (Date dateIter = fromDate; !dateIter.after(toDate); dateIter = DateUtils.addDays(dateIter, 1)) {
        // ...
    }
Miguel
fuente
+1, en mi humilde opinión, este es el más limpio cuando trabajas con código antiguo. Simplemente agregue una importación estática adicional para addDays(..)y se vuelve aún más corta.
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
fuente
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
fuente
3

Podemos migrar la lógica a varios métodos para Java 7, Java 8 y 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());
}

Entonces podemos invocar estos métodos como:

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

El resultado sería:

[Lunes 20 de diciembre 00:00:00 IST 2010, martes 21 de diciembre 00:00:00 IST 2010, miércoles 22 de diciembre 00:00:00 IST 2010, jueves 23 de diciembre 00:00:00 IST 2010, viernes 24 de diciembre 00: 00:00 IST 2010, sáb 25 dic 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
fuente
1
Lo terrible Datey las Calendarclases fueron suplantadas por las clases java.time hace años. Específicamente, reemplazado por Instanty ZonedDateDate.
Basil Bourque
1
Me gustan las formas Java 8 y 9. Para Java 6 y 7, recomiendo usar la biblioteca ThreeTen Backport y luego de la misma manera que en Java 8. Demuestra muy bien cómo esta manera es más clara y más amigable para los programadores.
Ole VV
2

Aquí está el código Java 8. Creo que este código resolverá tu problema. Codificación feliz

    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
fuente
Este es el mejor y más fácil enfoque que puede seguir.
jatin Goyal
1

¿Por qué no usar epoch y recorrer fácilmente?

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
fuente
1

Puede escribir una clase como esta (implementando la interfaz del iterador) e iterar sobre ella.

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

y úsalo como:

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

}
jdev
fuente
1

Puedes probar esto:

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

Esto lo ayudará a comenzar 30 días atrás y recorrer hasta la fecha de hoy. puede cambiar fácilmente el rango de fechas y dirección.

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
fuente