¿Cómo mido el tiempo transcurrido en Java?

Quiero tener algo como esto:

public class Stream { public startTime; public endTime; public getDuration() { return startTime - endTime; } } 

También es importante que, por ejemplo, si startTime es 23:00 y endTime 1:00 para obtener una duración de 2:00.

¿Qué tipos usar para lograr esto en Java?

Desafortunadamente, ninguna de las diez respuestas publicadas hasta ahora es correcta.

Si está midiendo el tiempo transcurrido y desea que sea correcto , debe usar System.nanoTime() . No puede usar System.currentTimeMillis() , a menos que no le importe que su resultado sea incorrecto.

El propósito de nanoTime es medir el tiempo transcurrido , y el propósito de currentTimeMillis es medir el tiempo del reloj de pared . No puedes usar el uno para el otro propósito. La razón es que el reloj de ninguna computadora es perfecto; siempre se desplaza y de vez en cuando necesita ser corregido. Esta corrección puede ocurrir manualmente o, en el caso de la mayoría de las máquinas, hay un proceso que se ejecuta y continuamente emite pequeñas correcciones al reloj del sistema (“reloj de pared”). Estos tienden a suceder a menudo. Otra corrección de este tipo ocurre cada vez que hay un segundo intercalar.

Como el propósito de nanoTime es medir el tiempo transcurrido, no se ve afectado por ninguna de estas pequeñas correcciones. Es lo que quieres usar Todos los tiempos actualmente en curso con currentTimeMillis estarán desactivados, posiblemente incluso serán negativos.

Usted puede decir: “esto no parece que realmente importe mucho”, a lo que le digo, tal vez no, pero en general, ¿el código correcto no es mejor que el código incorrecto? Además, nanoTime es más corto para escribir de todos modos.

Los descargos de responsabilidad publicados con anterioridad sobre nanoTime suelen tener una precisión de microsegundos son válidos. También puede tardar más de un microsegundo entero en invocarse, dependiendo de las circunstancias (como puede ser el otro), por lo tanto, no espere progtwigr intervalos muy pequeños de manera correcta.

¿Qué tipos usar para lograr esto en Java?

La respuesta corta es long . Ahora, más sobre cómo medir …

System.currentTimeMillis ()

La forma “tradicional” de hacer esto es utilizar System.currentTimeMillis() :

 long startTime = System.currentTimeMillis(); // ... do something ... long estimatedTime = System.currentTimeMillis() - startTime; 

oacltStopWatch

Tenga en cuenta que Commons Lang tiene una clase StopWatch que se puede usar para medir el tiempo de ejecución en milisegundos. Tiene métodos métodos como split() , suspend() , resume() , etc. que permiten medir en diferentes puntos de la ejecución y que puede ser conveniente. Échale un vistazo.

System.nanoTime ()

Es posible que prefiera utilizar System.nanoTime() si está buscando mediciones extremadamente precisas del tiempo transcurrido. De su javadoc:

 long startTime = System.nanoTime(); // ... the code being measured ... long estimatedTime = System.nanoTime() - startTime; 

Jamon

Otra opción sería usar JAMon , una herramienta que reúne estadísticas (tiempo de ejecución, número de aciertos, tiempo promedio de ejecución, mínimo, máximo, etc.) para cualquier código que entre los métodos start () y stop (). A continuación, un ejemplo muy simple:

 import com.jamonapi.*; ... Monitor mon=MonitorFactory.start("myFirstMonitor"); ...Code Being Timed... mon.stop(); 

Vea este artículo en http://www.javaperformancetunning.com para una buena introducción.

Usando AOP

Finalmente, si no quiere saturar su código con estas medidas (o si no puede cambiar el código existente), entonces AOP sería un arma perfecta. No voy a discutir esto muy profundamente, pero quería al menos mencionarlo.

A continuación, un aspecto muy simple usando AspectJ y JAMon (aquí, el nombre corto del punto de corte se usará para el monitor JAMon, de ahí la llamada a thisJoinPoint.toShortString() ):

 public aspect MonitorAspect { pointcut monitor() : execution(* *.ClassToMonitor.methodToMonitor(..)); Object arround() : monitor() { Monitor monitor = MonitorFactory.start(thisJoinPoint.toShortString()); Object returnedObject = proceed(); monitor.stop(); return returnedObject; } } 

