Hora actual en microsegundos en java

104

En un sistema Unix, ¿hay alguna manera de obtener una marca de tiempo con una precisión de microsegundos en Java? Algo parecido a la gettimeofdayfunción de C.

Seth
fuente
6
Tenga en cuenta que los relojes de las computadoras no están configurados en ningún lugar cercano a ese nivel de precisión; dos relojes en diferentes computadoras generalmente diferirán en al menos unos pocos milisegundos, a menos que haya realizado un esfuerzo para configurar un procedimiento de sincronización de alta precisión (cuando tal cosa es incluso físicamente posible). No puedo imaginar qué sentido tendría conocer el tiempo de la computadora al microsegundo. (Si está tratando de medir un intervalo con precisión en una computadora, entonces claro, eso es perfectamente razonable, pero no necesita la marca de tiempo completa.)
David Z
2
Además, algunas versiones de Unix / Linux solo devuelven una granularidad de 1 milisegundo o 10 milisegundos en el campo de microsegundos.
Stephen C
Una forma indirecta es a través de JDBC , si está conectado a una base de datos como Postgres que captura la hora actual en microsegundos.
Basil Bourque
2
Java 9 y posterior:Instant.now()
Basil Bourque
Utilice Instant para calcular microsegundos desde Epoch: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

Respuestas:

148

No, Java no tiene esa capacidad.

Tiene System.nanoTime (), pero eso solo da un desplazamiento de un tiempo previamente conocido. Entonces, aunque no puede tomar el número absoluto de esto, puede usarlo para medir una precisión de nanosegundos (o más).

Tenga en cuenta que JavaDoc dice que si bien esto proporciona precisión de nanosegundos, eso no significa precisión de nanosegundos. Así que tome un módulo suficientemente grande del valor de retorno.

AlBlue
fuente
3
Uno podría conjeturar que las razones por las que Java no tiene getTimeInMicroseconds () incluyen 1) la precisión no está disponible en muchas plataformas, 2) devolver una precisión de milisegundos en una llamada de microsegundos conduce a problemas de portabilidad de aplicaciones.
Stephen C
9
En Linux, System.nanoTime () llama a clock_gettime (CLOCK_MONOTONIC, _). Brian Oxley buscó en el código fuente de Java para encontrar esta pepita.
David Weber
7
Esta respuesta ahora está desactualizada . Las implementaciones OpenJDK y Oracle de Java 9 vienen con una nueva implementación Clockque permite capturar el momento actual con una resolución más fina que milisegundos. En una MacBook Pro Retina obtengo la hora actual en microsegundos (seis dígitos de fracción decimal). Los resultados reales y la precisión dependen del hardware de reloj subyacente de la computadora host.
Basil Bourque
1
@BasilBourque muchos sistemas aún se ejecutan en Java 8 o incluso antes, ya que no es necesario migrarlos. Si bien la respuesta está desactualizada, sigue siendo útil.
Dragas
1
@Dragas En realidad, sería necesario migrarlos ... para obtener la hora actual en microsegundos como lo pide esta pregunta.
Basil Bourque
78

tl; dr

Java 9 y posterior: resolución de hasta nanosegundos al capturar el momento actual. Eso es 9 dígitos de fracción decimal.

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

Para limitar a microsegundos , truncar .

Instant                // Represent a moment in UTC. 
.now()                 // Capture the current moment. Returns a `Instant` object. 
.truncatedTo(          // Lop off the finer part of this moment. 
    ChronoUnit.MICROS  // Granularity to which we are truncating. 
)                      // Returns another `Instant` object rather than changing the original, per the immutable objects pattern. 

2017-12-23T12: 34: 56.123456Z

En la práctica, verá solo microsegundos capturados con, .nowya que los relojes de hardware de computadora convencionales contemporáneos no son precisos en nanosegundos .

Detalles

Las otras Respuestas están algo desactualizadas a partir de Java 8.

java.time

Java 8 y versiones posteriores vienen con el marco java.time . Estas nuevas clases suplantan las problemáticas y defectuosas clases de fecha y hora incluidas en las primeras versiones de Java, como java.util.Date/.Calendar y java.text.SimpleDateFormat. El marco está definido por JSR 310, inspirado en Joda-Time , ampliado por el proyecto ThreeTen-Extra.

