Como iterar através do intervalo de datas em Java?

145

No meu script, preciso executar um conjunto de ações através de um intervalo de datas, com base nas datas de início e término.
Por favor, me forneça orientações para conseguir isso usando Java.

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

}

Eu sei que o código acima é simplesmente impossível, mas faço isso para mostrar o que eu gostaria de alcançar.

Cache Staheli
fonte
Abordagem limpa do Java 8 e 9: stackoverflow.com/a/51942109/1216775
akhil_mittal 13/03/19

Respostas:

198

Bem, você poderia fazer algo assim usando a API do tempo do Java 8 , especificamente para esse problema java.time.LocalDate(ou as classes equivalentes do Joda Time para Java 7 e anteriores)

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

Eu recomendaria completamente o uso java.time(ou Joda Time) sobre as classes Date/ built-in Calendar.

Jon Skeet
fonte
2
Para expandir o assunto sobre o Joda Time: tentar implementar isso corretamente é mais difícil do que se imagina por causa dos casos extremos relacionados às mudanças de e para o horário de verão.
Raedwald 28/05
+1 para Joda, espero que um dia chegue a sua terra na API padrão.
gyorgyabraham
4
@gyabraham: JSR-310 está à procura de uma forma muito boa para Java 8.
Jon Skeet
4
Pode confirmar que exatamente o mesmo código funcionará usando o java.time.LocalDate do Java 8 em vez do Joda.
Ice derretido
3
O projeto Joda-Time agora está no modo de manutenção e recomenda a migração para as classes java.time. Conforme mencionado nos comentários, o código deste Respondente funciona como está em java.time, basta alterar suas importinstruções.
Basil Bourque
148

O JodaTime é bom, no entanto, por uma questão de integridade e / ou se você preferir os recursos fornecidos pela API, aqui estão as abordagens padrão da API.

Ao iniciar com java.util.Dateinstâncias como abaixo:

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

Aqui está a java.util.Calendarabordagem herdada , caso você ainda não esteja no 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);
}

E aqui está a java.time.LocalDateabordagem do Java8 , basicamente exatamente a abordagem do 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);
}

Se você deseja iterar inclusive a data final, use !start.after(end)e !date.isAfter(end)respectivamente.

BalusC
fonte
75

Estilo Java 8 , usando as 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);

Resultado:

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

Alternativo:

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

O Java 9 adicionou o método datasUntil () :

start.datesUntil(end.plusDays(1)).forEach(System.out::println);
Martin Andersson
fonte
1
você pode colocar múltiplos valores? por exemplo: apenas segunda-feira ou quinta-feira ou ambos
entrega 27/12
26

Esta é essencialmente a mesma resposta que BalusC deu, mas um pouco mais legível com um loop while no lugar de um loop 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.
fonte
3
Isso não funcionará se a lógica envolver instruções "continue", enquanto a versão do loop for do BalusC trabalha com instruções continue.
Sanjiv Jivan
6

Apache Commons

    for (Date dateIter = fromDate; !dateIter.after(toDate); dateIter = DateUtils.addDays(dateIter, 1)) {
        // ...
    }
Mike
fonte
+1, IMHO, este é o mais limpo quando você trabalha com código antigo. Basta fazer uma importação extra estática addDays(..)e ela fica ainda mais curta.
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
fonte
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
fonte
3

Podemos migrar a lógica para vários métodos para Java 7, Java 8 e 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());
}

Então, podemos invocar esses 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);

A saída seria:

[Seg 20 de dezembro 00:00:00 IST 2010, ter 21 de dezembro 00:00:00 IST 2010, qua 22 de dezembro 00:00:00 IST 2010, qui 23 de dezembro 00:00:00 IST 2010, sex 24 de dezembro 00: 00:00 IST 2010, sábado, 25 de dezembro 00:00:00 IST 2010]

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

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

akhil_mittal
fonte
1
O terrível Datee as Calendarclasses foram suplantadas pelas classes java.time anos atrás. Especificamente, substituído por Instante ZonedDateDate.
Basil Bourque
1
Eu gosto do Java 8 e 9 maneiras. Para Java 6 e 7, recomendo usar a biblioteca ThreeTen Backport e, em seguida, da mesma maneira que no Java 8. Você demonstra bem como essa maneira é mais clara e mais amigável para programadores.
Ole VV
2

Aqui está o código Java 8. Eu acho que esse código resolverá seu problema. Happy Coding

    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
fonte
Essa é a melhor e mais fácil abordagem que você pode acompanhar.
Jatin Goyal
1

Por que não usar a época e percorrer facilmente.

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

Você pode escrever uma classe como ela (implementando a interface do iterador) e iterar sobre ela.

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

e use-o como:

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

}
jdev
fonte
1

Você pode tentar isso:

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

Isso ajudará você a começar 30 dias até a data de hoje. você pode facilmente alterar o intervalo de datas e a direção.

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
fonte