Aktualny czas w mikrosekundach w java

105

Czy w systemie Unix można uzyskać znacznik czasu z dokładnością do mikrosekund w języku Java? Coś w rodzaju gettimeofdayfunkcji C.

Seth
źródło
6
Należy pamiętać, że zegary komputerowe nie są ustawione nigdzie w pobliżu tego poziomu dokładności - dwa zegary na różnych komputerach zwykle różnią się o co najmniej kilka milisekund, chyba że włożysz trochę wysiłku w skonfigurowanie procedury synchronizacji o wysokiej dokładności (kiedy coś takiego jest nawet fizycznie możliwe). Nie mogę sobie wyobrazić, jaki byłby sens znajomości czasu komputera z dokładnością do mikrosekundy. (Jeśli próbujesz zmierzyć interwał dokładnie na jednym komputerze, to na pewno jest to całkowicie rozsądne, ale nie potrzebujesz pełnego znacznika czasu.)
David Z
2
Ponadto niektóre wersje systemu Unix / Linux zwracają tylko 1 milisekundę lub 10 milisekund szczegółowości w polu mikrosekund.
Stephen C
Pośrednim sposobem jest użycie JDBC , jeśli jest podłączony do bazy danych, takiej jak Postgres, która rejestruje bieżący czas w mikrosekundach.
Basil Bourque
2
Java 9 i późniejsze:Instant.now()
Basil Bourque
Użyj funkcji Instant, aby obliczyć mikrosekundy od Epoki: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.,

Odpowiedzi:

149

Nie, Java nie ma takiej możliwości.

Ma System.nanoTime (), ale to po prostu daje przesunięcie w stosunku do wcześniej znanego czasu. Więc chociaż nie możesz wziąć z tego absolutnej liczby, możesz użyć jej do pomiaru precyzji nanosekundowej (lub wyższej).

Zauważ, że JavaDoc mówi, że chociaż zapewnia to precyzję nanosekundową, nie oznacza to dokładności nanosekundowej. Więc weź odpowiednio duży moduł wartości zwracanej.

AlBlue
źródło
3
Można by przypuszczać, że powodem, dla którego Java nie ma metody getTimeInMicroseconds (), są: 1) dokładność niedostępna na wielu platformach, 2) zwracanie milisekundowej dokładności w wywołaniu mikrosekundowym prowadzi do problemów z przenośnością aplikacji.
Stephen C
9
W Linuksie System.nanoTime () wywołuje clock_gettime (CLOCK_MONOTONIC, _). Brian Oxley sięgnął do kodu źródłowego Javy, aby znaleźć ten samorodek.
David Weber,
8
Ta odpowiedź jest teraz nieaktualna . Implementacje OpenJDK i Oracle Java 9 są wyposażone w nową implementację, Clockktóra umożliwia uchwycenie bieżącego momentu z rozdzielczością dokładniejszą niż milisekundy. Na MacBooku Pro Retina otrzymuję aktualny czas w mikrosekundach (sześć cyfr części dziesiętnej). Rzeczywiste wyniki i dokładność zależą od sprzętu zegarowego komputera głównego.
Basil Bourque
1
@BasilBourque wiele systemów nadal działa na Javie 8 lub nawet wcześniej, ponieważ nie ma potrzeby ich migracji. Chociaż odpowiedź jest nieaktualna, nadal jest przydatna.
Dragas
1
@Dragas Właściwie należałoby je przenieść… aby uzyskać aktualny czas w mikrosekundach, zgodnie z tym pytaniem.
Basil Bourque
81

tl; dr

Java 9 i nowsze: rozdzielczość do nanosekund podczas rejestrowania bieżącego momentu. To 9 cyfr ułamka dziesiętnego.

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

Aby ograniczyć do mikrosekund , obetnij .

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

W praktyce zobaczysz uchwycone tylko mikrosekundy, .nowponieważ współczesne konwencjonalne zegary sprzętu komputerowego nie są dokładne w nanosekundach .

Detale

Pozostałe odpowiedzi są nieco nieaktualne od wersji Java 8.

