Calcule el tiempo transcurrido en Java / Groovy

Yo tengo…

Date start = new Date() ... ... ... Date stop = new Date() 

Me gustaría obtener los años, meses, días, horas, minutos y segundos transcurridos entre estas dos fechas.

Voy a refinar la pregunta.

Solo quiero obtener el tiempo transcurrido, como una medida absoluta, es decir, sin tener en cuenta los años bisiestos, los días de cada mes, etc.

Por lo tanto, creo que es imposible obtener los años y meses transcurridos, todo lo que puedo obtener son días, horas, minutos y segundos.

Más específicamente, quiero decir que una determinada tarea duró, por ejemplo,

 20 sec 13 min, 4 sec 2 h, 10 min, 2 sec 4 d, 4 h, 2 min, 2 sec 

Así que por favor perdona mi falta de precisión.

Acabo de descubrir esta solución rápida de Groovy:

 import groovy.time.TimeCategory import groovy.time.TimeDuration TimeDuration td = TimeCategory.minus( stop, start ) println td 

Puedes hacer todo esto con división y mod.

 long l1 = start.getTime(); long l2 = stop.getTime(); long diff = l2 - l1; long secondInMillis = 1000; long minuteInMillis = secondInMillis * 60; long hourInMillis = minuteInMillis * 60; long dayInMillis = hourInMillis * 24; long elapsedDays = diff / dayInMillis; diff = diff % dayInMillis; long elapsedHours = diff / hourInMillis; diff = diff % hourInMillis; long elapsedMinutes = diff / minuteInMillis; diff = diff % minuteInMillis; long elapsedSeconds = diff / secondInMillis; 

Eso debería darle toda la información que solicitó.

EDITAR: Dado que las personas parecen estar confundidas, no, esto no toma en cuenta cosas como los años bisiestos o los interruptores de horario de verano. Es tiempo transcurrido puro, que es lo que opensas pidió.

No es tan fácil con la API de fecha estándar.

Es posible que desee mirar Joda Time o JSR-310 en su lugar.

No soy un experto en Joda, pero creo que el código sería:

 Interval interval = new Interval(d1.getTime(), d2.getTime()); Period period = interval.toPeriod(); System.out.printf( "%d years, %d months, %d days, %d hours, %d minutes, %d seconds%n", period.getYears(), period.getMonths(), period.getDays(), period.getHours(), period.getMinutes(), period.getSeconds()); 

Con respecto a JodaTime, simplemente lo hice funcionar; gracias al respondedor que lo sugirió. Aquí hay una versión más condensada del código Joda sugerido:

 Period period = new Period(d1.getTime(), d2.getTime()); System.out.printf( "%d years, %d months, %d days, %d hours, %d minutes, %d seconds%n", period.getYears(), period.getMonths(), period.getDays(), period.getHours(), period.getMinutes(), period.getSeconds()); 

(no estoy seguro si esto está ayudando a la pregunta original, pero ciertamente buscadores).

Bueno, desde Java 1.5 debes usar TimeUnit.

Aquí hay un ejemplo simple y sencillo para esto. Creo que en Groovy podría ser más corto (como siempre).

 /** * Formats a given {@link Date} to display a since-then timespan. * @param created * @return String with a format like "3 minutes ago" */ public static String getElapsedTime(Date created) { long duration = System.currentTimeMillis() - created.getTime(); long seconds = TimeUnit.MILLISECONDS.toSeconds(duration); long days = TimeUnit.MILLISECONDS.toDays(duration); long hours = TimeUnit.MILLISECONDS.toHours(duration); long minutes = TimeUnit.MILLISECONDS.toMinutes(duration); if (days > 0) { return days + " days"; } if (hours > 0) { return hours + " hrs"; } if (minutes > 0) { return minutes + " minutes"; } return seconds + " seconds"; } 

Ah, y evitar múltiples devoluciones por favor;)

En realidad, con respecto a las respuestas anteriores sobre Joda-Time .