La definición de pointcut podría adaptarse fácilmente para monitorear cualquier método basado en el nombre de clase, el nombre del paquete, el nombre del método o cualquier combinación de estos. La medición es realmente un caso de uso perfecto para AOP.

Tu nueva clase:

 public class TimeWatch { long starts; public static TimeWatch start() { return new TimeWatch(); } private TimeWatch() { reset(); } public TimeWatch reset() { starts = System.currentTimeMillis(); return this; } public long time() { long ends = System.currentTimeMillis(); return ends - starts; } public long time(TimeUnit unit) { return unit.convert(time(), TimeUnit.MILLISECONDS); } } 

Uso:

  TimeWatch watch = TimeWatch.start(); // do something long passedTimeInMs = watch.time(); long passedTimeInSeconds = watch.time(TimeUnit.SECONDS); 

Después, el tiempo transcurrido se puede convertir al formato que desee, con un calendario por ejemplo

Greetz, GHad

Si el propósito es simplemente imprimir información aproximada de tiempo en los registros de su progtwig, entonces la solución fácil para los proyectos de Java no es escribir su propio cronómetro o clases de temporizador, sino simplemente usar la clase org.apache.commons.lang.time.StopWatch que es parte de Apache Commons Lang.

 final StopWatch stopwatch = new StopWatch(); stopwatch.start(); LOGGER.debug("Starting long calculations: {}", stopwatch); ... LOGGER.debug("Time after key part of calcuation: {}", stopwatch); ... LOGGER.debug("Finished calculating {}", stopwatch); 

tl; dr

por ejemplo, si startTime es 23:00 y endTime 1:00 para obtener una duración de 2:00.

Imposible. Si solo tiene la hora del día, el reloj se detiene a la medianoche. Sin el contexto de las fechas, ¿cómo sabemos si se refiere a la 1 AM del día siguiente, la próxima semana o la próxima década?