Las clases en java.time se resuelven en nanosegundos , mucho más fino que los milisegundos utilizados por las antiguas clases de fecha y hora y por Joda-Time. Y más fino que los microsegundos que se preguntan en la Pregunta.

ingrese la descripción de la imagen aquí

Clock Implementación

Si bien las clases java.time admiten datos que representan valores en nanosegundos, las clases aún no generan valores en nanosegundos. Los now()métodos utilizan la misma aplicación de reloj antiguo como las clases de fecha y hora de edad, System.currentTimeMillis(). Tenemos la nueva Clockinterfaz en java.time, pero la implementación de esa interfaz es el mismo reloj de milisegundos.

Por lo tanto, podría formatear la representación textual del resultado de ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )para ver nueve dígitos de una fracción de segundo, pero solo los primeros tres dígitos tendrán números como este:

2017-12-23T12:34:56.789000000Z

Nuevo reloj en Java 9

Las implementaciones OpenJDK y Oracle de Java 9 tienen una nueva Clockimplementación predeterminada con una granularidad más fina, hasta la capacidad completa de nanosegundos de las clases java.time.

Consulte el problema de OpenJDK, Aumente la precisión de la implementación de java.time.Clock.systemUTC () . Ese problema se ha implementado con éxito.

2017-12-23T12:34:56.123456789Z

En una MacBook Pro (Retina, 15 pulgadas, finales de 2013) con macOS Sierra, obtengo el momento actual en microsegundos (hasta seis dígitos de fracción decimal).

2017-12-23T12:34:56.123456Z

Reloj de hardware

Recuerde que incluso con una nueva Clockimplementación más fina , sus resultados pueden variar según la computadora. Java depende del reloj del hardware de la computadora subyacente para conocer el momento actual.

  • La resolución de los relojes de hardware varía ampliamente. Por ejemplo, si el reloj de hardware de una computadora en particular solo admite microsegundos granularidad de , cualquier valor de fecha y hora generado tendrá solo seis dígitos de una fracción de segundo y los últimos tres dígitos serán ceros.
  • La precisión de los relojes de hardware varía ampliamente. Solo porque un reloj genera un valor con varios dígitos de fracción decimal de un segundo, esos dígitos pueden ser inexactos, solo aproximaciones, a la deriva del tiempo real como se podría leer en un reloj atómico . En otras palabras, el hecho de que vea un montón de dígitos a la derecha de la marca decimal no significa que pueda confiar en que el tiempo transcurrido entre tales lecturas sea fiel a ese minuto.
Albahaca Bourque
fuente
Pueden resolverse en nanosegundos, pero todavía se basan en System.currentTimeMillis, por lo que solo agregan un montón de ceros al final. No ayuda en absoluto si está tratando de obtener tiempo en micro / nano segundos, el usuario podría simplemente agregar 0s al tiempo de milisegundos manualmente.
annedroiid
@annedroiid Cubrí eso en mi Respuesta. En Java 8 puede almacenar momentos con una resolución de nanosegundos, pero capturar el momento actual es solo en milisegundos. Se solucionó en Java 9 con una nueva implementación de Clock. Aun así, en Java 8 las clases java.time son útiles para mantener valores capturados por otras fuentes como microsegundos en la base de datos de Postgres. Pero tenga cuidado con la inexactitud de los valores en el rango de microsegundos y nanosegundos; Es posible que el hardware de computadora común no lleve un tiempo de seguimiento de reloj de hardware con tanta precisión. Un valor con seis o nueve dígitos de una fracción de segundo puede no ser cierto.
Basil Bourque
¿No es ChronoUnit.MICROS ?
Roland
@Roland Sí, ahora arreglado, gracias. Para su información, en Stack Overflow se le invita a editar directamente una Respuesta para corregir dicho error.
Basil Bourque
55

Puede utilizar System.nanoTime():

long start = System.nanoTime();
// do stuff
long end = System.nanoTime();
long microseconds = (end - start) / 1000;