Hay una forma más fácil de hacerlo con la clase Período de Joda-Time:

 Period period = new Period(startDate, endDate); System.out.println(PeriodFormat.getDefault().print(period)); 

Para personalizar el resultado, consulte las clases PeriodFormat , PeriodFormatter y PeriodFormatterBuilder .

tl; dr

 Duration.between( then , Instant.now() ) 

Usando java.time

La forma moderna usa las clases java.time que suplantan a las problemáticas clases antiguas de fecha y hora.

En lugar de Date , usa Instant . La clase Instant representa un momento en la línea de tiempo en UTC con una resolución de nanosegundos (hasta nueve (9) dígitos de una fracción decimal).

 Instant then = Instant.now(); … Instant now = Instant.now(); 

Use la clase Duration por un lapso de tiempo no vinculado a la línea de tiempo, con resolución de día-horas-minutos-segundos-nanos.

 Duration d = Duration.between( then , now ); 

Durante un lapso de tiempo con resolución de años-meses-días, use la clase Period .

Generar una cadena es el formato estándar ISO 8601 para duraciones : PnYnMnDTnHnMnS . La P marca el comienzo. La T separa cualquier año-meses-días de horas-minutos-segundos. Entonces, dos horas y media es PT2H30M .

 String output = d.toString(); 

En Java 9 y toDaysPart posteriores, puede acceder a las partes individuales con métodos para toDaysPart , a toHoursPart , y así sucesivamente.


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 .

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

  • Java SE 8 y SE 9 y posteriores
    • 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 SE 7
    • Gran parte de la funcionalidad de java.time se transfiere a Java 6 y 7 en ThreeTen-Backport .
  • Androide
    • El proyecto ThreeTenABP adapta ThreeTen-Backport (mencionado anteriormente) específicamente para Android.
    • 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 .

Hmm, si obtengo lo que estás preguntando, quieres saber si:

start = 1 de enero de 2001, 10: 00: 00.00 a.m. y

stop = 6 de enero de 2003, 12: 01: 00,000 p.m.

quieres una respuesta de 2 años, 0 meses, 5 días, 2 horas, 1 minuto

Desafortunadamente, esta es una respuesta engañosa. ¿Qué pasa si las fechas fueron el 2 de enero y el 31 de enero? ¿Serían eso 29 días? Ok, pero del 2 de febrero al 2 de marzo son 28 (29) días, ¿pero aparecerían como 1 mes?

El tiempo en cualquier cosa que no sean segundos o posiblemente días es variable sin conocer el contexto, ya que meses y años son de diferentes longitudes. La diferencia entre 2 fechas debe estar en unidades estáticas, que son fácilmente computables desde stop.getTime () – start.getTime () (que es la diferencia en milisegundos)

Además de la gran API JodaTime antes mencionada que recomiendo, la mejor alternativa estándar de Java que puedes tener es java.util.Calendar . Es engorroso trabajar con él (esto es un eufemismo … mire los ejemplos de JodaTime de una línea aquí), pero también puede calcular el tiempo transcurrido con él. La clave importante es que debe usar el Calendar#add() en un bucle para obtener el valor transcurrido de años, meses y días para tener en cuenta días bisiestos, años y siglos. No debe calcularlos desde los (mil) segundos.

