Java'da mikrosaniye cinsinden geçerli saat


105

Bir Unix sisteminde, Java'da mikrosaniye düzeyinde doğrulukla zaman damgası almanın bir yolu var mı? C'nin gettimeofdayişlevi gibi bir şey .


6
Bilgisayar saatlerinin bu doğruluk düzeyine yakın herhangi bir yere ayarlanmadığını unutmayın - farklı bilgisayarlardaki iki saat, yüksek doğrulukta bir eşitleme prosedürü oluşturmak için biraz çaba göstermediğiniz sürece genellikle en az birkaç milisaniye farklılık gösterecektir. (böyle bir şey fiziksel olarak bile mümkün olduğunda). Bilgisayarın mikrosaniyeye kadar olan zamanını bilmenin ne anlama geldiğini hayal edemiyorum. (Eğer bir aralığı tam olarak bir bilgisayarda ölçmeye çalışıyorsanız , o zaman elbette, bu tamamen mantıklıdır, ancak tam zaman damgasına ihtiyacınız yoktur.)
David Z

2
Ayrıca, Unix / Linux'un bazı sürümleri, mikrosaniye alanında yalnızca 1 milisaniye veya 10 milisaniye ayrıntı düzeyi döndürür.
Stephen C

Dolaylı bir yol, şu anki zamanı mikrosaniye cinsinden yakalayan Postgres gibi bir veritabanına bağlıysa, JDBC aracılığıyladır .
Basil Bourque

2
Java 9 ve sonrası:Instant.now()
Basil Bourque

Epoch'tan beri mikrosaniyeleri hesaplamak için Anında Arama'yı kullanın: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

Yanıtlar:


149

Hayır, Java'nın bu yeteneği yok.

System.nanoTime () var, ancak bu sadece önceden bilinen bir zamandan bir sapma veriyor. Dolayısıyla, bundan mutlak sayıyı alamasanız da, onu nanosaniye (veya daha yüksek) hassasiyeti ölçmek için kullanabilirsiniz.

JavaDoc, bunun nanosaniye hassasiyeti sağlarken, bunun nanosaniye doğruluğu anlamına gelmediğini söylüyor. Öyleyse, geri dönüş değerinin uygun büyüklükte bir modülü alın.


3
Java'nın getTimeInMicroseconds () özelliğine sahip olmamasının nedenlerinin 1) doğruluk pek çok platformda mevcut olmaması, 2) bir mikrosaniye çağrısında milisaniye doğruluğunun döndürülmesinin uygulama taşınabilirliği sorunlarına yol açtığı varsayılabilir.
Stephen C

9
Linux'ta System.nanoTime () clock_gettime (CLOCK_MONOTONIC, _) öğesini çağırır. Brian Oxley bu külçeyi bulmak için Java'nın kaynak kodunu araştırdı.
David Weber

8
Bu Cevap artık güncel değil . Java 9'un OpenJDK ve Oracle uygulamaları Clock, mevcut anı milisaniyeden daha ince bir çözünürlükle yakalamayı sağlayan yeni bir uygulama ile birlikte gelir . MacBook Pro Retina'da şu anki saati mikrosaniye cinsinden alıyorum (altı basamaklı ondalık kesir). Gerçek sonuçlar ve doğruluk, ana bilgisayarın temelindeki saat donanımına bağlıdır.
Basil Bourque

1
@BasilBourque Birçok sistem, geçiş yapmaya gerek olmadığından, Java 8 veya daha önce hala çalışıyor. Cevap güncelliğini yitirmiş olsa da yine de faydalıdır.
Dragas

1
@Dragas Aslında orada olurdu ihtiyaç ... Onları geçirmek için bu Soru sorduğu olarak mikrosaniye geçerli saati almak olacak.
Basil Bourque

81

tl; dr

Java 9 ve üstü: Geçerli anı yakalarken nanosaniye kadar çözünürlük. Bu 9 basamaklı ondalık kesir.

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

İçin sınırlamak için mikrosaniye , kesiğinin .

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