Entonces, ir de las 11 p.m. a la 1 a.m. significa retroceder en el tiempo 22 horas, corriendo las manecillas del reloj en sentido antihorario. Ver el resultado a continuación, un negativo de veintidós horas.

 Duration.between( // Represent a span of time a total number of seconds plus a fractional second in nanoseconds. LocalTime.of( 23 , 0 ) , // A time-of-day without a date and without a time zone. LocalTime.of( 1 , 0 ) // A time-of-day clock stops at midnight. So getting to 1 AM from 11 PM means going backwards 22 hours. ) // Return a `Duration` object. .toString() // Generate a `String` representing this span of time using standard ISO 8601 format: PnYnMnDTnHnMnS 

PT-22H

Cruzar la medianoche requiere un contexto más amplio de fecha además de la hora del día (ver a continuación).

¿Cómo mido el tiempo transcurrido en Java?

  1. Capture el momento actual en UTC, con Instant.now() .
  2. Captura otro momento más tarde.
  3. Pase ambos a Duration.between .
  4. (a) A partir del objeto Duration resultante, extraiga un número de 24 horas al día, horas, minutos, segundos y fracciones de segundo en nanosegundos llamando a los diversos to…Part Métodos de to…Part .
    (b) O bien, llame a String para generar una String en el formato estándar ISO 8601 de PnYnMnDTnHnMnS .

Código de ejemplo, usando un par de objetos Instant .

 Duration.between( // Represent a span of time a total number of seconds plus a fractional second in nanoseconds. then , // Some other `Instant` object, captured earlier with `Instant.now()`. Instant.now() // Capture the current moment in UTC with a resolution as fine as nanoseconds, depending on the limits of your host computer hardware clock and operating system. Generally you will get current moment in microseconds (six decimal digits of fractional second) in Java 9, 10, and 11, but only milliseconds in Java 8. ) // Return a `Duration` object. .toString() // Generate a `String` representing this span of time using standard ISO 8601 format: PnYnMnDTnHnMnS 

PT3M27.602197S

Nueva tecnología en Java 8+

Tenemos una nueva tecnología para esto ahora integrada en Java 8 y posterior, el marco java.time .

java.time

El framework java.time está definido por JSR 310 , inspirado en el exitoso proyecto Joda-Time , extendido por el proyecto ThreeTen-Extra , y descrito en el Tutorial de Oracle .

Las antiguas clases de fecha y hora como java.util.Date/.Calendar incluidas con las versiones más antiguas de Java han demostrado estar mal diseñadas, confusas y problemáticas. Ellos son suplantados por las clases java.time.

Resolución

Otras respuestas discuten la resolución.

Las clases java.time tienen una resolución de nanosegundos , hasta nueve dígitos de una fracción decimal de segundo. Por ejemplo, 2016-03-12T04:29:39.123456789Z .

Tanto las antiguas clases java.util.Date/.Calendar como las clases Joda-Time tienen una resolución de milisegundos (3 dígitos de fracción). Por ejemplo, 2016-03-12T04:29:39.123Z .

En Java 8, el momento actual se obtiene con una resolución de solo milisegundos debido a un problema heredado. En Java 9 y posteriores, la hora actual se puede determinar con una resolución de nanosegundos siempre que el reloj de hardware de su computadora funcione tan finamente.

Hora del día

Si realmente desea trabajar solo con la hora del día que carece de cualquier fecha u huso horario, use la clase LocalTime .

 LocalTime sooner = LocalTime.of ( 17, 00 ); LocalTime later = LocalTime.of ( 19, 00 ); 

Una Duration representa un lapso de tiempo en términos de un conteo de segundos más nanosegundos.

 Duration duration = Duration.between ( sooner, later ); 

Volcado a la consola.

 System.out.println ( "sooner: " + sooner + " | later: " + later + " | duration: " + duration ); 

antes: 17:00 | después: 19:00 | duración: PT2H

ISO 8601

Observe que la salida predeterminada de Duration::toString está en formato estándar ISO 8601 . En este formato, la P marca el comienzo (como en ‘Período’), y la T separa cualquier porción de años-meses-días de la porción de horas-minutos-segundos.

Cruzando la medianoche

Desafortunadamente, trabajar con la hora del día solo es complicado cuando te envuelves en el reloj cruzando la medianoche. La clase LocalTime maneja esto asumiendo que desea retroceder a un punto anterior del día.

Usar el mismo código que el anterior pero ir de 23:00 a 01:00 da como resultado veintidós horas negativas ( PT-22H ).

 LocalTime sooner = LocalTime.of ( 23, 0 ); LocalTime later = LocalTime.of ( 1, 0 ); 

antes: 23:00 | más tarde: 01:00 | duración: PT-22H

Fecha y hora

Si tiene la intención de cruzar la medianoche, probablemente tenga sentido que trabaje con valores de fecha y hora en lugar de valores de solo hora del día.

Zona horaria

La zona horaria es crucial para las fechas. Por lo tanto, especificamos tres elementos: (1) la fecha deseada, (2) la hora del día deseada y (3) la zona horaria como un contexto para interpretar esa fecha y hora. Aquí elegimos arbitrariamente el huso horario del área de Montreal .

Si define la fecha solo mediante un desplazamiento desde UTC , use un ZoneOffset con un OffsetDateTime . Si tiene una zona horaria completa (compensación más reglas para manejar anomalías como el horario de verano), use un ZoneId con un ZonedDateTime .

 LocalDate localDate = LocalDate.of ( 2016, 1, 23 ); ZoneId zoneId = ZoneId.of ( "America/Montreal" ); ZonedDateTime sooner = ZonedDateTime.of ( localDate, LocalTime.of ( 23, 0 ), zoneId ); 

Especificamos la hora posterior como el día siguiente a la 1:00 a.m.

 ZonedDateTime later = ZonedDateTime.of ( localDate.plusDays ( 1 ), LocalTime.of ( 1, 0 ), zoneId ); 

Calculamos la Duration de la misma manera que se ve arriba. Ahora obtenemos las dos horas esperadas por esta Pregunta.

 Duration duration = Duration.between ( sooner, later ); 

Volcado a la consola.

 System.out.println ( "sooner: " + sooner + " | later: " + later + " | duration: " + duration ); 

antes: 2016-01-23T23: 00-05: 00 [America / Montreal] | luego: 2016-01-24T01: 00-05: 00 [America / Montreal] | duración: PT2H

Horario de verano

Si los tiempos de las fechas involucraran el horario de verano (DST) u otra anomalía similar, las clases de java.time se ajustarían según sea necesario. Lee el doc de la clase para más detalles.


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 .

Java proporciona el método estático System.currentTimeMillis() . Y eso está devolviendo un valor largo, por lo que es una buena referencia. Muchas otras clases aceptan un parámetro ‘timeInMillis’ que también es largo.

Y a mucha gente le resulta más fácil usar la biblioteca Joda Time para hacer cálculos de fechas y horas.

¿Qué tipos usar para lograr esto en Java?

Respuesta : larga

 public class Stream { public long startTime; public long endTime; public long getDuration() { return endTime - startTime; } // I would add public void start() { startTime = System.currentTimeMillis(); } public void stop() { endTime = System.currentTimeMillis(); } } 

Uso:

  Stream s = .... s.start(); // do something for a while s.stop(); s.getDuration(); // gives the elapsed time in milliseconds. 

Esa es mi respuesta directa para tu primera pregunta.

Para la última “nota” le sugiero que use Joda Time. Contiene una clase de intervalo adecuada para lo que necesita.

Cabe resaltar que

  • System.currentTimeMillis () tiene solo una precisión de milisegundos en el mejor de los casos. A valor, puede ser de 16 ms en algunos sistemas de Windows. Tiene un costo menor que las alternativas <200 ns.
  • System.nanoTime () solo es microsegundo preciso en la mayoría de los sistemas y puede saltar en los sistemas de Windows en 100 microsegundos (es decir, a veces no es tan preciso como parece)
  • El calendario es una forma muy costosa de calcular el tiempo. (Puedo pensar aparte de XMLGregorianCalendar) A veces es la solución más adecuada, pero tenga en cuenta que solo debe realizar largos intervalos de tiempo.

Si está escribiendo una aplicación que debe lidiar con la duración del tiempo, eche un vistazo a Joda-Time, que tiene una clase específica para el manejo de Duraciones, Intervalos y Períodos. Su método getDuration() parece que podría devolver un Intervalo Joda-Time:

 DateTime start = new DateTime(2004, 12, 25, 0, 0, 0, 0); DateTime end = new DateTime(2005, 1, 1, 0, 0, 0, 0); public Interval getInterval() { Interval interval = new Interval(start, end); } 

Si prefiere usar la API de calendario de Java, puede probar esto,

 Date startingTime = Calendar.getInstance().getTime(); //later on Date now = Calendar.getInstance().getTime(); long timeElapsed = now.getTime() - startingTime.getTime(); 

Si obtiene sus marcas de tiempo de System.currentTimeMillis() , entonces sus variables de tiempo deberían ser largas.

 Byte Stream Reader Elapsed Time for 23.7 MB is 96 secs import java.io.*; import java.io.IOException; import java.util.Scanner; class ElaspedTimetoCopyAFileUsingByteStream { private long startTime = 0; private long stopTime = 0; private boolean running = false; public void start() { this.startTime = System.currentTimeMillis(); this.running = true; } public void stop() { this.stopTime = System.currentTimeMillis(); this.running = false; } public long getElapsedTime() { long elapsed; if (running) { elapsed = (System.currentTimeMillis() - startTime); } else { elapsed = (stopTime - startTime); } return elapsed; } public long getElapsedTimeSecs() { long elapsed; if (running) { elapsed = ((System.currentTimeMillis() - startTime) / 1000); } else { elapsed = ((stopTime - startTime) / 1000); } return elapsed; } public static void main(String[] args) throws IOException { ElaspedTimetoCopyAFileUsingByteStream s = new ElaspedTimetoCopyAFileUsingByteStream(); s.start(); FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("vowels.txt"); // 23.7 MB File out = new FileOutputStream("output.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } }finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } s.stop(); System.out.println("elapsed time in seconds: " + s.getElapsedTimeSecs()); } } [Elapsed Time for Byte Stream Reader][1] **Character Stream Reader Elapsed Time for 23.7 MB is 3 secs** import java.io.*; import java.io.IOException; import java.util.Scanner; class ElaspedTimetoCopyAFileUsingCharacterStream { private long startTime = 0; private long stopTime = 0; private boolean running = false; public void start() { this.startTime = System.currentTimeMillis(); this.running = true; } public void stop() { this.stopTime = System.currentTimeMillis(); this.running = false; } public long getElapsedTime() { long elapsed; if (running) { elapsed = (System.currentTimeMillis() - startTime); } else { elapsed = (stopTime - startTime); } return elapsed; } public long getElapsedTimeSecs() { long elapsed; if (running) { elapsed = ((System.currentTimeMillis() - startTime) / 1000); } else { elapsed = ((stopTime - startTime) / 1000); } return elapsed; } public static void main(String[] args) throws IOException { ElaspedTimetoCopyAFileUsingCharacterStream s = new ElaspedTimetoCopyAFileUsingCharacterStream(); s.start(); FileReader in = null; // CharacterStream Reader FileWriter out = null; try { in = new FileReader("vowels.txt"); // 23.7 MB out = new FileWriter("output.txt"); int c; while ((c = in.read()) != -1) { out.write(c); } }finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } s.stop(); System.out.println("elapsed time in seconds: " + s.getElapsedTimeSecs()); } } [Elapsed Time for Character Stream Reader][2] [1]: http://sofes.miximages.com/time/hYo8y.png [2]: http://sofes.miximages.com/time/xPjCK.png 

Encontré que este código es útil al cronometrar cosas:

 public class Et { public Et() { reset(); } public void reset() { t0=System.nanoTime(); } public long t0() { return t0; } public long dt() { return System.nanoTime()-t0(); } public double etms() { return etms(dt()); } @Override public String toString() { return etms()+" ms."; } public static double etms(long dt) { return dt/1000000.; // 1_000_000. breaks cobertura } private Long t0; } 

Utilizar esta:

 SimpleDateFormat format = new SimpleDateFormat("HH:mm"); Date d1 = format.parse(strStartTime); Date d2 = format.parse(strEndTime); long diff = d2.getTime() - d1.getTime(); long diffSeconds,diffMinutes,diffHours; if (diff > 0) { diffSeconds = diff / 1000 % 60; diffMinutes = diff / (60 * 1000) % 60; diffHours = diff / (60 * 60 * 1000); } else{ long diffpos = (24*((60 * 60 * 1000))) + diff; diffSeconds = diffpos / 1000 % 60; diffMinutes = diffpos / (60 * 1000) % 60; diffHours = (diffpos / (60 * 60 * 1000)); } 

(También es importante que, por ejemplo, si startTime es 23:00 y endTime 1:00 para obtener una duración de 2:00).

la parte “else” puede corregirlo

Construí una función de formateo basada en cosas que robé SO. Necesitaba una forma de “perfilar” cosas en los mensajes de registro, por lo que necesitaba un mensaje de duración de duración fija.

 public static String GetElapsed(long aInitialTime, long aEndTime, boolean aIncludeMillis) { StringBuffer elapsed = new StringBuffer(); Map units = new HashMap(); long milliseconds = aEndTime - aInitialTime; long seconds = milliseconds / 1000; long minutes = milliseconds / (60 * 1000); long hours = milliseconds / (60 * 60 * 1000); long days = milliseconds / (24 * 60 * 60 * 1000); units.put("milliseconds", milliseconds); units.put("seconds", seconds); units.put("minutes", minutes); units.put("hours", hours); units.put("days", days); if (days > 0) { long leftoverHours = hours % 24; units.put("hours", leftoverHours); } if (hours > 0) { long leftoeverMinutes = minutes % 60; units.put("minutes", leftoeverMinutes); } if (minutes > 0) { long leftoverSeconds = seconds % 60; units.put("seconds", leftoverSeconds); } if (seconds > 0) { long leftoverMilliseconds = milliseconds % 1000; units.put("milliseconds", leftoverMilliseconds); } elapsed.append(PrependZeroIfNeeded(units.get("days")) + " days ") .append(PrependZeroIfNeeded(units.get("hours")) + " hours ") .append(PrependZeroIfNeeded(units.get("minutes")) + " minutes ") .append(PrependZeroIfNeeded(units.get("seconds")) + " seconds ") .append(PrependZeroIfNeeded(units.get("milliseconds")) + " ms"); return elapsed.toString(); } private static String PrependZeroIfNeeded(long aValue) { return aValue < 10 ? "0" + aValue : Long.toString(aValue); } 

Y una clase de prueba:

 import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import junit.framework.TestCase; public class TimeUtilsTest extends TestCase { public void testGetElapsed() { long start = System.currentTimeMillis(); GregorianCalendar calendar = (GregorianCalendar) Calendar.getInstance(); calendar.setTime(new Date(start)); calendar.add(Calendar.MILLISECOND, 610); calendar.add(Calendar.SECOND, 35); calendar.add(Calendar.MINUTE, 5); calendar.add(Calendar.DAY_OF_YEAR, 5); long end = calendar.getTimeInMillis(); assertEquals("05 days 00 hours 05 minutes 35 seconds 610 ms", TimeUtils.GetElapsed(start, end, true)); } }