Tengo un método Java que compara dos Dates y devuelve el número de días entre ellos, pero tiene una diferencia de un día.

Incluso después de 0 fuera de las horas, minutos y segundos, el cálculo sigue desactivado.

public long compareDates(Date exp, Date today){
        TimeZone tzone = TimeZone.getTimeZone("America/New_York");
        Calendar expDate = Calendar.getInstance();
        Calendar todayDate = Calendar.getInstance();

        expDate.setTime(exp);
        todayDate.setTime(today);

        expDate.set(Calendar.HOUR_OF_DAY, 0);
        expDate.set(Calendar.MINUTE, 0);
        expDate.set(Calendar.SECOND, 0);

        todayDate.set(Calendar.HOUR_OF_DAY, 0);
        todayDate.set(Calendar.MINUTE, 0);
        todayDate.set(Calendar.SECOND, 0);

        logger.info("Today = " + Long.toString(todayDate.getTimeInMillis()) + " Expiration = " + Long.toString(expDate.getTimeInMillis()));

        expDate.setTimeZone(tzone);
        todayDate.setTimeZone(tzone);

        return (expDate.getTimeInMillis()-todayDate.getTimeInMillis())/86400000;
    }

Salida

Today = 1453939200030 Expiration = 1454544000000

Hay 7 días entre 1/28 y 2/4, pero esto devuelve 6.

1
PT_C 29 ene. 2016 a las 00:10

4 respuestas

La mejor respuesta

Para solucionar mis problemas, he puesto a cero los milisegundos como se mencionó, y he cambiado los largos a dobles para mantener la precisión y la redondez cuando sea necesario.

expDate.setTime(exp);
todayDate.setTime(today);
expDate.setTimeZone(tzone);
todayDate.setTimeZone(tzone);

expDate.set(Calendar.HOUR_OF_DAY, 0);
expDate.set(Calendar.MINUTE, 0);
expDate.set(Calendar.SECOND, 0);
expDate.set(Calendar.MILLISECOND, 0);

todayDate.set(Calendar.HOUR_OF_DAY, 0);
todayDate.set(Calendar.MINUTE, 0);
todayDate.set(Calendar.SECOND, 0);
todayDate.set(Calendar.MILLISECOND, 0);

double diff = ((double)expDate.getTimeInMillis()-(double)todayDate.getTimeInMillis())/86400000;

return Math.round(diff);
0
PT_C 29 ene. 2016 a las 16:02

Java.time

La Pregunta y otras Respuestas utilizan clases pasadas de moda. Las antiguas clases de fecha y hora como java.util.Date/.Calendar incluidas con las primeras versiones de Java han demostrado ser bastante problemáticas. Esas clases antiguas han sido reemplazadas por java.time marco en Java 8 y posterior.

Como las otras Respuestas señalan correctamente, el problema es que el inicio long tiene 30 en el lado derecho, lo que excluye un cálculo de día completo.

Definición de conteo de días

Además, debe definir lo que quiere decir con una cuenta de días. ¿Te refieres a un recuento por fecha, por lo que cualquier hora del 3 de enero a cualquier hora del 4 es un día, incluso si las horas fueron un minuto antes y después de la medianoche? ¿O se refiere a un recuento de bloques de tiempo genéricos de 24 horas sin tener en cuenta el hecho de que los días particulares en zonas horarias particulares no siempre tienen una duración de 24 horas debido al horario de verano (DST) y otras anomalías?

Contar días por fecha

Si desea lo primero, cuente por fechas, luego utilice la clase LocalDate (una clase de solo fecha sin hora del día ni zona horaria) y la clase Period (un período de tiempo definido como un recuento de años, meses, días) que se encuentran en java.time.

Defina sus entradas. Utilice long en lugar de int. Estos números aparentemente representan una cuenta de milisegundos desde el primer momento de 1970 en UTC.

long startMilli = 1_453_939_200_030L;
long stopMilli = 1_454_544_000_000L;

Convierta esos números long en objetos Instant, un momento en la línea de tiempo en UTC.

Instant startInstant = Instant.ofEpochMilli ( startMilli );
Instant stopInstant = Instant.ofEpochMilli ( stopMilli );

Defina la zona horaria en la que desea considerar las fechas del calendario. Tenga en cuenta que la zona horaria es crucial para definir fechas. La fecha no es simultáneamente la misma en todo el mundo. La fecha varía según la zona horaria.