para obtener el tiempo en nanosegundos, pero es una medida estrictamente relativa. No tiene un significado absoluto. Solo es útil para comparar con otros tiempos nano para medir cuánto tiempo tomó hacer algo.

cletus
fuente
14

Como ya indicaron otros carteles; El reloj de su sistema probablemente no esté sincronizado hasta microsegundos con la hora mundial real. No obstante, las marcas de tiempo de precisión de microsegundos son útiles como un híbrido tanto para indicar el tiempo de pared actual como para medir / perfilar la duración de las cosas.

Yo etiqueto todos los eventos / mensajes escritos en un archivo de registro usando marcas de tiempo como "2012-10-21 19: 13: 45.267128". Estos transmiten cuándo sucedió (tiempo de "pared") y también se pueden usar para medir la duración entre este y el siguiente evento en el archivo de registro (diferencia relativa en microsegundos).

Para lograr esto, necesita vincular System.currentTimeMillis () con System.nanoTime () y trabajar exclusivamente con System.nanoTime () desde ese momento en adelante. Código de ejemplo:

/**
 * Class to generate timestamps with microsecond precision
 * For example: MicroTimestamp.INSTANCE.get() = "2012-10-21 19:13:45.267128"
 */ 
public enum MicroTimestamp 
{  INSTANCE ;

   private long              startDate ;
   private long              startNanoseconds ;
   private SimpleDateFormat  dateFormat ;

   private MicroTimestamp()
   {  this.startDate = System.currentTimeMillis() ;
      this.startNanoseconds = System.nanoTime() ;
      this.dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") ;
   }

   public String get()
   {  long microSeconds = (System.nanoTime() - this.startNanoseconds) / 1000 ;
      long date = this.startDate + (microSeconds/1000) ;
      return this.dateFormat.format(date) + String.format("%03d", microSeconds % 1000) ;
   }
}
Jason Smith
fuente
5
La idea es buena, pero debes volver a sincronizar de vez en cuando. La hora del sistema (currentTime) y el reloj de la CPU (nanoTime) NO están sincronizados, ya que a menudo se basan en diferentes relojes de hardware. Además, el servidor de hora de su sistema operativo vuelve a sincronizar el reloj del sistema con una fuente de hora externa, si está disponible. Por lo tanto, su clase aparentemente muy precisa producirá marcas de tiempo que podrían estar muy lejos.
h2stein
3
Ese código no parece ser seguro para subprocesos. Dos llamadas simultáneas a MicroTimestamp.INSTANCE.get()podrían ser problemáticas.
Ahmed
@Ahmed ¿Por qué crees que el código no es seguro para subprocesos? No hay nada en el get()método que actualice las variables miembro; solo usa variables locales temporales.
Jason Smith
6

Tal vez podría crear un componente que determine el desplazamiento entre System.nanoTime () y System.currentTimeMillis () y obtener efectivamente nanosegundos desde epoch.

public class TimerImpl implements Timer {

    private final long offset;

    private static long calculateOffset() {
        final long nano = System.nanoTime();
        final long nanoFromMilli = System.currentTimeMillis() * 1_000_000;
        return nanoFromMilli - nano;
    }

    public TimerImpl() {
        final int count = 500;
        BigDecimal offsetSum = BigDecimal.ZERO;
        for (int i = 0; i < count; i++) {
            offsetSum = offsetSum.add(BigDecimal.valueOf(calculateOffset()));
        }
        offset = (offsetSum.divide(BigDecimal.valueOf(count))).longValue();
    }

    public long nowNano() {
        return offset + System.nanoTime();
    }

    public long nowMicro() {
        return (offset + System.nanoTime()) / 1000;
    }

    public long nowMilli() {
        return System.currentTimeMillis();
    }
}

La siguiente prueba produce resultados bastante buenos en mi máquina.

    final Timer timer = new TimerImpl();
    while (true) {
        System.out.println(timer.nowNano());
        System.out.println(timer.nowMilli());
    }

La diferencia parece oscilar en un rango de + -3ms. Supongo que se podría modificar un poco más el cálculo de compensación.

