Número de días entre dos fechas en Joda-Time

336

¿Cómo encuentro la diferencia en días entre dos instancias de Joda-Time DateTime ? Con "diferencia en días" quiero decir que si el inicio es el lunes y el final es el martes, espero un valor de retorno de 1 independientemente de la hora / minuto / segundo de las fechas de inicio y finalización.

Days.daysBetween(start, end).getDays() me da 0 si el inicio es por la tarde y termina por la mañana.

También tengo el mismo problema con otros campos de fecha, así que esperaba que hubiera una forma genérica de "ignorar" los campos de menor importancia.

En otras palabras, los meses entre febrero y 4 de marzo también serían 1, al igual que las horas entre las 14:45 y las 15:12. Sin embargo, la diferencia horaria entre las 14:01 y las 14:55 sería 0.

pvgoddijn
fuente

Respuestas:

393

Molesto, la respuesta withTimeAtStartOfDay es incorrecta, pero solo ocasionalmente. Usted quiere:

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

Resulta que "medianoche / comienzo del día" a veces significa 1 am (el horario de verano ocurre de esta manera en algunos lugares), que Days.daysBetween no se maneja correctamente.

// 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

Yendo por un LocalDatelado elude todo el asunto.

Alice Purcell
fuente
¿Puedes proporcionar un caso de ejemplo con datos específicos donde creas que Days.daysBetweenes incorrecto?
Basil Bourque
Cuando dices usar Instant, no solo estás hablando start.toInstant(), ¿verdad?
Patrick M
@PatrickM Sí, lo estaba. En la reflexión, no está claro exactamente qué restricciones se pretende imponer, por lo que eliminaré esa última oración. ¡Gracias!
Alice Purcell
@chrispy days Entre doc dice que devuelve el número de TODOS los días. En mi caso, 2 días y 1 hora deberían devolverme 3 días. En su ejemplo, devuelve 2 días. ¿Hay una manera de lograr esto?
Sujit Joshi
@SujitJoshi Me temo que no entiendo tu pregunta. Sugiero publicar una pregunta completa de desbordamiento de pila y pegar un enlace aquí para mí :)
Alice Purcell
186

Days Clase

Usar la Daysclase con el withTimeAtStartOfDaymétodo debería funcionar:

Days.daysBetween(start.withTimeAtStartOfDay() , end.withTimeAtStartOfDay() ).getDays() 
Michael Borgwardt
fuente
1
¡Gracias! Estaba tratando de lograr el comportamiento de android.text.format.DateUtils.getRelativeTimeSpanString () con joda y esto fue realmente útil.
gosho_ot_pochivka
1
Señor, con referencia a esta publicación, el método toDateMidnight()del tipo DateTime está en desuso.
Aniket Kulkarni
2
Ahora debe usar .withTimeAtStartOfDay () en lugar de .toDateMidnight ()
bgolson
2
¿Qué endpasa si es anterior al start, devuelve días negativos?
akhy
77
@akhyar: ¿por qué no lo intentas?
Michael Borgwardt
86

puedes usar LocalDate:

Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 
Bozho
fuente
Señor, con referencia a esta publicación, el método toDateMidnight()del tipo DateTime está en desuso.
Aniket Kulkarni
¿Por qué usar new LocalDate(date)más date.toLocalDate()?
SARose
9

tl; dr

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

…o…

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

java.time

Para su información, el proyecto Joda-Time ahora está en modo de mantenimiento , y el equipo aconseja migrar a las clases java.time .

El equivalente de Joda-Time DateTimees ZonedDateTime.

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

Aparentemente desea contar los días por fechas, lo que significa que desea ignorar la hora del día. Por ejemplo, comenzar un minuto antes de la medianoche y terminar un minuto después de la medianoche debería dar como resultado un solo día. Para este comportamiento, extraiga un LocalDatede su ZonedDateTime. La LocalDateclase representa un valor de solo fecha sin hora del día y sin zona horaria.

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

Use la ChronoUnitenumeración para calcular los días transcurridos u otras unidades.

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

Truncar

En cuanto a preguntar por una forma más general de hacer este recuento cuando le interesa el delta de horas como la hora del reloj en lugar de las horas completas como un período de tiempo de sesenta minutos, use el truncatedTométodo.

Aquí está su ejemplo de 14:45 a 15:12 el mismo día.

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

Esto no funciona por días. Use toLocalDate () en este caso.


Sobre java.time

El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date, Calendar, y SimpleDateFormat.

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .

Puede intercambiar objetos java.time directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. No hay necesidad de cadenas, no hay necesidad de java.sql.*clases.

¿Dónde obtener las clases java.time?

El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval, YearWeek, YearQuarter, y más .

El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval, YearWeek, YearQuarter, y más .

Albahaca Bourque
fuente
Su TL; DR durante días usando truncamiento cae presa del error detallado en la respuesta aceptada, es decir, está apagado por uno cuando el comienzo del día es a la 1 a.m. Lo actualicé para usar la respuesta correcta que dio más abajo, usando toLocalDate.
Alice Purcell
2

La respuesta aceptada crea dos LocalDateobjetos, que son bastante caros si está leyendo muchos datos. Yo uso esto:

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

Al llamar getMillis(), utiliza variables ya existentes.
MILLISECONDS.toDays()luego, usa un cálculo aritmético simple, no crea ningún objeto.

JBoy
fuente
1
Esta respuesta solo funciona si su definición de 'días' es exactamente de 24 horas sin tener en cuenta las zonas horarias y las fechas. Si es así, use este enfoque. Si no, busque las otras respuestas que abordan las zonas horarias.
Basil Bourque
@BasilBourque, tiene razón, aún así, buscaría una solución basada en el cálculo aritmético en lugar de construir objetos caros para cada llamada al método, hay muchos sabores para tales cálculos, si está leyendo una entrada de una página web, puede estar bien, pero si está procesando un archivo de registro con cientos de miles de líneas, esto solo lo hace muy lento
JBoy
1
Java optimizará la asignación de objetos si su ciclo está activo. Ver docs.oracle.com/javase/7/docs/technotes/guides/vm/…
Alice Purcell
1

java.time.Period

Usa la java.time.Periodclase para contar días .

Como Java 8 calcular la diferencia es más intuitivo LocalDate, LocalDateTimerepresenta las dos fechas

    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
fuente
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
fuente
-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;
}
usuario1091978
fuente
1
la pregunta está buscando específicamente el tiempo joda.
kapad