ZoneId zoneId = ZoneId.of ( "America/Montreal" );

Aplique esa zona horaria a cada Instant para producir ZonedDateTime.

ZonedDateTime startZdt = ZonedDateTime.ofInstant ( startInstant , zoneId );
ZonedDateTime stopZdt = ZonedDateTime.ofInstant ( stopInstant , zoneId );

Para obtener un Period, necesitamos fechas "locales". Por "local" nos referimos a cualquier localidad en particular, un valor de fecha genérico. La clase LocalDate no contiene zona horaria, pero la zona horaria contenida en ZonedDateTime se aplica al determinar un LocalDate.

LocalDate startLocalDate = startZdt.toLocalDate ();;
LocalDate stopLocalDate = stopZdt.toLocalDate ();

Defina nuestro lapso de tiempo como un recuento de días genéricos, en Period.

Period period = Period.between ( startLocalDate , stopLocalDate );

Interrogue el Period para preguntar el número de días genéricos que contiene.

int days = period.getDays ();

Volcado a la consola.

System.out.println ( "milli: " + startMilli + "/" + stopMilli + " | Instant: " + startInstant + "/" + stopInstant + " | ZonedDateTime: " + startZdt + "/" + stopZdt + " | LocalDate: " + startLocalDate + "/" + stopLocalDate + " | period: " + period + " | days: " + days );

milli: 1453939200030/1454544000000 | Instantáneo: 2016-01-28T00: 00: 00.030Z / 2016-02-04T00: 00: 00Z | ZonedDateTime: 2016-01-27T19: 00: 00.030-05: 00 [América / Montreal] / 2016-02-03T19: 00-05: 00 [América / Montreal] | LocalDate: 2016-01-27 / 2016-02-03 | período: P7D | días: 7

Cuenta de días completos

Si desea contar los días completos, use el {{ X0}} clase de ThreeTen-Extra. Observe en el resultado a continuación que obtenemos un recuento de seis (6) días en lugar de siete (7) como se ve arriba.

ThreeTen-Extra

El proyecto ThreeTen-Extra amplía java.time. Dirigido por las mismas personas que construyeron java.time.

El comportamiento de between no está documentado con claridad. La experimentación muestra que parece basarse en períodos de 24 horas, no en fechas. Reemplaza 030 con 000, y también intenta reemplazar en stopMilli el último 000 con 030, para ver el comportamiento por ti mismo.

Days daysObject = Days.between ( startZdt , stopZdt );
int daysObjectCount = daysObject.getAmount ();

Volcar a la consola. La cadena P6D que ve en la salida se generó de acuerdo con los formatos definidos en ISO 8601 estándar. Este estándar se utiliza de forma predeterminada en java.time para todo el análisis y la generación de representaciones textuales de valores de fecha y hora. Estos formatos estándar son bastante prácticos y útiles, así que echa un vistazo a la página de Wikipedia vinculada.

System.out.println ( "daysObject: " + daysObject + " | daysObjectCount: " + daysObjectCount );

daysObject: P6D | daysObjectCount: 6

0
Basil Bourque 28 ene. 2016 a las 23:47

Hoy = 1453939200030

Los tiempos se dan en milisegundos, y parece que de alguna manera su Date ingresado tiene 30 milisegundos adicionales.

Cuando resto los 30 milisegundos, luego hago los cálculos en una calculadora, obtengo 7 días. Con sus cifras como están, obtengo 6.9999996527777777777777777777778, y en long matemáticas, las cifras decimales se truncan a 6.

También ponga a cero los milisegundos.

expDate.set(Calendar.MILLISECOND, 0);
todayDate.set(Calendar.MILLISECOND, 0);
3
rgettman 28 ene. 2016 a las 21:39

Bueno, como puede ver, no borró los milisegundos, y 1454544000000 - 1453939200030 = 604799970 y dividiendo por 86400000 obtiene 6.99999965277777..., lo que significa 6 cuando se trunca a {{X4 }}.

Ahora, si borra los milisegundos también, hoy se convierte en 1453939200000, lo que le llevará a responder 7.

Nota: Esto no significa que haya terminado, debido al horario de verano. Con DST, una de las marcas de tiempo puede estar a ± 1 hora de la otra, por lo que aún puede tener ese problema de truncamiento.

Esta fue una respuesta a su problema particular. Intente buscar cómo encontrar correctamente los días entre fechas en Java.

5
Andreas 28 ene. 2016 a las 21:39