Pratikte, .nowçağdaş geleneksel bilgisayar donanım saatleri nanosaniye cinsinden doğru olmadığından , yalnızca mikrosaniyelerin yakalandığını göreceksiniz .

Detaylar

Diğer Cevaplar Java 8'den itibaren biraz eskidir.

java.time

Java 8 ve üzeri, java.time çerçevesiyle birlikte gelir . Bu yeni sınıflar, java.util.Date/.Calendar ve java.text.SimpleDateFormat gibi Java'nın en eski sürümleriyle birlikte gönderilen hatalı, sorunlu tarih-saat sınıflarının yerini alıyor. Çerçeve, Joda-Time'dan esinlenerek , ThreeTen-Extra projesi tarafından genişletilen JSR 310 tarafından tanımlanmıştır .

Java.time içindeki sınıflar, hem eski tarih-saat sınıfları hem de Joda-Time tarafından kullanılan milisaniyeden çok daha ince olan nanosaniyelere çözümlenir . Ve Soru'da sorulan mikrosaniyeden daha ince .

görüntü açıklamasını buraya girin

Clock Uygulama

Java.time sınıfları destek verisi nanosaniye değerleri temsil ederken, sınıflar henüz yok üretmek nanosaniye değerleri. now()Yöntemleri, eski tarih-saat sınıfları aynı tarihi saat uygulamasını kullanabilirsiniz System.currentTimeMillis(). ClockJava.time'da yeni arayüzümüz var, ancak bu arayüz için uygulama aynı eski milisaniye saatidir.

Böylece ZonedDateTime.now( ZoneId.of( "America/Montreal" ) ), kesirli bir saniyenin dokuz basamağını görmek için sonucun metinsel temsilini biçimlendirebilirsiniz, ancak yalnızca ilk üç basamak aşağıdaki gibi sayılara sahip olacaktır:

2017-12-23T12:34:56.789000000Z

Java 9'da Yeni Saat

Java 9'un OpenJDK ve Oracle uygulamaları Clock, java.time sınıflarının tam nanosaniye kapasitesine kadar daha ince ayrıntı düzeyine sahip yeni bir varsayılan uygulamaya sahiptir.

OpenJDK sorununa bakın, java.time.Clock.systemUTC () uygulamasının kesinliğini artırın . Bu sorun başarıyla uygulandı.

2017-12-23T12:34:56.123456789Z

MacOS Sierra yüklü bir MacBook Pro'da (Retina, 15 inç, 2013 Sonu), şu anki anı mikrosaniye cinsinden (altı haneye kadar ondalık kesir) alıyorum.

2017-12-23T12:34:56.123456Z

Donanım Saati

Daha ince bir Clockuygulamayla bile sonuçlarınızın bilgisayara göre değişebileceğini unutmayın. Java, mevcut anı bilmek için temel bilgisayar donanımının saatine bağlıdır.

  • Çözünürlük donanım saatlerinin büyük farklılıklar gösterir. Örneğin, belirli bir bilgisayarın donanım saati yalnızca mikrosaniye ayrıntı düzeyini destekliyorsa , üretilen herhangi bir tarih-saat değeri, son üç basamak sıfır olmak üzere yalnızca altı basamaklı kesirli saniyeye sahip olacaktır.
  • Doğruluk donanım saatlerin geniş çapta değişebilir. Bir saat, saniyenin birkaç basamaklı ondalık kesirine sahip bir değer ürettiği için, bu basamaklar yanlış olabilir, sadece tahminler, atomik bir saatten okunabileceği gibi gerçek zamandan sapma olabilir . Diğer bir deyişle, ondalık işaretin sağında bir dizi rakam görüyor olmanız, bu tür okumalar arasında geçen süreye o dakika derecesine kadar güvenebileceğiniz anlamına gelmez.

Nanosaniye olarak çözülebilirler ancak yine de System.currentTimeMillis'e dayalıdırlar, bu nedenle sonuna bir grup 0 eklerler. Mikro / nano saniyelerde zaman elde etmeye çalışıyorsanız, kullanıcı milisaniye süresine manuel olarak 0 ekleyebilir.
annedroiid