java.time

Java 8 i nowsze są dostarczane ze strukturą java.time . Te nowe klasy zastępują wadliwe, kłopotliwe klasy daty i czasu dostarczane z najwcześniejszymi wersjami języka Java, takie jak java.util.Date/.Calendar i java.text.SimpleDateFormat. Framework definiuje JSR 310, zainspirowany Joda-Time , rozszerzony o projekt ThreeTen-Extra.

Klasy w java.time są rozwiązywane do nanosekund , znacznie dokładniejszych niż milisekundy używane zarówno przez stare klasy daty i godziny, jak i przez Joda-Time. I dokładniejszy niż mikrosekundy zadane w pytaniu.

wprowadź opis obrazu tutaj

Clock Realizacja

Chociaż klasy java.time obsługują dane reprezentujące wartości w nanosekundach, klasy nie generują jeszcze wartości w nanosekundach. Te now()metody użyć tego samego starego zegara jako realizację dawnych klas data-czasowych System.currentTimeMillis(). Mamy nowy Clockinterfejs w java.time, ale implementacja tego interfejsu to ten sam stary zegar milisekundowy.

Więc możesz sformatować tekstową reprezentację wyniku, ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )aby zobaczyć dziewięć cyfr ułamka sekundy, ale tylko pierwsze trzy cyfry będą miały liczby takie jak ta:

2017-12-23T12:34:56.789000000Z

Nowy zegar w Javie 9

Implementacje OpenJDK i Oracle Java 9 mają nową domyślną Clockimplementację z większą szczegółowością, aż do pełnej nanosekundowej możliwości klas java.time.

Zobacz kwestię OpenJDK, Zwiększ precyzję implementacji java.time.Clock.systemUTC () . Ten problem został pomyślnie wdrożony.

2017-12-23T12:34:56.123456789Z

Na MacBooku Pro (Retina, 15-calowym, koniec 2013 r.) Z macOS Sierra otrzymuję aktualny moment w mikrosekundach (do sześciu cyfr ułamka dziesiętnego).

2017-12-23T12:34:56.123456Z

Zegar sprzętowy

Pamiętaj, że nawet przy nowej, lepszej Clockimplementacji, wyniki mogą się różnić w zależności od komputera. Java jest zależna od zegara sprzętu komputerowego, aby poznać aktualny moment.

  • Rozdzielczość zegarów sprzętowe są bardzo zróżnicowane. Na przykład, jeśli zegar sprzętowy konkretnego komputera obsługuje dokładność tylko w mikrosekundach , wszelkie wygenerowane wartości daty i godziny będą miały tylko sześć cyfr ułamka sekundy, a ostatnie trzy cyfry będą zerami.
  • Dokładność zegarów sprzętowe są bardzo zróżnicowane. Tylko dlatego, że zegar generuje wartość z kilkoma cyframi dziesiętnego ułamka sekundy, cyfry te mogą być niedokładne, tylko przybliżone, odbiegające od rzeczywistego czasu, które można odczytać z zegara atomowego . Innymi słowy, tylko dlatego, że widzisz kilka cyfr po prawej stronie znaku dziesiętnego, nie oznacza, że ​​możesz ufać, że czas, który upłynął między takimi odczytami, będzie zgodny z tym minutowym stopniem.
Basil Bourque
źródło
Mogą rozwiązywać się do nanosekund, ale nadal są oparte na System.currentTimeMillis, więc na końcu dodają po prostu kilka zer. W ogóle nie pomaga, jeśli próbujesz uzyskać czas w mikro / nano sekundach, użytkownik może po prostu ręcznie dodać 0s do milisekund.
annedroiid
@annedroiid Omówiłem to w mojej odpowiedzi. W Javie 8 możesz przechowywać momenty z rozdzielczością nanosekund, ale uchwycenie aktualnego momentu jest tylko w milisekundach. Naprawiono w Javie 9 dzięki nowej implementacji Clock. Mimo to w Javie 8 klasy java.time są przydatne do przechowywania wartości przechwyconych przez inne źródła, takie jak mikrosekundy, w bazie danych Postgres. Uważaj jednak na niedokładność wartości w zakresie mikrosekund i nanosekund; Zwykły sprzęt komputerowy może nie mieć dokładnego czasu śledzenia zegara sprzętowego. Wartość zawierająca sześć lub dziewięć cyfr ułamka sekundy może nie być prawdą.
Basil Bourque
Czy to nie ChronoUnit.MICROS ?
Roland,
@Roland Tak, teraz naprawione, dzięki. Do Twojej wiadomości, na Stack Overflow zapraszamy do bezpośredniej edycji odpowiedzi w celu naprawienia takiego błędu.
Basil Bourque
55

