Aktuelle Zeit in Mikrosekunden in Java

104

Gibt es auf einem Unix-System eine Möglichkeit, einen Zeitstempel mit einer Genauigkeit von Mikrosekunden in Java zu erhalten? So etwas wie Cs gettimeofdayFunktion.

Seth
quelle
6
Beachten Sie, dass Computeruhren nicht annähernd auf diese Genauigkeit eingestellt sind. Zwei Uhren auf verschiedenen Computern unterscheiden sich normalerweise um mindestens einige Millisekunden, es sei denn, Sie haben einige Anstrengungen unternommen, um ein hochgenaues Synchronisationsverfahren einzurichten (wenn so etwas überhaupt physikalisch möglich ist). Ich kann mir nicht vorstellen, welchen Sinn es hätte, die Zeit des Computers auf die Mikrosekunde genau zu kennen. (Wenn Sie versuchen, ein Intervall genau auf einem Computer zu messen , ist das zwar durchaus sinnvoll, aber Sie benötigen nicht den vollständigen Zeitstempel.)
David Z
2
Außerdem geben einige Versionen von Unix / Linux im Mikrosekundenfeld nur eine Granularität von 1 Millisekunde oder 10 Millisekunden zurück.
Stephen C
Ein indirekter Weg führt über JDBC , wenn eine Verbindung mit einer Datenbank wie Postgres besteht, die die aktuelle Zeit in Mikrosekunden erfasst.
Basil Bourque
2
Java 9 und höher:Instant.now()
Basil Bourque
Verwenden Sie Instant, um Mikrosekunden seit Epoche zu berechnen: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

Antworten:

148

Nein, Java hat diese Fähigkeit nicht.

Es hat zwar System.nanoTime (), aber das gibt nur einen Versatz von einer zuvor bekannten Zeit. Obwohl Sie die absolute Zahl nicht daraus ableiten können, können Sie damit die Genauigkeit von Nanosekunden (oder höher) messen.

Beachten Sie, dass der JavaDoc sagt, dass dies zwar eine Nanosekundengenauigkeit bietet, dies jedoch keine Nanosekundengenauigkeit bedeutet. Nehmen Sie also einen entsprechend großen Modul des Rückgabewerts.

AlBlue
quelle
3
Man könnte vermuten, dass die Gründe, warum Java keine getTimeInMicroseconds () hat, 1) Genauigkeit, die auf vielen Plattformen nicht verfügbar ist, 2) die Rückgabe der Millisekundengenauigkeit in einem Mikrosekundenaufruf zu Problemen bei der Portabilität von Anwendungen führen.
Stephen C
9
Unter Linux ruft System.nanoTime () clock_gettime (CLOCK_MONOTONIC, _) auf. Brian Oxley hat sich in Javas Quellcode vertieft, um dieses Nugget zu finden.
David Weber
7
Diese Antwort ist jetzt veraltet . Die OpenJDK- und Oracle-Implementierungen von Java 9 enthalten eine neue Implementierung Clock, mit der der aktuelle Moment mit einer Auflösung erfasst werden kann, die feiner als Millisekunden ist. Auf einem MacBook Pro Retina erhalte ich die aktuelle Zeit in Mikrosekunden (sechs Nachkommastellen). Die tatsächlichen Ergebnisse und die Genauigkeit hängen von der zugrunde liegenden Takthardware des Host-Computers ab.
Basil Bourque
1
@BasilBourque Viele Systeme laufen noch auf Java 8 oder noch früher, da sie nicht migriert werden müssen. Obwohl die Antwort veraltet ist, ist sie dennoch nützlich.
Dragas
1
@Dragas Eigentlich müssten sie migriert werden, um die aktuelle Zeit in Mikrosekunden zu erhalten, wie in dieser Frage gestellt.
Basil Bourque
78

tl; dr

Java 9 und höher: Bis zu Nanosekunden Auflösung bei der Erfassung des aktuellen Moments. Das sind 9 Nachkommastellen.

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

Um auf Mikrosekunden zu beschränken , schneiden Sie ab .

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 der Praxis werden nur Mikrosekunden angezeigt, mit denen .nowherkömmliche Computerhardwaretakte in Nanosekunden nicht genau sind .

Einzelheiten

Die anderen Antworten sind ab Java 8 etwas veraltet.

java.time