@annedroiid Bunu Cevabımda ele aldım. Java 8'de anları nanosaniye çözünürlükte saklayabilirsiniz , ancak mevcut anı yakalamak yalnızca milisaniye cinsindendir. Java 9'da yeni bir Clock. Yine de, Java 8'de java.time sınıfları, Postgres veritabanında mikrosaniyeler gibi diğer kaynaklar tarafından yakalanan değerleri tutmak için yararlıdır. Ancak mikrosaniye ve nanosaniye aralığındaki değerlerin yanlışlığına dikkat edin; ortak bilgisayar donanımı, bir donanım saati izleme süresini doğru bir şekilde taşımayabilir. Kesirli saniyenin altı veya dokuz hanesine sahip bir değer doğru olmayabilir.
Basil Bourque

ChronoUnit.MICROS değil mi ?
Roland

@Roland Evet, şimdi düzeltildi, teşekkürler. Bilginize, Stack Overflow'da böyle bir hatayı düzeltmek için bir Yanıtı doğrudan düzenlemeye davetlisiniz.
Basil Bourque

55

Şunları kullanabilirsiniz System.nanoTime():

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

nanosaniye cinsinden zaman elde etmek için, ancak bu kesinlikle göreceli bir ölçüdür. Mutlak bir anlamı yoktur. Yalnızca bir şeyin ne kadar sürdüğünü ölçmek için diğer nano zamanlarla karşılaştırmak için kullanışlıdır.


14

Diğer afişlerde de belirtildiği gibi; sistem saatiniz muhtemelen mikrosaniyeye kadar gerçek dünya saatiyle senkronize edilmemiştir. Yine de mikrosaniye hassas zaman damgaları, hem mevcut duvar zamanını belirtmek hem de nesnelerin süresini ölçmek / profillemek için bir melez olarak yararlıdır.

"2012-10-21 19: 13: 45.267128" gibi zaman damgalarını kullanarak bir günlük dosyasına yazılan tüm olayları / mesajları etiketlerim. Bunlar, hem gerçekleştiğinde ("duvar" zamanı) iletir, hem de bununla günlük dosyasındaki sonraki olay arasındaki süreyi ölçmek için kullanılabilir (mikrosaniye cinsinden göreceli fark).

Bunu başarmak için System.currentTimeMillis () ile System.nanoTime () arasında bağlantı kurmanız ve o andan itibaren yalnızca System.nanoTime () ile çalışmanız gerekir. Örnek 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) ;
   }
}

5
Fikir güzel, ancak arada bir yeniden senkronize etmeniz gerekiyor. Sistem zamanı (currentTime) ve CPU saati (nanoTime) genellikle farklı donanım saatlerine dayandıklarından senkronize DEĞİLDİR. Ayrıca, işletim sisteminizin zaman sunucusu, sistem saatini, varsa harici bir zaman kaynağıyla yeniden senkronize eder. Bu nedenle, görünüşte çok hassas olan sınıfınız, çok uzak olabilecek zaman damgaları üretecektir!
h2stein

3
Bu kod iş parçacığı açısından güvenli görünmüyor. Aynı anda yapılan iki çağrı MicroTimestamp.INSTANCE.get()sorunlu olabilir.
Ahmed

@Ahmed Neden kodun iş parçacığı güvenli olmadığını düşünüyorsunuz? get()Yöntemde üye değişkenleri güncelleyen hiçbir şey yoktur ; yalnızca geçici yerel değişkenler kullanır.
Jason Smith

6

System.nanoTime () ve System.currentTimeMillis () arasındaki uzaklığı belirleyen ve epoch'tan bu yana etkili bir şekilde nanosaniye elde eden bir bileşen oluşturabilirsiniz.

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

Aşağıdaki test, makinemde oldukça iyi sonuçlar veriyor.

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

Fark, + -3ms aralığında salınıyor gibi görünüyor. Sanırım ofset hesaplamasında biraz daha ince ayar yapılabilir.

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