Możesz użyć System.nanoTime():

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

aby uzyskać czas w nanosekundach, ale jest to miara ściśle względna. Nie ma absolutnego znaczenia. Przydaje się tylko do porównania z innymi nanoczasami, aby zmierzyć, jak długo coś trwało.

cletus
źródło
14

Jak już wskazywały inne plakaty; Twój zegar systemowy prawdopodobnie nie jest zsynchronizowany do mikrosekund z rzeczywistym czasem światowym. Niemniej jednak znaczniki czasu z dokładnością do mikrosekund są przydatne jako hybryda zarówno do wskazywania aktualnego czasu ściany, jak i do pomiaru / profilowania czasu trwania rzeczy.

Oznaczam wszystkie zdarzenia / wiadomości zapisane w plikach dziennika za pomocą sygnatur czasowych, takich jak „2012-10-21 19: 13: 45.267128”. Wskazują one zarówno kiedy to się wydarzyło (czas „ściany”), jak i mogą być również użyte do pomiaru czasu trwania między tym a następnym zdarzeniem w pliku dziennika (względna różnica w mikrosekundach).

Aby to osiągnąć, musisz połączyć System.currentTimeMillis () z System.nanoTime () i od tego momentu pracować wyłącznie z System.nanoTime (). Przykładowy kod:

/**
 * 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
źródło
5
Pomysł jest dobry, ale od czasu do czasu musisz ponownie zsynchronizować. Czas systemowy (currentTime) i zegar procesora (nanoTime) NIE są zsynchronizowane, ponieważ często są oparte na różnych zegarach sprzętowych. Ponadto serwer czasu systemu operacyjnego ponownie synchronizuje zegar systemowy z zewnętrznym źródłem czasu, jeśli jest dostępne. Dlatego Twoja pozornie bardzo precyzyjna klasa będzie generować znaczniki czasu, które mogą być dalekie!
h2stein
3
Ten kod nie wydaje się być bezpieczny dla wątków. Dwa jednoczesne połączenia MicroTimestamp.INSTANCE.get()mogą być problematyczne.
Ahmed,
@Ahmed Dlaczego uważasz, że kod nie jest bezpieczny dla wątków? W get()metodzie nie ma nic , co aktualizowałoby zmienne składowe; używa tylko tymczasowych zmiennych lokalnych.
Jason Smith
6

Możesz utworzyć komponent, który określa przesunięcie między System.nanoTime () i System.currentTimeMillis () i efektywnie uzyskać nanosekundy od epoki.

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

Poniższy test daje dość dobre wyniki na moim komputerze.

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

Różnica wydaje się oscylować w przedziale + -3 ms. Wydaje mi się, że można by nieco bardziej podkręcić obliczenia przesunięcia.

1495065607202174413
1495065607203
1495065607202177574
1495065607203
...
1495065607372205730
1495065607370
1495065607372208890
1495065607370
...
kangcz
źródło
2

Jeśli interesuje Cię Linux: Jeśli wyłowisz kod źródłowy do „currentTimeMillis ()”, zobaczysz, że wywołanie tej metody w Linuksie powoduje cofnięcie się o mikrosekundę. Jednak Java skraca następnie mikrosekundy i zwraca milisekundy. Dzieje się tak częściowo dlatego, że Java musi być wieloplatformowa, więc dostarczanie metod specjalnie dla Linuksa było wielkim nie-nie w tamtych czasach (pamiętajcie, że obsługa linków miękkich od wersji 1.6 wstecz ?!) Dzieje się tak również dlatego, że chociaż zegar w Linuksie może zwracać mikrosekundy, niekoniecznie oznacza to, że będzie dobry do sprawdzania czasu. Na poziomach mikrosekund musisz wiedzieć, że NTP nie wyrównuje twojego czasu i że twój zegar nie dryfował zbytnio podczas wywołań metod.

Oznacza to, że teoretycznie w systemie Linux można napisać opakowanie JNI, które jest takie samo jak w pakiecie System, ale nie skracać mikrosekund.

zamhassam
źródło
2

„szybkie i brudne” rozwiązanie, które ostatecznie wybrałem:

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

AKTUALIZACJA:

Początkowo korzystałem z System.nanoTime, ale potem odkryłem, że powinien być używany tylko przez upływający czas, ostatecznie zmieniłem kod, aby działał z milisekundami lub w niektórych miejscach używam:

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

ale to po prostu doda zera na końcu wartości (micros = millis * 1000)

Zostawiłem tę odpowiedź tutaj jako „znak ostrzegawczy” na wypadek, gdyby ktoś pomyślał o nanoTime :)

keisar
źródło
1
Doc wyraźnie mówi się nie używać System.nanoTimena upływ czasu: „Metoda ta może być stosowana tylko do mierzenia upływu czasu i nie jest związana z żadnym innym pojęciem systemu lub upływ czasu.”
Basil Bourque
1
@BasilBourque masz rację, po napisaniu tego w końcu dowiedziałem się i zmieniłem kod, ale zapomniałem tutaj zaktualizować, dzięki za komentarz!
keisar
2

Obsługa języka Java w mikrosekundach przez TimeUnitwyliczenie.

Oto dokument java: Enum TimeUnit

Możesz uzyskać mikrosekundy w Javie w ten sposób:

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

Możesz także przekonwertować mikrosekundy z powrotem na inne jednostki czasu, na przykład:

long seconds = TimeUnit.MICROSECONDS.toSeconds(microsenconds);
tanghao
źródło
To nie jest poprawne, otrzymasz czas w rozdzielczości / długości MICRO, ale sam czas będzie zawsze tylko z dokładnością MILI (co oznacza, że ​​ostatnie 3 cyfry będą zawsze równe 0).
Michalsx
0

Jeśli zamierzasz używać go w systemie czasu rzeczywistego, być może java nie jest najlepszym wyborem, aby uzyskać znacznik czasu. Ale jeśli zamierzasz użyć klucza if dla unikalnego klucza, odpowiedź Jasona Smitha wystarczy. Ale na wszelki wypadek, aby przewidzieć, że 2 elementy otrzymają ten sam znacznik czasu (jest to możliwe, jeśli te 2 zostały przetworzone prawie jednocześnie), możesz zapętlić, aż ostatni znacznik czasu nie będzie równy bieżącemu znacznikowi czasu.

String timestamp = new String();
do {
    timestamp = String.valueOf(MicroTimestamp.INSTANCE.get());
    item.setTimestamp(timestamp);
} while(lasttimestamp.equals(timestamp));
lasttimestamp = item.getTimestamp();
skrzydlaty korona
źródło
0

Użyj funkcji Instant, aby obliczyć mikrosekundy od Epoki:

val instant = Instant.now();
val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.
źródło
0
LocalDateTime.now().truncatedTo(ChronoUnit.MICROS)
walkeros
źródło
-3

Oto przykład sposobu tworzenia UnsignedLong current Timestamp:

UnsignedLong current = new UnsignedLong(new Timestamp(new Date().getTime()).getTime());
na czworo
źródło
2
Niepoprawna odpowiedź. Klasa java.util.Date ma rozdzielczość w milisekundach, a nie mikrosekundach określoną w pytaniu. Utworzenie java.sql.Timestamp nie powoduje wyrzucenia dodatkowych danych z powietrza.
Basil Bourque