Aquí hay un ejemplo básico:

 import java.util.Calendar; public class Test { public static void main(String[] args) throws Exception { Calendar start = Calendar.getInstance(); start.set(1978, 2, 26, 12, 35, 0); // Just an example. Calendar end = Calendar.getInstance(); Integer[] elapsed = new Integer[6]; Calendar clone = (Calendar) start.clone(); // Otherwise changes are been reflected. elapsed[0] = elapsed(clone, end, Calendar.YEAR); clone.add(Calendar.YEAR, elapsed[0]); elapsed[1] = elapsed(clone, end, Calendar.MONTH); clone.add(Calendar.MONTH, elapsed[1]); elapsed[2] = elapsed(clone, end, Calendar.DATE); clone.add(Calendar.DATE, elapsed[2]); elapsed[3] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 3600000; clone.add(Calendar.HOUR, elapsed[3]); elapsed[4] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 60000; clone.add(Calendar.MINUTE, elapsed[4]); elapsed[5] = (int) (end.getTimeInMillis() - clone.getTimeInMillis()) / 1000; System.out.format("%d years, %d months, %d days, %d hours, %d minutes, %d seconds", elapsed); } private static int elapsed(Calendar before, Calendar after, int field) { Calendar clone = (Calendar) before.clone(); // Otherwise changes are been reflected. int elapsed = -1; while (!clone.after(after)) { clone.add(field, 1); elapsed++; } return elapsed; } } 

Debería imprimir mi edad a partir de ahora =)

Ah, debería agregar, puede “convertir” la Date en el Calendar usando Calendar#setTime() .

Es fácil; Debes establecer la zona horaria correcta

 import java.util.TimeZone; import java.util.logging.Logger; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; public class ImportData { private final static Logger log = Logger.getLogger(ImportData.class.getName()); private final static DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS"); private final static DateTimeFormatter dtfh = DateTimeFormat.forPattern("HH:mm:ss.SSS"); /** * @param args the command line arguments */ public static void main(String[] args) throws Exception { TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin")); DateTimeZone.setDefault(DateTimeZone.forID("Europe/Berlin")); // Quotes connection=Quotes.getInstance(); final long start = System.currentTimeMillis(); // do something here ... // connection.importTickdata(); Thread.currentThread().sleep(2000); final long end = System.currentTimeMillis(); log.info("[start] " + dtf.print(start)); log.info("[end] " + dtf.print(end)); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); DateTimeZone.setDefault(DateTimeZone.forID("UTC")); log.info("[duration] " + dtfh.print(end - start)); // connection.logOff(); // System.exit(0); } 

devoluciones:

 10.11.2010 00:08:12 ImportData principal
 INFORMACIÓN: [inicio] 2010-11-10 00: 08: 10.306
 10.11.2010 00:08:12 ImportData principal
 INFORMACIÓN: [fin] 2010-11-10 00: 08: 12.318
 10.11.2010 00:08:12 ImportData principal
 INFORMACIÓN: [duración] 00: 00: 02.012

No tiene sentido crear un objeto Date, ya que todo esto es wrap System.currentTimeMillis (). La función getTime () solo desenvuelve el objeto Date. Sugiero que solo use esta función para obtener un largo.

Si solo necesitas una segunda precisión, está bien. Sin embargo, si quiere una precisión inferior a milisegundos, use System.nanoTime () para obtener el tiempo transcurrido.

Esta es una función completa que implementé basada en la respuesta de Sebastian Celis . Y nuevamente, desde su publicación, esto no toma en cuenta cosas como los años bisiestos o los interruptores de horario de verano. Es tiempo puro transcurrido .

La salida se adapta a mi necesidad. Esto solo produce tres secciones significativas. En lugar de regresar

4 meses, 2 semanas, 3 días, 7 horas, 28 minutos, 43 segundos

Simplemente devuelve el primer bloque de 3 (ver más ejecución de muestra al final de la publicación) :

4 meses, 2 semanas, 3 días