Java 8 und höher wird mit dem Java.time- Framework geliefert . Diese neuen Klassen ersetzen die fehlerhaften, problematischen Datums- und Uhrzeitklassen, die mit den frühesten Java-Versionen wie java.util.Date/.Calendar und java.text.SimpleDateFormat ausgeliefert wurden. Das Framework wird durch JSR 310 definiert, inspiriert von Joda-Time , erweitert durch das ThreeTen-Extra-Projekt.

Die Klassen in java.time werden in Nanosekunden aufgelöst , viel feiner als die Millisekunden, die sowohl von den alten Datums- / Uhrzeitklassen als auch von Joda-Time verwendet werden. Und feiner als die in der Frage gestellten Mikrosekunden .

Geben Sie hier die Bildbeschreibung ein

Clock Implementierung

Während die java.time-Klassen Daten unterstützen, die Werte in Nanosekunden darstellen, generieren die Klassen noch keine Werte in Nanosekunden. Die now()Methoden verwenden dieselbe alte Uhrenimplementierung wie die alten Datums- / Zeitklassen System.currentTimeMillis(). Wir haben die neue ClockSchnittstelle in java.time, aber die Implementierung für diese Schnittstelle ist dieselbe alte Millisekundenuhr.

Sie können also die Textdarstellung des Ergebnisses von formatieren ZonedDateTime.now( ZoneId.of( "America/Montreal" ) ), um neun Ziffern einer Bruchsekunde zu sehen, aber nur die ersten drei Ziffern haben folgende Zahlen:

2017-12-23T12:34:56.789000000Z

Neue Uhr in Java 9

Die OpenJDK- und Oracle-Implementierungen von Java 9 verfügen über eine neue Standardimplementierung Clockmit feinerer Granularität bis zur vollen Nanosekundenfähigkeit der java.time-Klassen.

Weitere Informationen finden Sie im OpenJDK-Problem. Erhöhen Sie die Genauigkeit der Implementierung von java.time.Clock.systemUTC () . Dieses Problem wurde erfolgreich implementiert.

2017-12-23T12:34:56.123456789Z

Auf einem MacBook Pro (Retina, 15 Zoll, Ende 2013) mit macOS Sierra erhalte ich den aktuellen Moment in Mikrosekunden (bis zu sechs Dezimalstellen).

2017-12-23T12:34:56.123456Z

Hardware-Uhr

Denken Sie daran, dass ClockIhre Ergebnisse auch bei einer neuen, feineren Implementierung je nach Computer variieren können. Java hängt von der Uhr der zugrunde liegenden Computerhardware ab, um den aktuellen Moment zu ermitteln.

  • Die Auflösung der Hardware-Uhren variiert stark. Wenn beispielsweise die Hardware-Uhr eines bestimmten Computers nur eine Mikrosekunden- Granularität unterstützt, haben alle generierten Datums- / Uhrzeitwerte nur sechs Sekundenbruchteil, wobei die letzten drei Stellen Nullen sind.
  • Die Genauigkeit der Hardware-Uhren variiert stark. Nur weil eine Uhr einen Wert mit mehreren Stellen im Dezimalbruchteil einer Sekunde erzeugt, können diese Stellen ungenau sein, nur Annäherungen, die von der tatsächlichen Zeit abweichen, wie sie von einer Atomuhr gelesen werden könnten . Mit anderen Worten, nur weil Sie eine Reihe von Ziffern rechts von der Dezimalstelle sehen, bedeutet dies nicht, dass Sie darauf vertrauen können, dass die zwischen diesen Messwerten verstrichene Zeit bis zu diesem winzigen Grad wahr ist.
