Número de dias entre duas datas no Joda-Time

335

Como encontro a diferença de dias entre duas instâncias Joda-Time DateTime ? Com "diferença de dias", quero dizer, se o início é na segunda-feira e o fim é na terça-feira, espero um valor de retorno 1, independentemente da hora / minuto / segundo das datas de início e término.

Days.daysBetween(start, end).getDays() dá-me 0 se o início for à noite e terminar de manhã.

Também estou tendo o mesmo problema com outros campos de data, então esperava que houvesse uma maneira genérica de 'ignorar' os campos de menor importância.

Em outras palavras, os meses entre fevereiro e 4 de março também seriam 1, assim como as horas entre 14:45 e 15:12. No entanto, a diferença horária entre 14:01 e 14:55 seria 0.

pvgoddijn
fonte

Respostas:

393

Irritantemente, a resposta withTimeAtStartOfDay está errada, mas apenas ocasionalmente. Você quer:

Days.daysBetween(start.toLocalDate(), end.toLocalDate()).getDays()

Acontece que "meia-noite / início do dia" às vezes significa 1h (o horário de verão acontece dessa maneira em alguns lugares), que Days.daysBetween não lida adequadamente.

// 5am on the 20th to 1pm on the 21st, October 2013, Brazil
DateTimeZone BRAZIL = DateTimeZone.forID("America/Sao_Paulo");
DateTime start = new DateTime(2013, 10, 20, 5, 0, 0, BRAZIL);
DateTime end = new DateTime(2013, 10, 21, 13, 0, 0, BRAZIL);
System.out.println(daysBetween(start.withTimeAtStartOfDay(),
                               end.withTimeAtStartOfDay()).getDays());
// prints 0
System.out.println(daysBetween(start.toLocalDate(),
                               end.toLocalDate()).getDays());
// prints 1

Passar por um LocalDatedesvio contorna toda a questão.

Alice Purcell
fonte
Você pode fornecer um exemplo de caso com dados específicos nos quais considera Days.daysBetweenincorretos?
Basil Bourque
Quando você diz para usar Instant, não está falando apenas start.toInstant(), está?
Patrick M
@PatrickM Sim, eu estava. Pensando bem, não está claro exatamente quais restrições isso pretende impor, então removerei a última frase. Obrigado!
Alice Purcell
@chrispy daysBetween doc diz que retorna o número de dias INTEIROS . No meu caso, 2 dias e 1 hora devem me devolver 3 dias. No seu exemplo, ele retorna 2 dias. Existe uma maneira de conseguir isso?
precisa
@SujitJoshi Acho que não entendi sua pergunta. Eu sugiro postar uma pergunta cheia de estouro de pilha e colar um link aqui para me :)
Alice Purcell
186

Days Classe

Usar a Daysclasse com o withTimeAtStartOfDaymétodo deve funcionar:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 
Michael Borgwardt
fonte
11
obrigado! Eu estava tentando alcançar o comportamento de android.text.format.DateUtils.getRelativeTimeSpanString () com joda e isso foi realmente útil.
gosho_ot_pochivka
11
Senhor, com referência a esta postagem, o método toDateMidnight()do tipo DateTime está obsoleto.
Aniket Kulkarni
2
Agora você deve usar .withTimeAtStartOfDay () em vez de .toDateMidnight ()
bgolson
2
e se o endfor antes do start, ele retorna dias negativos?
akhy
7
@akhyar: por que você não tenta?
Michael Borgwardt
86

você pode usar LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 
Bozho
fonte
Senhor, com referência a esta postagem, o método toDateMidnight()do tipo DateTime está obsoleto.
Aniket Kulkarni
Por que usar new LocalDate(date)mais date.toLocalDate()?
SARose
9

tl; dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.toLocalDate(), 
    later.toLocalDate() 
)

…ou…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

Para sua informação, o projeto Joda-Time agora está no modo de manutenção , com a equipe aconselhando a migração para as classes java.time .

O equivalente a Joda-Time DateTimeé ZonedDateTime.

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;

Aparentemente, você deseja contar os dias por datas, o que significa que deseja ignorar a hora do dia. Por exemplo, iniciar um minuto antes da meia-noite e terminar um minuto depois da meia-noite deve resultar em um único dia. Para esse comportamento, extraia um LocalDatedo seu ZonedDateTime. A LocalDateclasse representa um valor somente de data, sem hora do dia e sem fuso horário.

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Use a ChronoUnitenumeração para calcular os dias decorridos ou outras unidades.

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Truncar

