Ora attuale in microsecondi in java


104

Su un sistema Unix, c'è un modo per ottenere un timestamp con una precisione al livello di microsecondi in Java? Qualcosa come la gettimeofdayfunzione di C.


6
Tieni presente che gli orologi dei computer non sono impostati su un livello vicino a quel livello di precisione: due orologi su computer diversi di solito differiranno di almeno alcuni millisecondi, a meno che tu non abbia messo un po 'di impegno nell'impostare una procedura di sincronizzazione ad alta precisione (quando una cosa del genere è anche fisicamente possibile). Non riesco a immaginare che senso avrebbe conoscere l'ora del computer al microsecondo. (Se stai cercando di misurare un intervallo che precisamente su un computer, allora certo, è perfettamente ragionevole, ma non hai bisogno del timestamp completo.)
David Z

2
Inoltre, alcune versioni di Unix / Linux restituiscono solo una granularità di 1 millisecondo o 10 millisecondi nel campo dei microsecondi.
Stephen C

Un modo indiretto è tramite JDBC , se connesso a un database come Postgres che acquisisce l'ora corrente in microsecondi.
Basil Bourque

2
Java 9 e Instant.now()
versioni

Usa Instant per calcolare i microsecondi da Epoch: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

Risposte:


148

No, Java non ha questa capacità.

Ha System.nanoTime (), ma questo fornisce solo un offset rispetto a un tempo precedentemente noto. Quindi, anche se non puoi prendere il numero assoluto da questo, puoi usarlo per misurare la precisione in nanosecondi (o superiore).

Si noti che JavaDoc dice che sebbene questo fornisca una precisione al nanosecondo, ciò non significa precisione al nanosecondo. Quindi prendi un modulo adeguatamente grande del valore di ritorno.


3
Si potrebbe ipotizzare che i motivi per cui Java non ha getTimeInMicroseconds () includono 1) precisione non disponibile su molte piattaforme, 2) restituire una precisione al millisecondo in una chiamata al microsecondo porta a problemi di portabilità dell'applicazione.
Stephen C

9
Su Linux, System.nanoTime () chiama clock_gettime (CLOCK_MONOTONIC, _). Brian Oxley ha approfondito il codice sorgente di Java per trovare questo nugget.
David Weber

7
Questa risposta è ormai obsoleta . Le implementazioni OpenJDK e Oracle di Java 9 vengono fornite con una nuova implementazione Clockche prevede di catturare il momento corrente con una risoluzione più fine dei millisecondi. Su un MacBook Pro Retina ottengo l'ora corrente in microsecondi (sei cifre della frazione decimale). I risultati effettivi e la precisione dipendono dall'hardware dell'orologio sottostante del computer host.
Basil Bourque

1
@BasilBourque molti sistemi funzionano ancora su Java 8 o anche prima poiché non è necessario migrarli. Sebbene la risposta sia obsoleta, è comunque utile.
Dragas

1
@ Dragas In realtà, sarebbe necessario migrarli ... per ottenere l'ora corrente in microsecondi come richiesto da questa domanda.
Basil Bourque

78

tl; dr

Java 9 e versioni successive: risoluzione fino a nanosecondi durante l'acquisizione del momento corrente. Sono 9 cifre della frazione decimale.

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

Per limitare a microsecondi , troncare .

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

In pratica, vedrai solo i microsecondi catturati con .nowgli orologi hardware dei computer convenzionali contemporanei non sono precisi in nanosecondi .

Dettagli

Le altre risposte sono un po 'obsolete a partire da Java 8.

java.time

Java 8 e versioni successive vengono fornite con il framework java.time . Queste nuove classi soppiantano le fastidiose classi data-ora fornite con le prime versioni di Java come java.util.Date/.Calendar e java.text.SimpleDateFormat. Il framework è definito da JSR 310, ispirato a Joda-Time , ampliato dal progetto ThreeTen-Extra.

Le classi in java.time si risolvono in nanosecondi , molto più fini dei millisecondi usati sia dalle vecchie classi data-ora che da Joda-Time. E più fine dei microsecondi richiesti nella Domanda.

inserisci qui la descrizione dell'immagine

Clock Implementazione

Sebbene le classi java.time supportino dati che rappresentano valori in nanosecondi, le classi non generano ancora valori in nanosecondi. I now()metodi utilizzano la stessa implementazione orologio antico quanto le vecchie classi data-ora, System.currentTimeMillis(). Abbiamo la nuova Clockinterfaccia in java.time ma l'implementazione per quell'interfaccia è lo stesso vecchio orologio dei millisecondi.