2

Linux ile ilgileniyorsanız: Kaynak kodunu "currentTimeMillis ()" olarak dışarı atarsanız, Linux'ta, bu yöntemi çağırırsanız, mikrosaniye geri aldığını görürsünüz. Ancak Java daha sonra mikrosaniyeleri kısaltır ve size milisaniyeleri geri verir. Bunun nedeni kısmen Java'nın çapraz platform olması gerektiğidir, bu nedenle özellikle Linux için yöntemler sağlamak, gün içinde büyük bir hiç-hayır değildi (1.6'dan geriye doğru kirli yumuşak bağlantı desteğini hatırlayın ?!). Aynı zamanda, Linux'ta saat size mikrosaniye geri verebilirken, bu, saati kontrol etmek için iyi olacağı anlamına gelmez. Mikrosaniye seviyelerinde, NTP'nin zamanınızı yeniden ayarlamadığını ve yöntem çağrıları sırasında saatinizin çok fazla kaymadığını bilmeniz gerekir.

Bu, teoride, Linux'ta, Sistem paketindekiyle aynı olan, ancak mikrosaniyeleri kısaltmayan bir JNI sarıcısı yazabileceğiniz anlamına gelir.


2

Sonunda kullandığım "hızlı ve kirli" bir çözüm:

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

GÜNCELLEME:

Başlangıçta System.nanoTime ile gittim ama sonra sadece geçen süre için kullanılması gerektiğini öğrendim, sonunda kodumu milisaniyelerle çalışacak şekilde değiştirdim veya bazı yerlerde kullanın:

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

ancak bu, değerin sonuna sıfır ekleyecektir (micros = millis * 1000)

Başka birinin nanoTime'ı düşünmesi durumunda bu yanıtı burada bir "uyarı işareti" olarak bıraktı :)


1
Doktor açıkça diyor değil kullanmak System.nanoTimeduvar saatlik süre: “Bu yöntem yalnızca geçen süreyi ölçmek için kullanılabilir ve sistem veya duvar saati diğer herhangi bir zaman kavramına ilgili değildir.”
Fesleğen Bourque

1
@BasilBourque haklısınız, bunu yazdıktan sonra sonunda kodumu öğrendim ve değiştirdim ancak burada güncellemeyi unuttum, yorum için teşekkürler!
keisar

2

Java, TimeUnitnumaralandırma yoluyla mikrosaniyeleri destekler .

İşte java dokümanı: Enum TimeUnit

Bu şekilde java'da mikrosaniye elde edebilirsiniz:

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

Mikrosaniyeleri başka bir zaman birimine de dönüştürebilirsiniz, örneğin:

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

Bu doğru değil, MICRO çözünürlüğünde / uzunluğunda zaman alacaksınız, ancak zamanın kendisi her zaman yalnızca MILI doğruluğu ile olacaktır (yani son 3 hane her zaman 0 olacaktır).
Michalsx

0

Gerçek zamanlı sistem için kullanmayı düşünüyorsanız, zaman damgasını almak için belki de java en iyi seçenek değildir. Ama eğer benzersiz anahtar için kullanacaksanız, Jason Smith'in cevabı yeterli olacaktır. Ancak, her ihtimale karşı, 2 öğenin aynı zaman damgasını alacağını tahmin etmek için (bu ikisi neredeyse aynı anda işlenmişse mümkündür), son zaman damgası geçerli zaman damgasıyla eşit olmayana kadar döngü yapabilirsiniz.

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

0

Epoch'tan sonraki mikrosaniyeleri hesaplamak için Anında Arama'yı kullanın:

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


-3

Bir UnsignedLong geçerli Zaman Damgasının nasıl oluşturulacağına dair bir örnek:

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

2
Yanlış cevap. Java.util.Date sınıfı, Soru'da belirtilen mikrosaniye değil, milisaniyelik bir çözünürlüğe sahiptir. Bir java.sql.Timestamp oluşturmak ek verileri havadan çekmez.
Fesleğen Bourque
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.