Quanto a você perguntar sobre uma maneira mais geral de fazer isso contando onde você está interessado no delta de horas como hora do relógio, em vez de horas completas como tempo de sessenta minutos, use o truncatedTométodo

Aqui está o seu exemplo das 14:45 às 15:12 no mesmo dia.

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1 1

Isso não funciona por dias. Use toLocalDate () neste caso.


Sobre java.time

A estrutura java.time é integrada ao Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date, Calendar, e SimpleDateFormat.

O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para as classes java.time .

Para saber mais, consulte o Tutorial do Oracle . E procure Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .

Você pode trocar objetos java.time diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou posterior. Não há necessidade de strings, não há necessidade de java.sql.*classes.

Onde obter as classes java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 e posterior
    • Construídas em.
    • Parte da API Java padrão com uma implementação em pacote configurável.
    • O Java 9 adiciona alguns recursos e correções menores.
  • Java SE 6 e Java SE 7
    • Grande parte da funcionalidade java.time é portada para Java 6 e 7 no ThreeTen-Backport .
  • Android
    • Versões posteriores das implementações de pacote configurável do Android das classes java.time.
    • Para anteriormente Android (<26), o ThreeTenABP projeto adapta ThreeTen-Backport (mencionados acima). Consulte Como usar o ThreeTenABP… .

O projeto ThreeTen-Extra estende o java.time com classes adicionais. Este projeto é um campo de prova para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como Interval, YearWeek, YearQuarter, e mais .

O projeto ThreeTen-Extra estende o java.time com classes adicionais. Este projeto é um campo de prova para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como Interval, YearWeek, YearQuarter, e mais .

Basil Bourque
fonte
Seu TL; DR por dias usando truncado é vítima do bug detalhado na resposta aceita, ou seja, é desativado por um quando o início do dia é às 01:00. Eu o atualizei para usar a resposta correta que você deu mais abaixo, usando toLocalDate.
Alice Purcell
2

A resposta aceita cria dois LocalDateobjetos, que são bastante caros se você estiver lendo muitos dados. Eu uso isso:

  public static int getDaysBetween(DateTime earlier, DateTime later)
  {
    return (int) TimeUnit.MILLISECONDS.toDays(later.getMillis()- earlier.getMillis());
  }

Ao ligar, getMillis()você usa variáveis ​​já existentes.
MILLISECONDS.toDays()depois, usa um cálculo aritmético simples, não cria nenhum objeto.

JBoy
fonte
11
Esta resposta funciona apenas se a sua definição de 'dias' tiver exatamente 24 horas, sem considerar os fusos horários e datas. Nesse caso, use essa abordagem. Caso contrário, procure as outras respostas que abordam os fusos horários.
Basil Bourque
@BasilBourque, você está certo, ainda assim, eu procuraria uma solução baseada em cálculo aritmético, em vez de criar objetos caros para cada chamada de método, existem muitos sabores para esses cálculos, se você estiver lendo uma entrada de uma página da Web, pode ser ok, mas se você está processando um arquivo de log com centenas de milhares de linhas isso só faz com que seja muito lento
JBoy
11
Java otimizará a alocação de objetos se o loop estiver quente. Veja docs.oracle.com/javase/7/docs/technotes/guides/vm/…
Alice Purcell
1

java.time.Period

Use a java.time.Periodclasse para contar os dias .

Como o cálculo do Java 8, a diferença é mais intuitiva LocalDate, LocalDateTimepara representar as duas datas

    LocalDate now = LocalDate.now();
    LocalDate inputDate = LocalDate.of(2018, 11, 28);

    Period period = Period.between( inputDate, now);
    int diff = period.getDays();
    System.out.println("diff = " + diff);
AzizSM
fonte
0
DateTime  dt  = new DateTime(laterDate);        

DateTime newDate = dt.minus( new  DateTime ( previousDate ).getMillis());

System.out.println("No of days : " + newDate.getDayOfYear() - 1 );    
Arnab Bhui
fonte
-11
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}
user1091978
fonte
11
a questão está procurando especificamente o tempo de joda.
7115 kapad