1495065607202174413
1495065607203
1495065607202177574
1495065607203
...
1495065607372205730
1495065607370
1495065607372208890
1495065607370
...
kangcz
fuente
2

Si está interesado en Linux: si extrae el código fuente de "currentTimeMillis ()", verá que, en Linux, si llama a este método, retrocede un microsegundo. Sin embargo, Java luego trunca los microsegundos y te devuelve milisegundos. Esto se debe en parte a que Java tiene que ser multiplataforma, por lo que proporcionar métodos específicamente para Linux era un gran no-no en el pasado (¿recuerdas ese soporte de enlaces blandos desde 1.6 hacia atrás?). También se debe a que, si bien el reloj puede devolverle microsegundos en Linux, eso no significa necesariamente que sea bueno para verificar la hora. A niveles de microsegundos, debe saber que NTP no está realineando su tiempo y que su reloj no se ha desviado demasiado durante las llamadas a métodos.

Esto significa que, en teoría, en Linux, podría escribir un contenedor JNI que sea el mismo que el del paquete System, pero sin truncar los microsegundos.

zamhassam
fuente
2

una solución "rápida y sucia" con la que finalmente fui:

TimeUnit.NANOSECONDS.toMicros(System.nanoTime());

ACTUALIZAR:

Originalmente fui con System.nanoTime pero luego descubrí que solo debería usarse durante el tiempo transcurrido, finalmente cambié mi código para que funcione con milisegundos o en algunos lugares use:

TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());

pero esto solo agregará ceros al final del valor (micros = milis * 1000)

Dejó esta respuesta aquí como una "señal de advertencia" en caso de que alguien más piense en nanoTime :)

keisar
fuente
1
El documento dice explícitamente que no se use System.nanoTimepara el tiempo de reloj de pared: "Este método solo se puede usar para medir el tiempo transcurrido y no está relacionado con ninguna otra noción de sistema o tiempo de reloj de pared".
Basil Bourque
1
@BasilBourque tienes razón, después de escribir esto eventualmente descubrí y cambié mi código, pero olvidé actualizar aquí, ¡gracias por el comentario!
keisar
2

Soporte de Java microsegundos a través de TimeUnit enum.

Aquí está el documento de Java: Enum TimeUnit

Puede obtener microsegundos en java de esta manera:

long microsenconds = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());

También puede convertir microsegundos a otras unidades de tiempo, por ejemplo:

long seconds = TimeUnit.MICROSECONDS.toSeconds(microsenconds);
tanghao
fuente
Esto no es correcto, obtendrá el tiempo en resolución / longitud MICRO, pero el tiempo en sí será siempre solo con precisión MILI (lo que significa que los últimos 3 dígitos siempre serán 0).
Michalsx
0

Si tiene la intención de usarlo para el sistema en tiempo real, quizás Java no sea la mejor opción para obtener la marca de tiempo. Pero si va a usar if para una clave única, entonces la respuesta de Jason Smith será suficiente. Pero por si acaso, para anticipar que 2 elementos terminan obteniendo la misma marca de tiempo (es posible si esos 2 se procesaron casi simultáneamente), puede realizar un ciclo hasta que la última marca de tiempo no sea igual a la marca de tiempo actual.

String timestamp = new String();
do {
    timestamp = String.valueOf(MicroTimestamp.INSTANCE.get());
    item.setTimestamp(timestamp);
} while(lasttimestamp.equals(timestamp));
lasttimestamp = item.getTimestamp();
corona de alas
fuente
0

Utilice Instant para calcular microsegundos desde Epoch:

val instant = Instant.now();
val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.
fuente
0
LocalDateTime.now().truncatedTo(ChronoUnit.MICROS)
walkeros
fuente
-3

A continuación, se muestra un ejemplo de cómo crear una marca de tiempo actual UnsignedLong:

UnsignedLong current = new UnsignedLong(new Timestamp(new Date().getTime()).getTime());
los cuatro ojos
fuente
2
Respuesta incorrecta. La clase java.util.Date tiene una resolución de milisegundos, no microsegundos especificada en la Pregunta. Hacer un java.sql.Timestamp no extrae datos adicionales del aire.
Basil Bourque