Aquí está el código fuente del método completo:

 /** * Format milliseconds to elapsed time format * * @param time difference in milliseconds * @return Human readable string representation - eg. 2 days, 14 hours, 5 minutes */ public static String formatTimeElapsedSinceMillisecond(long milisDiff) { if(milisDiff<1000){ return "0 second";} String formattedTime = ""; long secondInMillis = 1000; long minuteInMillis = secondInMillis * 60; long hourInMillis = minuteInMillis * 60; long dayInMillis = hourInMillis * 24; long weekInMillis = dayInMillis * 7; long monthInMillis = dayInMillis * 30; int timeElapsed[] = new int[6]; // Define time units - plural cases are handled inside loop String timeElapsedText[] = {"second", "minute", "hour", "day", "week", "month"}; timeElapsed[5] = (int) (milisDiff / monthInMillis); // months milisDiff = milisDiff % monthInMillis; timeElapsed[4] = (int) (milisDiff / weekInMillis); // weeks milisDiff = milisDiff % weekInMillis; timeElapsed[3] = (int) (milisDiff / dayInMillis); // days milisDiff = milisDiff % dayInMillis; timeElapsed[2] = (int) (milisDiff / hourInMillis); // hours milisDiff = milisDiff % hourInMillis; timeElapsed[1] = (int) (milisDiff / minuteInMillis); // minutes milisDiff = milisDiff % minuteInMillis; timeElapsed[0] = (int) (milisDiff / secondInMillis); // seconds // Only adds 3 significant high valued units for(int i=(timeElapsed.length-1), j=0; i>=0 && j<3; i--){ // loop from high to low time unit if(timeElapsed[i] > 0){ formattedTime += ((j>0)? ", " :"") + timeElapsed[i] + " " + timeElapsedText[i] + ( (timeElapsed[i]>1)? "s" : "" ); ++j; } } // end for - build string return formattedTime; } // end of formatTimeElapsedSinceMillisecond utility method 

Aquí hay algunos ejemplos de statement de prueba:

 System.out.println(formatTimeElapsedSinceMillisecond(21432424234L)); // Output: 8 months, 1 week, 1 day System.out.println(formatTimeElapsedSinceMillisecond(87724294L)); // Output: 1 day, 22 minutes, 4 seconds System.out.println(formatTimeElapsedSinceMillisecond(123434L)); // Output: 2 minutes, 3 seconds 

Esta es otra función similar, no aparecerá días, horas, minutos, etc. si no es necesario, cambie sus literales si es necesario.

 public class ElapsedTime { public static void main(String args[]) { long start = System.currentTimeMillis(); start -= (24 * 60 * 60 * 1000 * 2); start -= (60 * 60 * 1000 * 2); start -= (60 * 1000 * 3); start -= (1000 * 55); start -= 666; long end = System.currentTimeMillis(); System.out.println(elapsedTime(start, end)); } public static String elapsedTime(long start, long end) { String auxRet = ""; long aux = end - start; long days = 0, hours = 0, minutes = 0, seconds = 0, milliseconds = 0; // days if (aux > 24 * 60 * 60 * 1000) { days = aux / (24 * 60 * 60 * 1000); } aux = aux % (24 * 60 * 60 * 1000); // hours if (aux > 60 * 60 * 1000) { hours = aux / (60 * 60 * 1000); } aux = aux % (60 * 60 * 1000); // minutes if (aux > 60 * 1000) { minutes = aux / (60 * 1000); } aux = aux % (60 * 1000); // seconds if (aux > 1000) { seconds = aux / (1000); } milliseconds = aux % 1000; if (days > 0) { auxRet = days + " days "; } if (days != 0 || hours > 0) { auxRet += hours + " hours "; } if (days != 0 || hours != 0 || minutes > 0) { auxRet += minutes + " minutes "; } if (days != 0 || hours != 0 || minutes != 0 || seconds > 0) { auxRet += seconds + " seconds "; } auxRet += milliseconds + " milliseconds "; return auxRet; } } 

Este es un problema y es necesario realizar un algoritmo para tener en cuenta los años bisiestos y la cantidad exacta de meses y días al año. Es interesante cómo es simple si solo se va a usar una unidad de medida. Por ejemplo, el número total de días entre dos días es correcto, ya que está relacionado con el número de recordatorio de días después de calcular el número de meses y años dentro de, digamos, dos décadas.

Actualmente estoy trabajando en esto para proporcionarlo desde mi implementación de PML, por ejemplo, en la forma de:

 unemployed < - date.difference[ From = 2009-07-01, Till = now, YEARS, MONTHS, DAYS ]: yyyy-MM-dd $$-> *unemployed -> date.translate[ YEARS, MONTHS, DAYS ] -> print["Unemployed for:", .] 

Por supuesto, esto también sería útil y necesario para los cálculos exactos de la tasa de interés.