Hora atual em microssegundos em java

104

Em um sistema Unix, existe uma maneira de obter um carimbo de data / hora com precisão de nível de microssegundo em Java? Algo como a gettimeofdayfunção de C.

Seth
fonte
6
Lembre-se de que os relógios dos computadores não estão definidos para nada perto desse nível de precisão - dois relógios em computadores diferentes geralmente vão diferir em pelo menos alguns milissegundos, a menos que você tenha feito algum esforço para configurar um procedimento de sincronização de alta precisão (quando tal coisa é fisicamente possível). Não consigo imaginar que sentido haveria em saber o tempo do computador em microssegundos. (Se você está tentando medir um intervalo precisamente em um computador, então, com certeza, isso é perfeitamente razoável, mas você não precisa do carimbo de data / hora completo.)
David Z
2
Além disso, algumas versões do Unix / Linux retornam apenas 1 milissegundo ou 10 milissegundos de granularidade no campo de microssegundo.
Stephen C
Uma forma indireta é através do JDBC , se conectado a um banco de dados como o Postgres, que captura a hora atual em microssegundos.
Basil Bourque
2
Java 9 e posterior:Instant.now()
Basil Bourque
Use o Instant para calcular microssegundos desde a Época: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

Respostas:

148

Não, Java não tem essa capacidade.

Ele tem System.nanoTime (), mas isso apenas fornece um deslocamento de algum tempo conhecido anteriormente. Portanto, embora você não possa tirar o número absoluto disso, você pode usá-lo para medir a precisão de nanossegundos (ou superior).

Observe que o JavaDoc diz que, embora forneça precisão de nanossegundos, isso não significa precisão de nanossegundos. Portanto, pegue um módulo adequadamente grande do valor de retorno.

AlBlue
fonte
3
Alguém poderia conjeturar que os motivos pelos quais Java não tem getTimeInMicroseconds () incluem 1) precisão não disponível em muitas plataformas, 2) retornar precisão de milissegundos em uma chamada de microssegundo leva a problemas de portabilidade do aplicativo.
Stephen C
9
No Linux, System.nanoTime () chama clock_gettime (CLOCK_MONOTONIC, _). Brian Oxley vasculhou o código-fonte do Java para encontrar esse nugget.
David Weber
7
Esta resposta agora está desatualizada . As implementações OpenJDK e Oracle do Java 9 vêm com uma nova implementação Clockque fornece a captura do momento atual com uma resolução mais fina do que milissegundos. Em um MacBook Pro Retina, estou obtendo a hora atual em microssegundos (seis dígitos da fração decimal). Os resultados reais e a precisão dependem do hardware de relógio subjacente do computador host.
Basil Bourque
1
@BasilBourque muitos sistemas ainda são executados em Java 8 ou até mesmo antes, pois não há necessidade de migrá-los. Embora a resposta esteja desatualizada, ainda é útil.
Dragas
1
@Dragas Na verdade, seria necessário migrá-los ... para obter a hora atual em microssegundos, conforme perguntado por esta pergunta.
Basil Bourque
78

tl; dr

Java 9 e posterior: Resolução de até nanossegundos ao capturar o momento atual. São 9 dígitos da fração decimal.

Instant.now()   

23-12-2017T12: 34: 56.123456789Z

Para limitar a microssegundos , trunque .

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. 

23-12-2017T12: 34: 56.123456Z

Na prática, você verá apenas microssegundos capturados com .nowrelógios convencionais de hardware de computador contemporâneos que não são precisos em nanossegundos .

Detalhes

As outras respostas estão um tanto desatualizadas no Java 8.

java.time

Java 8 e posterior vem com a estrutura java.time . Essas novas classes substituem as classes problemáticas e problemáticas de data e hora enviadas com as versões mais antigas do Java, como java.util.Date/.Calendar e java.text.SimpleDateFormat. O framework é definido pela JSR 310, inspirado no Joda-Time , estendido pelo projeto ThreeTen-Extra.

As classes em java.time resolvem em nanossegundos , muito mais precisos do que os milissegundos usados ​​pelas antigas classes de data e hora e pelo Joda-Time. E mais fino do que os microssegundos perguntados na pergunta.

insira a descrição da imagem aqui

Clock Implementação

Embora as classes java.time suportem dados que representam valores em nanossegundos, as classes ainda não geram valores em nanossegundos. Os now()métodos usam a mesma implementação de relógio antigo que as classes de data e hora antigas System.currentTimeMillis(). Temos a nova Clockinterface em java.time, mas a implementação dessa interface é o mesmo relógio de milissegundos antigo.