Quindi potresti formattare la rappresentazione testuale del risultato di ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )per vedere nove cifre di un secondo frazionario, ma solo le prime tre cifre avranno numeri come questo:

2017-12-23T12:34:56.789000000Z

Nuovo orologio in Java 9

Le implementazioni OpenJDK e Oracle di Java 9 hanno una nuova Clockimplementazione predefinita con una granularità più fine, fino alla piena capacità in nanosecondi delle classi java.time.

Vedere il problema OpenJDK, Aumentare la precisione dell'implementazione di java.time.Clock.systemUTC () . Questo problema è stato implementato con successo.

2017-12-23T12:34:56.123456789Z

Su un MacBook Pro (Retina, 15 pollici, fine 2013) con macOS Sierra, ottengo il momento corrente in microsecondi (fino a sei cifre della frazione decimale).

2017-12-23T12:34:56.123456Z

Orologio hardware

Ricorda che anche con una nuova Clockimplementazione più precisa, i risultati possono variare a seconda del computer. Java dipende dall'orologio dell'hardware del computer sottostante per conoscere il momento corrente.

  • La risoluzione dei clock hardware varia ampiamente. Ad esempio, se l'orologio hardware di un particolare computer supporta solo la granularità di microsecondi , tutti i valori di data e ora generati avranno solo sei cifre frazionarie di secondo con le ultime tre cifre pari a zero.
  • La precisione degli orologi hardware varia notevolmente. Solo perché un orologio genera un valore con diverse cifre di frazione decimale di secondo, quelle cifre potrebbero essere imprecise, solo approssimazioni, alla deriva rispetto al tempo effettivo come potrebbe essere letta da un orologio atomico . In altre parole, solo perché vedi un gruppo di cifre a destra del segno decimale non significa che puoi fidarti che il tempo trascorso tra tali letture sia vero fino a quel grado minuto.

Possono risolversi in nanosecondi ma sono ancora basati su System.currentTimeMillis, quindi aggiungono solo un gruppo di 0 alla fine. Non aiuta affatto se stai cercando di ottenere il tempo in micro / nano secondi, l'utente potrebbe semplicemente aggiungere 0 al millisecondo manualmente.
annedroiid

@annedroiid L'ho spiegato nella mia risposta. In Java 8 è possibile memorizzare momenti con una risoluzione di nanosecondi, ma l' acquisizione del momento corrente è solo in millisecondi. Risolto in Java 9 con una nuova implementazione di Clock. Anche così, in Java 8 le classi java.time sono utili per conservare i valori catturati da altre fonti come i microsecondi sul database Postgres. Ma attenzione all'imprecisione dei valori nell'intervallo di microsecondi e nanosecondi; l'hardware del computer comune potrebbe non supportare un tempo di rilevamento dell'orologio hardware in modo accurato. Un valore con sei o nove cifre di una frazione di secondo potrebbe non essere vero.
Basil Bourque,


@Roland Sì, ora risolto, grazie. Cordiali saluti, su Stack Overflow sei invitato a modificare direttamente una risposta per correggere tale errore.
Basil Bourque

55

Puoi usare System.nanoTime():

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

per ottenere il tempo in nanosecondi ma è una misura strettamente relativa. Non ha alcun significato assoluto. È utile solo per il confronto con altri nano tempi per misurare quanto tempo ha impiegato qualcosa.


14

Come altri poster già indicati; probabilmente l'orologio di sistema non è sincronizzato fino a microsecondi con l'ora mondiale effettiva. Tuttavia, i timestamp di precisione al microsecondo sono utili come ibridi sia per indicare il tempo di parete corrente, sia per misurare / profilare la durata delle cose.

Etichetta tutti gli eventi / messaggi scritti in un file di registro utilizzando timestamp come "2012-10-21 19: 13: 45.267128". Questi trasmettono sia quando è accaduto (tempo "wall"), e possono anche essere usati per misurare la durata tra questo e il prossimo evento nel file di log (differenza relativa in microsecondi).

Per ottenere ciò, è necessario collegare System.currentTimeMillis () con System.nanoTime () e lavorare esclusivamente con System.nanoTime () da quel momento in poi. Codice di esempio:

/**
 * 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) ;
   }
}

5
L'idea è buona, ma è necessario risincronizzarla ogni tanto. L'ora di sistema (currentTime) e l'orologio della CPU (nanoTime) NON sono sincronizzati, poiché spesso si basano su diversi orologi hardware. Inoltre, il server dell'ora del sistema operativo risincronizza l'orologio di sistema con un'origine dell'ora esterna, se disponibile. Pertanto, la tua classe apparentemente molto precisa produrrà timestamp che potrebbero essere lontani!
h2stein

3
Quel codice non sembra essere thread-safe. Due chiamate simultanee a MicroTimestamp.INSTANCE.get()potrebbero essere problematiche.
Ahmed

@ Ahmed Perché pensi che il codice non sia thread-safe? Non c'è niente nel get()metodo che aggiorna le variabili membro; utilizza solo variabili locali temporanee.
Jason Smith,

6

Potresti forse creare un componente che determini l'offset tra System.nanoTime () e System.currentTimeMillis () e ottenere effettivamente nanosecondi dall'epoca.

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

Il seguente test produce risultati abbastanza buoni sulla mia macchina.

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

La differenza sembra oscillare nel range di + -3ms. Immagino che si potrebbe modificare un po 'di più il calcolo dell'offset.

1495065607202174413
1495065607203
1495065607202177574
1495065607203
...
1495065607372205730
1495065607370
1495065607372208890
1495065607370
...

2

Se sei interessato a Linux: se ripeti il ​​codice sorgente in "currentTimeMillis ()", vedrai che, su Linux, se chiami questo metodo, torna indietro di un microsecondo. Tuttavia, Java tronca i microsecondi e ti restituisce i millisecondi. Ciò è in parte dovuto al fatto che Java deve essere multipiattaforma, quindi fornire metodi specifici per Linux era un grande no-no nel corso della giornata (ricordate quel rozzo supporto per i collegamenti morbidi dalla 1.6 all'indietro ?!). È anche perché, sebbene l'orologio possa restituirti microsecondi in Linux, ciò non significa necessariamente che sarà utile per controllare l'ora. A livelli di microsecondi, devi sapere che NTP non riallinea il tuo tempo e che il tuo orologio non si è spostato troppo durante le chiamate ai metodi.

Ciò significa che, in teoria, su Linux, potresti scrivere un wrapper JNI uguale a quello nel pacchetto System, ma non troncare i microsecondi.


2

una soluzione "rapida e sporca" che alla fine sono andato con:

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

AGGIORNARE:

Inizialmente ho scelto System.nanoTime ma poi ho scoperto che dovrebbe essere usato solo per il tempo trascorso, alla fine ho cambiato il mio codice per lavorare con i millisecondi o in alcuni luoghi uso:

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

ma questo aggiungerà solo zeri alla fine del valore (micros = millis * 1000)

Ho lasciato questa risposta qui come un "segnale di avvertimento" nel caso in cui qualcun altro pensi a nanoTime :)


1
Il documento dice esplicitamente di non utilizzare System.nanoTimeper il tempo dell'orologio da parete: "Questo metodo può essere utilizzato solo per misurare il tempo trascorso e non è correlato a nessun'altra nozione di tempo di sistema o dell'orologio da parete".
Basil Bourque

1
@BasilBourque hai ragione, dopo aver scritto questo alla fine ho scoperto e cambiato il mio codice ma ho dimenticato di aggiornare qui, grazie per il commento!
keisar

2

Java supporta microsecondi tramite TimeUnitenum.

Ecco il documento java: Enum TimeUnit

Puoi ottenere microsecondi in java in questo modo:

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

Puoi anche riconvertire i microsecondi in altre unità di tempo, ad esempio:

long seconds = TimeUnit.MICROSECONDS.toSeconds(microsenconds);

Questo non è corretto, otterrai il tempo in risoluzione / lunghezza MICRO, ma il tempo stesso sarà sempre solo con precisione MILI (il che significa che le ultime 3 cifre saranno sempre 0).
Michalsx

0

Se intendi usarlo per il sistema in tempo reale, forse java non è la scelta migliore per ottenere il timestamp. Ma se intendi utilizzare se per la chiave univoca, la risposta di Jason Smith sarà sufficiente. Ma nel caso in cui, per anticipare che 2 elementi finiscano per ottenere lo stesso timestamp (è possibile se quei 2 sono stati elaborati quasi contemporaneamente), è possibile eseguire il ciclo fino a quando l'ultimo timestamp non è uguale al timestamp corrente.

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

0

Usa Instant per calcolare i microsecondi da Epoch:

val instant = Instant.now();
val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;


-3

Ecco un esempio di come creare un Timestamp corrente UnsignedLong:

UnsignedLong current = new UnsignedLong(new Timestamp(new Date().getTime()).getTime());

2
Risposta sbagliata. La classe java.util.Date ha una risoluzione di millisecondi, non di microsecondi specificati in Question. Creare un java.sql.Timestamp non estrae dati aggiuntivi dall'aria.
Basil Bourque
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.