Java 8: diferencia entre dos LocalDateTime en varias unidades
Estoy tratando de calcular la diferencia entre dos.LocalDateTime
.
La salida debe tener el formato y years m months d days h hours m minutes s seconds
. Esto es 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 obtengo es 29 years 8 months 24 days 12 hours 0 minutes 50 seconds
. He comprobado 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 son incorrectos en mi código. Cualquier sugerencia sería de gran ayuda.
Actualizar
Probé mi resultado en otro sitio web y el resultado que obtuve es diferente. Aquí está: Calcular 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 qué sitio me dio qué resultado, necesito conocer el algoritmo correcto y obtener los resultados correctos.
Descubrí que la mejor manera de hacer esto es con ChronoUnit.
long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);
long hours = ChronoUnit.HOURS.between(fromDate, toDate);
La documentación adicional está aquí: Periodo y Duración
Desafortunadamente, no parece haber una clase periódica que abarque también el tiempo, por lo que es posible que tengas que hacer los cálculos por tu cuenta.
Afortunadamente, las clases de fecha y hora tienen muchos métodos de utilidad que lo simplifican hasta cierto punto. Aquí tienes una forma de calcular la diferencia, aunque no necesariamente la más rápida:
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);
LocalDateTime tempDateTime = LocalDateTime.from( fromDateTime );
long years = tempDateTime.until( toDateTime, ChronoUnit.YEARS );
tempDateTime = tempDateTime.plusYears( years );
long months = tempDateTime.until( toDateTime, ChronoUnit.MONTHS );
tempDateTime = tempDateTime.plusMonths( months );
long days = tempDateTime.until( toDateTime, ChronoUnit.DAYS );
tempDateTime = tempDateTime.plusDays( days );
long hours = tempDateTime.until( toDateTime, ChronoUnit.HOURS );
tempDateTime = tempDateTime.plusHours( hours );
long minutes = tempDateTime.until( toDateTime, ChronoUnit.MINUTES );
tempDateTime = tempDateTime.plusMinutes( minutes );
long seconds = tempDateTime.until( toDateTime, ChronoUnit.SECONDS );
System.out.println( years + " years " +
months + " months " +
days + " days " +
hours + " hours " +
minutes + " minutes " +
seconds + " seconds.");
//prints: 29 years 8 months 24 days 22 hours 54 minutes 50 seconds.
La idea básica es la siguiente: crear una fecha de inicio temporal y completar los años hasta el final. Luego ajuste esa fecha por la cantidad de años para que la fecha de inicio sea menos de un año desde el final. Repita esto para cada unidad de tiempo en orden descendente.
Finalmente, un descargo de responsabilidad : no tomé en cuenta diferentes zonas horarias (ambas fechas deben estar en la misma zona horaria) y tampoco probé/verifiqué cómo cambia el horario de verano u otros cambios en un calendario (como los cambios de zona horaria en Samoa) afectar este cálculo. Así que úselo con cuidado.
¡Debería ser más sencillo!
Duration.between(startLocalDateTime, endLocalDateTime).toMillis();
Puedes convertir milisegundos a la unidad que quieras:
String.format("%d minutes %d seconds",
TimeUnit.MILLISECONDS.toMinutes(millis),
TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
A continuación se muestra un ejemplo que utiliza Duración y TimeUnit para obtener el formato 'hh:mm:ss'.
Duration dur = Duration.between(localDateTimeIni, localDateTimeEnd);
long millis = dur.toMillis();
String.format("%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis) -
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));