Portanto, você pode formatar a representação textual do resultado de ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )para ver nove dígitos de uma fração de segundo, mas apenas os três primeiros dígitos terão números como este:

2017-12-23T12:34:56.789000000Z

Novo relógio em Java 9

As implementações OpenJDK e Oracle do Java 9 têm uma nova Clockimplementação padrão com granularidade mais fina, até a capacidade total de nanossegundos das classes java.time.

Consulte o problema do OpenJDK, Aumente a precisão da implementação de java.time.Clock.systemUTC () . Esse problema foi implementado com sucesso.

2017-12-23T12:34:56.123456789Z

Em um MacBook Pro (Retina, 15 polegadas, final de 2013) com macOS Sierra, obtenho o momento atual em microssegundos (até seis dígitos da fração decimal).

2017-12-23T12:34:56.123456Z

Relógio de Hardware

Lembre-se de que, mesmo com uma nova Clockimplementação mais precisa, seus resultados podem variar de acordo com o computador. Java depende do relógio do hardware do computador para saber o momento atual.

  • A resolução dos relógios de hardware variam amplamente. Por exemplo, se o relógio de hardware de um determinado computador suportar apenas granularidade de microssegundos , quaisquer valores de data e hora gerados terão apenas seis dígitos de frações de segundo, com os três últimos dígitos sendo zeros.
  • A precisão dos relógios de hardware varia amplamente. Só porque um relógio gera um valor com vários dígitos da fração decimal de um segundo, esses dígitos podem ser imprecisos, apenas aproximações, à deriva do tempo real como podem ser lidos de um relógio atômico . Em outras palavras, só porque você vê um monte de dígitos à direita da marca decimal não significa que você pode confiar que o tempo decorrido entre essas leituras será verdadeiro para aquele grau de minuto.
Basil Bourque
fonte
Eles podem ser resolvidos em nanossegundos, mas ainda são baseados em System.currentTimeMillis, então eles apenas adicionam um monte de zeros no final. Não ajuda em nada se você está tentando obter o tempo em micro / nano segundos, o usuário pode simplesmente adicionar 0s ao tempo do milissegundo manualmente.
annedroiid de
@annedroiid Abordei isso na minha resposta. No Java 8 você pode armazenar momentos com resolução de nanossegundos, mas capturar o momento atual é apenas em milissegundos. Corrigido em Java 9 com uma nova implementação de Clock. Mesmo assim, no Java 8, as classes java.time são úteis para manter valores capturados por outras fontes, como microssegundos no banco de dados Postgres. Mas tome cuidado com a imprecisão dos valores na faixa de microssegundos e nanossegundos; hardware de computador comum pode não ter um relógio de hardware que controle o tempo com precisão. Um valor com seis ou nove dígitos de frações de segundo pode não ser verdadeiro.
Basil Bourque de
Não é ChronoUnit.MICROS ?
Roland
@Roland Sim, agora consertado, obrigado. Para sua informação, no Stack Overflow, você está convidado a editar diretamente uma resposta para corrigir esse erro.
Basil Bourque
55

Você pode usar System.nanoTime():

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

para obter o tempo em nanossegundos, mas é uma medida estritamente relativa. Não tem significado absoluto. É útil apenas para comparação com outros nano tempos para medir quanto tempo algo demorou para fazer.

cletus
fonte
14

Como outros pôsteres já indicados; o relógio do sistema provavelmente não está sincronizado em microssegundos com a hora mundial real. No entanto, os carimbos de data / hora de precisão de microssegundos são úteis como um híbrido para indicar o tempo de parede atual e medir / definir o perfil da duração das coisas.

Eu rotulo todos os eventos / mensagens gravados em arquivos de log usando carimbos de data / hora como "2012-10-21 19: 13: 45.267128". Eles transmitem quando isso aconteceu (tempo de "parede") e também podem ser usados ​​para medir a duração entre este e o próximo evento no arquivo de log (diferença relativa em microssegundos).

Para conseguir isso, você precisa vincular System.currentTimeMillis () com System.nanoTime () e trabalhar exclusivamente com System.nanoTime () daquele momento em diante. Código de exemplo:

