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

¿Cómo encuentro la diferencia en Días entre dos instancias 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 estoy teniendo 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 el 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 14:01 y 14:55 sería 0.

Es molesto que la respuesta withTimeAtStartOfDay sea incorrecta, pero solo ocasionalmente. Usted quiere:

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

Resulta que “medianoche / inicio del día” a veces significa 1am (el horario de verano se produce de esta manera en algunos lugares), que Days.daysBetween no 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 

Ir a través de un LocalDate evita todo el problema.

Clase de Days

Usar la clase Days con el método withTimeAtStartOfDay debería funcionar:

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

puedes usar LocalDate :

 Days.daysBetween(new LocalDate(start), new LocalDate(end)).getDays() 

tl; dr

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

…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 recomienda la migración a las clases java.time .

El equivalente de Joda-Time DateTime es 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 finalizar un minuto después de la medianoche debería resultar en un solo día. Para este comportamiento, extraiga un LocalDate de su ZonedDateTime . La clase LocalDate 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 enumeración ChronoUnit para calcular los días transcurridos u otras unidades.

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

Truncar

En cuanto a que pregunte acerca de una manera más general de hacer este recuento en el que le interesa el delta de horas como la hora del reloj en lugar de las horas completas como intervalos de tiempo de sesenta minutos, use el método truncatedTo .

Aquí está su ejemplo de 14:45 a 15:12 en 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

Para contar los días por fechas, trunque a ChronoUnit.DAYS . Aquí hay un ejemplo que pasa de la medianoche de cinco minutos antes a cinco minutos después, para los días transcurridos de 1 .

 ZoneId z = ZoneId.of( "America/Montreal" ); ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 23 , 55 , 0 , 0 , z ); ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 18 , 00 , 05 , 0 , 0 , z ); long days = ChronoUnit.DAYS.between( start.truncatedTo( ChronoUnit.DAYS ) , stop.truncatedTo( ChronoUnit.DAYS ) ); 

1


Acerca de java.time

El marco java.time está integrado en Java 8 y posterior. Estas clases suplantan a las problemáticas antiguas clases heredadas 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. Use un controlador JDBC que cumpla con JDBC 4.2 o posterior. Sin necesidad de cadenas, sin necesidad de clases java.sql.* .

¿Dónde obtener las clases de java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 y posterior
    • Incorporado.
    • Parte de la API Java estándar con una implementación integrada.
    • Java 9 agrega algunas características y correcciones menores.
  • Java SE 6 y Java SE 7
    • Gran parte de la funcionalidad de java.time se transfiere a Java 6 y 7 en ThreeTen-Backport .
  • Androide
    • Versiones posteriores de las implementaciones del paquete de Android de las clases java.time.
    • Para Android anterior (<26), el proyecto ThreeTenABP adapta ThreeTen-Backport (mencionado anteriormente). Consulte Cómo utilizar ThreeTenABP ….

El proyecto ThreeTen-Extra amplía java.time con clases adicionales. Este proyecto es un terreno de prueba para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como Interval , YearWeek , YearQuarter y más .

El proyecto ThreeTen-Extra amplía java.time con clases adicionales. Este proyecto es un terreno de prueba para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como Interval , YearWeek , YearQuarter y más .

La respuesta aceptada genera dos objetos LocalDate , 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 a getMillis() usa variables ya existentes.
MILLISECONDS.toDays() entonces, usa un cálculo aritmético simple, no crea ningún objeto.

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