Estoy tratando de calcular la diferencia entre dos LocalDateTime
.
La salida debe ser del formato y years m months d days h hours m minutes s seconds
. Aquí está lo que he escrito:
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
public class Main {
static final int MINUTES_PER_HOUR = 60;
static final int SECONDS_PER_MINUTE = 60;
static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
public static void main(String[] args) {
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 9, 19, 46, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
Period period = getPeriod(fromDateTime, toDateTime);
long time[] = getTime(fromDateTime, toDateTime);
System.out.println(period.getYears() + " years " +
period.getMonths() + " months " +
period.getDays() + " days " +
time[0] + " hours " +
time[1] + " minutes " +
time[2] + " seconds.");
}
private static Period getPeriod(LocalDateTime dob, LocalDateTime now) {
return Period.between(dob.toLocalDate(), now.toLocalDate());
}
private static long[] getTime(LocalDateTime dob, LocalDateTime now) {
LocalDateTime today = LocalDateTime.of(now.getYear(),
now.getMonthValue(), now.getDayOfMonth(), dob.getHour(), dob.getMinute(), dob.getSecond());
Duration duration = Duration.between(today, now);
long seconds = duration.getSeconds();
long hours = seconds / SECONDS_PER_HOUR;
long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
long secs = (seconds % SECONDS_PER_MINUTE);
return new long[]{hours, minutes, secs};
}
}
El resultado que estoy obteniendo es 29 years 8 months 24 days 12 hours 0 minutes 50 seconds
. He verificado mi resultado de este sitio web (con valores 12/16/1984 07:45:55
y 09/09/2014 19:46:45
). La siguiente captura de pantalla muestra el resultado:
Estoy bastante seguro de que los campos después del valor del mes salen mal de mi código. Cualquier sugerencia sería de gran ayuda.
Actualizar
He probado mi resultado de otro sitio web y el resultado que obtuve es diferente. Aquí está: Calcule la duración entre dos fechas (resultado: 29 años, 8 meses, 24 días, 12 horas, 0 minutos y 50 segundos).
Actualizar
Como obtuve dos resultados diferentes de dos sitios diferentes, me pregunto si el algoritmo de mi cálculo es legítimo o no. Si uso los siguientes dos LocalDateTime
objetos:
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
Entonces viene la salida: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.
Desde este enlace debería ser 29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds
. Entonces el algoritmo también necesita manejar los números negativos.
Tenga en cuenta que la pregunta no es sobre qué sitio me dio qué resultado, necesito saber el algoritmo correcto y tener los resultados correctos.
Period.between()
aplicar algún redondeo?7:45:55
y la hora de finalización19:46:45
(o7:46:45
PM). Entonces, la diferencia entre esos dos tiempos es de 12 horas, 0 minutos y 50 segundos y nunca 23 horas, 34 minutos y 12 segundos. Por lo tanto, su cálculo actual parece ser correcto, al menos en la parte del tiempo.LocalDateTime
no tiene zona horaria, es posible que no haya una respuesta única. Incluso si asume que las zonas horarias de inicio y finalización son las mismas, en ciertas zonas las fechas como 2014-09-09 serán en horario de verano o en horario de verano y en otras no. Esto podría retrasar las cosas por una hora. Por lo tanto, calcular la diferencia con el segundo no tiene sentido a menos que esto se resuelva.LocalDateTime
arroja resultados poco realistas, ya que esa clase carece deliberadamente de cualquier concepto de zona horaria o compensación desde UTC? Para valores realistas, asigne una zona horaria a travésZoneId
del usoZonedDateTime
.Respuestas:
Desafortunadamente, no parece haber una clase de período que abarque también el tiempo, por lo que es posible que tenga que hacer los cálculos por su cuenta.
Afortunadamente, las clases de fecha y hora tienen muchos métodos de utilidad que simplifican eso hasta cierto punto. Aquí hay una manera de calcular la diferencia, aunque no necesariamente la más rápida:
La idea básica es esta: cree una fecha de inicio temporal y obtenga los años completos hasta el final. Luego ajuste esa fecha por el número de años para que la fecha de inicio sea inferior a un año desde el final. Repita eso para cada unidad de tiempo en orden descendente.
Finalmente, un descargo de responsabilidad : no tomé en cuenta diferentes zonas horarias (ambas fechas deberían estar en la misma zona horaria) y tampoco probé / verifiqué cómo el horario de verano u otros cambios en un calendario (como la zona horaria cambia en Samoa) afectar este cálculo Así que úsalo con cuidado.
fuente
ChronoUnit
:) pero haciendo el trabajo "a mano".long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);
, devolvería un número mayor que 59 para cualquier intervalo de al menos 1 hora, y eso no es lo que quiere el OP. Teniendo eso en cuenta, no podrás hacer 6 llamadasChronoUnit#between()
y terminar.Encontré que la mejor manera de hacerlo es con ChronoUnit.
Documentación adicional está aquí: https://docs.oracle.com/javase/tutorial/datetime/iso/period.html
fuente
tempDateTime.until( toDateTime, ChronoUnit.YEARS)
conChronoUnit.YEARS.between(fromDate, toDate)
. Pero las líneas adicionales importantes comotempDateTime.plusYears( years )
etc. faltan por completo en su respuesta, por lo que no ayuda al OP. ¡El algoritmo completo importa!Aquí hay un solo ejemplo que usa Duration y TimeUnit para obtener el formato 'hh: mm: ss'.
fuente
String.format("%02d:%02d:%02d",dur.toHoursPart(), dur.toMinutesPart(), dur.toSecondsPart());
. Los métodos de parte le dan los números correctos para construir una cadena. Creo que es más legible.¡Debería ser más simple!
fuente
TL; DR
y luego utilizar los métodos
period.getYears()
,period.getMonths()
,period.getDays()
,duration.toHoursPart()
,duration.toMinutesPart()
,duration.toSecondsPart()
.Respuesta ampliada
Contestaré la pregunta original, es decir, cómo obtener la diferencia de tiempo entre dos
LocalDateTimes
en años, meses, días, horas y minutos, de modo que la "suma" (ver nota a continuación) de todos los valores para las diferentes unidades sea igual al total diferencia temporal, y tal que el valor en cada unidad es menor que el siguiente más grande unidad, es decirminutes < 60
,hours < 24
, y así sucesivamente.Dado dos
LocalDateTimes
start
yend
, por ejemplopodemos representar el intervalo de tiempo absoluto entre los dos con un — tal vez
Duration
usandoDuration.between(start, end)
. Pero la unidad más grande que podemos extraer de unDuration
día son los días (como una unidad temporal equivalente a 24 h); consulte la nota a continuación para obtener una explicación. Para usar unidades más grandes (meses, años) podemos representar estoDuration
con un par de (Period
,Duration
), dondePeriod
mide la diferencia hasta una precisión de días yDuration
representa el resto:Ahora podemos simplemente usar los métodos definidos en
Period
yDuration
para extraer las unidades individuales:o, usando el formato predeterminado:
Nota sobre años, meses y días
Tenga en cuenta que, según
java.time
la concepción, las "unidades" como "mes" o "año" no representan un valor temporal absoluto fijo; dependen de la fecha y el calendario, como lo ilustra el siguiente ejemplo:fuente
Hay algún problema para el código Tapas Bose y el código Thomas. Si la diferencia de tiempo es negativa, la matriz obtiene los valores negativos. Por ejemplo si
devuelve 0 años 0 meses 1 días -1 horas 0 minutos 0 segundos.
Creo que la salida correcta es: 0 años 0 meses 0 días 23 horas 0 minutos 0 segundos.
Propongo separar las instancias LocalDateTime en las instancias LocalDate y LocalTime. Después de eso, podemos obtener las instancias de período y duración de Java 8. La instancia de Duración se separa en el número de días y el valor de tiempo a lo largo del día (<24 h) con la corrección posterior del valor del período. Cuando el segundo valor de LocalTime es anterior al primer valor de LocalTime, es necesario reducir el período de un día.
Aquí está mi manera de calcular la diferencia LocalDateTime:
El método anterior se puede usar para calcular la diferencia de cualquier valor de fecha y hora local, por ejemplo:
Es conveniente escribir una prueba unitaria para el método anterior (ambos son miembros de la clase PeriodDuration). Aquí está el código:
}
Todas las pruebas tienen éxito independientemente de si el valor del primer LocalDateTime es anterior y para cualquier valor de LocalTime.
fuente
java.time.Period
donde los usuarios pueden hacer cumplir / producir tales signos mixtos debido a un diseño interno diferente).Y la versión de @Thomas en Groovy con toma las unidades deseadas en una lista en lugar de codificar los valores. Esta implementación (que puede portarse fácilmente a Java, hice explícita la declaración de la función) hace que el enfoque de Thomas sea más reutilizable.
Al momento de escribir esto, el código anterior regresa
47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis
. Y, para la entrada de @Gennady Kolomoets, el código vuelve23 Hours
.Cuando proporciona una lista de unidades, debe ordenarse por tamaño de las unidades (la más grande primero):
fuente
Aquí hay una respuesta muy simple a su pregunta. Funciona.
fuente
Age is: 0 years,0 months, 1 days, -10 hours, -48 minutes old
. No creo que esto sea lo que se deseaba.Joda-Time
Como muchas de las respuestas requerían compatibilidad con API 26 y mi API mínima era 23, lo resolví con el siguiente código:
fuente
Después de más de cinco años respondo mi pregunta. Creo que el problema con una duración negativa se puede resolver con una simple corrección:
Nota: El sitio https://www.epochconverter.com/date-difference ahora calcula correctamente la diferencia horaria.
Gracias a todos por su discusión y sugerencias.
fuente
toHours
,toMinutes
ytoSeconds
los métodos deDuration
. Desde Java 9 aún más desdetoMinutesPart()
ytoSecondsPart()
. Y no veo el punto de envolver las horas, minutos y segundos en una matriz solo para eliminarlos nuevamente. En general, una buena respuesta, sin embargo.