/**
 * 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
fonte
5
A ideia é boa, mas você deve sincronizar novamente de vez em quando. A hora do sistema (currentTime) e o relógio da CPU (nanoTime) NÃO estão sincronizados, pois costumam ser baseados em diferentes relógios de hardware. Além disso, o servidor de horário do sistema operacional sincroniza novamente o relógio do sistema com uma fonte de horário externa, se disponível. Portanto, sua classe aparentemente muito precisa produzirá marcações de tempo que podem estar muito erradas!
h2stein
3
Esse código não parece ser thread-safe. Duas chamadas simultâneas para MicroTimestamp.INSTANCE.get()podem ser problemáticas.
Ahmed,
@Ahmed Por que você acha que o código não é seguro para threads? Não há nada no get()método que atualize as variáveis ​​de membro; ele usa apenas variáveis ​​locais temporárias.
Jason Smith
6

Você poderia talvez criar um componente que determina o deslocamento entre System.nanoTime () e System.currentTimeMillis () e obter efetivamente nanosegundos desde a época.

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();
    }
}

O teste seguinte produz resultados bastante bons em minha máquina.

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

A diferença parece oscilar na faixa de + -3ms. Acho que é possível ajustar um pouco mais o cálculo do deslocamento.

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

Se você estiver interessado no Linux: Se você pescar o código-fonte para "currentTimeMillis ()", verá que, no Linux, se você chamar esse método, ele retorna um microssegundo de volta. No entanto, o Java então trunca os microssegundos e devolve milissegundos. Em parte, isso se deve ao fato de o Java ter que ser multiplataforma, então fornecer métodos específicos para o Linux era um grande problema no passado (lembra daquele suporte de soft link do 1.6 para trás ?!). Também é porque, embora seu relógio possa devolver microssegundos no Linux, isso não significa necessariamente que será bom para verificar a hora. Em níveis de microssegundos, você precisa saber que o NTP não está realinhando seu tempo e que seu relógio não mudou muito durante as chamadas de método.

Isso significa que, em teoria, no Linux, você poderia escrever um wrapper JNI igual ao do pacote System, mas sem truncar os microssegundos.

Zamhassam
fonte
2

uma solução "rápida e suja" que acabei usando:

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

ATUALIZAR:

Eu usei o System.nanoTime originalmente, mas descobri que ele só deveria ser usado pelo tempo decorrido, eventualmente mudei meu código para funcionar com milissegundos ou em alguns lugares use:

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

mas isso apenas adicionará zeros no final do valor (micros = millis * 1000)

Deixei esta resposta aqui como um "sinal de alerta" no caso de outra pessoa pensar em nanoTime :)

keisar
fonte
1
O documento diz explicitamente para não usar System.nanoTimecomo relógio de parede: “Este método só pode ser usado para medir o tempo decorrido e não está relacionado a qualquer outra noção de sistema ou tempo de relógio de parede.”
Basil Bourque
1
@BasilBourque você está correto, depois de escrever isso eventualmente descobri e mudei meu código, mas esqueci de atualizar aqui, obrigado pelo comentário!
keisar
2

Java oferece suporte a microssegundos por meio de TimeUnitenum.

Aqui está o documento java: Enum TimeUnit

Você pode obter microssegundos em java desta maneira:

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

Você também pode converter microssegundos de volta em outras unidades de tempo, por exemplo:

long seconds = TimeUnit.MICROSECONDS.toSeconds(microsenconds);
tanghao
fonte
Incorreto. Você obterá o tempo na resolução / duração MICRO, mas o tempo em si será sempre apenas com a precisão MILI (o que significa que os últimos 3 dígitos serão sempre 0).
Michalsx
0

Se você pretende usá-lo para o sistema em tempo real, talvez java não seja a melhor escolha para obter o carimbo de data / hora. Mas se você for usar if para a chave exclusiva, a resposta de Jason Smith será suficiente. Mas apenas no caso, para antecipar 2 itens acabam recebendo o mesmo carimbo de data / hora (é possível se esses 2 forem processados ​​quase simultaneamente), você pode fazer um loop até que o último carimbo de data / hora não seja igual ao carimbo de data / hora atual.

String timestamp = new String();
do {
    timestamp = String.valueOf(MicroTimestamp.INSTANCE.get());
    item.setTimestamp(timestamp);
} while(lasttimestamp.equals(timestamp));
lasttimestamp = item.getTimestamp();
coroa de asas
fonte
0

Use o Instant para calcular microssegundos desde a época:

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

Aqui está um exemplo de como criar um carimbo de data / hora atual UnsignedLong:

UnsignedLong current = new UnsignedLong(new Timestamp(new Date().getTime()).getTime());
os quatro olhos
fonte
2
Resposta incorreta. A classe java.util.Date tem uma resolução de milissegundos, não microssegundos especificados na pergunta. Fazer um java.sql.Timestamp não extrai dados adicionais do ar.
Basil Bourque