Basil Bourque
quelle
Sie werden möglicherweise in Nanosekunden aufgelöst, basieren jedoch immer noch auf System.currentTimeMillis, sodass am Ende nur ein paar Nullen hinzugefügt werden. Hilft überhaupt nicht, wenn Sie versuchen, die Zeit in Mikro- / Nanosekunden zu ermitteln. Der Benutzer kann der Millisekundenzeit einfach manuell Nullen hinzufügen.
Annedroiid
@annedroiid Ich habe das in meiner Antwort behandelt. In Java 8 können Sie Momente mit einer Auflösung von Nanosekunden speichern , die Erfassung des aktuellen Moments erfolgt jedoch nur in Millisekunden. In Java 9 mit einer neuen Implementierung von behoben Clock. In Java 8 sind die Klassen java.time jedoch hilfreich, um Werte zu speichern, die von anderen Quellen wie Mikrosekunden in der Postgres-Datenbank erfasst wurden. Achten Sie jedoch auf die Ungenauigkeit der Werte im Mikrosekunden- und Nanosekundenbereich. Gängige Computerhardware trägt möglicherweise keine so genaue Hardware-Taktverfolgungszeit. Ein Wert mit sechs oder neun Sekundenbruchteilen ist möglicherweise nicht wahr.
Basil Bourque
Ist es nicht ChronoUnit.MICROS ?
Roland
@ Roland Ja, jetzt behoben, danke. Zu Ihrer Information, bei Stack Overflow werden Sie aufgefordert, eine Antwort direkt zu bearbeiten, um einen solchen Fehler zu beheben.
Basil Bourque
55

Sie können verwenden System.nanoTime():

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

Zeit in Nanosekunden zu bekommen, aber es ist ein streng relatives Maß. Es hat keine absolute Bedeutung. Es ist nur nützlich, um mit anderen Nano-Zeiten zu vergleichen, um zu messen, wie lange etwas gedauert hat.

Cletus
quelle
14

Wie andere Plakate bereits angedeutet haben; Ihre Systemuhr ist wahrscheinlich nicht bis zu Mikrosekunden mit der tatsächlichen Weltzeit synchronisiert. Nichtsdestotrotz sind Mikrosekunden-Präzisionszeitstempel als Hybrid nützlich, um sowohl die aktuelle Wandzeit anzuzeigen als auch die Dauer von Dingen zu messen / zu profilieren.

Ich beschrifte alle Ereignisse / Nachrichten, die in eine Protokolldatei geschrieben wurden, mit Zeitstempeln wie "2012-10-21 19: 13: 45.267128". Diese geben an, wann es passiert ist ("Wand" -Zeit) und können auch verwendet werden, um die Dauer zwischen diesem und dem nächsten Ereignis in der Protokolldatei zu messen (relativer Unterschied in Mikrosekunden).

Um dies zu erreichen, müssen Sie System.currentTimeMillis () mit System.nanoTime () verknüpfen und von diesem Moment an ausschließlich mit System.nanoTime () arbeiten. Beispielcode:

/**
 * 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
quelle
5
Die Idee ist gut, aber Sie müssen hin und wieder neu synchronisieren. Die Systemzeit (currentTime) und die CPU-Uhr (nanoTime) sind NICHT synchron, da sie häufig auf unterschiedlichen Hardware-Uhren basieren. Darüber hinaus synchronisiert der Zeitserver Ihres Betriebssystems die Systemuhr mit einer externen Zeitquelle, falls verfügbar. Daher wird Ihre scheinbar sehr präzise Klasse Zeitstempel erstellen, die möglicherweise weit entfernt sind!
h2stein
3
Dieser Code scheint nicht threadsicher zu sein. Zwei gleichzeitige Anrufe bei MicroTimestamp.INSTANCE.get()könnten problematisch sein.
Ahmed
@Ahmed Warum ist der Code Ihrer Meinung nach nicht threadsicher? Die get()Methode enthält nichts , was die Mitgliedsvariablen aktualisiert. Es werden nur temporäre lokale Variablen verwendet.
Jason Smith
6

Sie könnten möglicherweise eine Komponente erstellen, die den Versatz zwischen System.nanoTime () und System.currentTimeMillis () bestimmt und seit der Epoche effektiv Nanosekunden abruft.

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

Der folgende Test liefert auf meiner Maschine ziemlich gute Ergebnisse.

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

Der Unterschied scheint im Bereich von + -3 ms zu schwingen. Ich denke, man könnte die Offset-Berechnung etwas weiter optimieren.

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

Wenn Sie sich für Linux interessieren: Wenn Sie den Quellcode auf "currentTimeMillis ()" fischen, werden Sie feststellen, dass unter Linux, wenn Sie diese Methode aufrufen, eine Mikrosekundenzeit zurückkommt. Java schneidet dann jedoch die Mikrosekunden ab und gibt Ihnen Millisekunden zurück. Dies liegt zum Teil daran, dass Java plattformübergreifend sein muss, sodass die Bereitstellung von Methoden speziell für Linux damals ein großes No-No war (denken Sie daran, dass die Unterstützung von Soft-Links ab 1.6 nicht mehr so ​​einfach ist?!). Dies liegt auch daran, dass Ihre Uhr unter Linux zwar Mikrosekunden zurückgeben kann, dies jedoch nicht unbedingt bedeutet, dass Sie die Uhrzeit überprüfen können. Auf Mikrosekundenebene müssen Sie wissen, dass NTP Ihre Zeit nicht neu ausrichtet und dass Ihre Uhr bei Methodenaufrufen nicht zu stark verschoben wurde.

Theoretisch bedeutet dies, dass Sie unter Linux einen JNI-Wrapper schreiben können, der mit dem im Systempaket identisch ist, die Mikrosekunden jedoch nicht abschneiden.

Zamhassam
quelle
2

Eine "schnelle und schmutzige" Lösung, mit der ich mich schließlich befasst habe:

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

AKTUALISIEREN:

Ich habe mich ursprünglich für System.nanoTime entschieden, aber dann habe ich herausgefunden, dass es nur für die verstrichene Zeit verwendet werden sollte. Schließlich habe ich meinen Code geändert, um mit Millisekunden zu arbeiten oder an einigen Stellen Folgendes zu verwenden:

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

Dies fügt jedoch nur Nullen am Ende des Werts hinzu (micros = millis * 1000).

Hinterließ diese Antwort hier als "Warnzeichen" für den Fall, dass jemand anderes an nanoTime denkt :)

Keisar
quelle
1
Das Dokument sagt ausdrücklich zu nicht zu verwenden System.nanoTimefür Wand-Uhr - Zeit: „Diese Methode kann nur verstrichene Zeit verwendet werden , um zu messen und ist nicht auf eine andere Vorstellung von System oder Wandtaktzeit bezogen.“
Basil Bourque
1
@BasilBourque Sie haben Recht, nachdem ich dies geschrieben habe, habe ich es schließlich herausgefunden und meinen Code geändert, aber vergessen, hier zu aktualisieren, danke für den Kommentar!
Keisar
2

Java unterstützt Mikrosekunden durch TimeUnitEnum.

Hier ist das Java-Dokument: Enum TimeUnit

Sie können Mikrosekunden in Java auf folgende Weise erhalten:

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

Sie können Mikrosekunden auch wieder in andere Zeiteinheiten konvertieren, zum Beispiel:

long seconds = TimeUnit.MICROSECONDS.toSeconds(microsenconds);
Tanghao
quelle
Dies ist nicht korrekt, Sie erhalten Zeit in MICRO-Auflösung / Länge, aber die Zeit selbst ist immer nur mit MILI-Genauigkeit (dh die letzten 3 Ziffern sind immer 0).
Michalsx
0

Wenn Sie es für ein Echtzeitsystem verwenden möchten, ist Java möglicherweise nicht die beste Wahl, um den Zeitstempel zu erhalten. Aber wenn Sie if für einen eindeutigen Schlüssel verwenden, reicht die Antwort von Jason Smith aus. Nur für den Fall, dass 2 Elemente denselben Zeitstempel erhalten (es ist möglich, wenn diese 2 fast gleichzeitig verarbeitet wurden), können Sie eine Schleife ausführen, bis der letzte Zeitstempel nicht mehr dem aktuellen Zeitstempel entspricht.

String timestamp = new String();
do {
    timestamp = String.valueOf(MicroTimestamp.INSTANCE.get());
    item.setTimestamp(timestamp);
} while(lasttimestamp.equals(timestamp));
lasttimestamp = item.getTimestamp();
geflügelte Krone
quelle
0

Verwenden Sie Instant, um Mikrosekunden seit Epoche zu berechnen:

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

Hier ist ein Beispiel für das Erstellen eines aktuellen Zeitstempels "UnsignedLong":

UnsignedLong current = new UnsignedLong(new Timestamp(new Date().getTime()).getTime());
thefourtheye
quelle
2
Falsche Antwort. Die Klasse java.util.Date hat eine Auflösung von Millisekunden, nicht von Mikrosekunden, die in der Frage angegeben sind. Durch das Erstellen eines java.sql.Timestamp werden keine zusätzlichen Daten aus der Luft gezogen.
Basil Bourque