Heure actuelle en microsecondes en Java


105

Sur un système Unix, existe-t-il un moyen d'obtenir un horodatage avec une précision de l'ordre de la microseconde en Java? Quelque chose comme la gettimeofdayfonction de C.


6
Gardez à l'esprit que les horloges des ordinateurs ne sont pas réglées à un niveau proche de ce niveau de précision - deux horloges sur des ordinateurs différents vont généralement différer d'au moins quelques millisecondes, à moins que vous n'ayez fait des efforts pour mettre en place une procédure de synchronisation de haute précision. (lorsqu'une telle chose est même physiquement possible). Je ne peux pas imaginer à quel point il serait utile de connaître l'heure de l'ordinateur à la microseconde. (Si vous essayez de mesurer un intervalle avec précision sur un ordinateur, alors bien sûr, c'est parfaitement raisonnable, mais vous n'avez pas besoin de l'horodatage complet.)
David Z

2
De plus, certaines versions d'Unix / Linux ne renvoient qu'une granularité de 1 milliseconde ou 10 millisecondes dans le champ microseconde.
Stephen C

Un moyen indirect consiste à utiliser JDBC , s'il est connecté à une base de données telle que Postgres qui capture l'heure actuelle en microsecondes.
Basil Bourque

2
Java 9 et plus tard:Instant.now()
Basil Bourque

Utilisez Instant pour calculer les microsecondes depuis l'époque: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Michael M.

Réponses:


148

Non, Java n'a pas cette capacité.

Il a System.nanoTime (), mais cela donne juste un décalage par rapport à une heure connue auparavant. Ainsi, même si vous ne pouvez pas en tirer le nombre absolu, vous pouvez l'utiliser pour mesurer une précision nanoseconde (ou supérieure).

Notez que le JavaDoc dit que si cela fournit une précision nanoseconde, cela ne signifie pas une précision nanoseconde. Prenez donc un module suffisamment grand de la valeur de retour.


3
On pourrait supposer que les raisons pour lesquelles Java n'a pas de getTimeInMicroseconds () incluent 1) la précision non disponible sur de nombreuses plates-formes, 2) le retour d'une précision de la milliseconde dans un appel de microseconde conduit à des problèmes de portabilité des applications.
Stephen C

9
Sous Linux, System.nanoTime () appelle clock_gettime (CLOCK_MONOTONIC, _). Brian Oxley a fouillé dans le code source de Java pour trouver ce pépite.
David Weber

7
Cette réponse est désormais obsolète . Les implémentations OpenJDK et Oracle de Java 9 sont livrées avec une nouvelle implémentation de Clockqui permet de capturer l'instant actuel avec une résolution plus fine que la milliseconde. Sur un MacBook Pro Retina, j'obtiens l'heure actuelle en microsecondes (six chiffres de la fraction décimale). Les résultats et la précision réels dépendent du matériel d'horloge sous-jacent de l'ordinateur hôte.
Basil Bourque

1
@BasilBourque beaucoup de systèmes fonctionnent toujours sous Java 8 ou même plus tôt car il n'est pas nécessaire de les migrer. Bien que la réponse soit obsolète, elle est toujours utile.
Dragas

1
@Dragas En fait, il serait nécessaire de les migrer… pour obtenir l'heure actuelle en microsecondes comme demandé par cette question.
Basil Bourque

79

tl; dr

Java 9 et versions ultérieures: résolution jusqu'à nanosecondes lors de la capture du moment actuel. Cela fait 9 chiffres de fraction décimale.

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

Pour limiter aux microsecondes , tronquez .

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

En pratique, vous ne verrez que les microsecondes capturées avec, .nowcar les horloges matérielles informatiques conventionnelles contemporaines ne sont pas précises en nanosecondes .

Détails

Les autres réponses sont quelque peu obsolètes à partir de Java 8.

java.time

Java 8 et versions ultérieures sont fournis avec le framework java.time . Ces nouvelles classes supplantent les classes date-heure défectueuses livrées avec les premières versions de Java telles que java.util.Date/.Calendar et java.text.SimpleDateFormat. Le framework est défini par JSR 310, inspiré de Joda-Time , étendu par le projet ThreeTen-Extra.

Les classes de java.time se résolvent en nanosecondes , beaucoup plus fines que les millisecondes utilisées à la fois par les anciennes classes date-heure et par Joda-Time. Et plus fin que les microsecondes posées dans la question.

entrez la description de l'image ici

Clock la mise en oeuvre

Alors que les classes java.time prennent en charge les données représentant des valeurs en nanosecondes, les classes ne génèrent pas encore de valeurs en nanosecondes. Les now()méthodes utilisent la même ancienne implémentation d'horloge que les anciennes classes de date-heure, System.currentTimeMillis(). Nous avons la nouvelle Clockinterface dans java.time mais l'implémentation de cette interface est la même vieille horloge en millisecondes.

Vous pouvez donc formater la représentation textuelle du résultat de ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )pour voir neuf chiffres d'une fraction de seconde, mais seuls les trois premiers chiffres auront des nombres comme celui-ci:

2017-12-23T12:34:56.789000000Z

Nouvelle horloge dans Java 9

Les implémentations OpenJDK et Oracle de Java 9 ont une nouvelle Clockimplémentation par défaut avec une granularité plus fine, jusqu'à la pleine capacité nanoseconde des classes java.time.

Voir le problème OpenJDK, Augmentez la précision de l'implémentation de java.time.Clock.systemUTC () . Ce problème a été mis en œuvre avec succès.

2017-12-23T12:34:56.123456789Z

Sur un MacBook Pro (Retina, 15 pouces, fin 2013) avec macOS Sierra, j'obtiens le moment actuel en microsecondes (jusqu'à six chiffres de fraction décimale).

2017-12-23T12:34:56.123456Z

Horloge matérielle

N'oubliez pas que même avec une nouvelle Clockimplémentation plus fine , vos résultats peuvent varier selon l'ordinateur. Java dépend de l'horloge du matériel informatique sous-jacent pour connaître le moment actuel.

  • La résolution des horloges matérielles varie considérablement. Par exemple, si l'horloge matérielle d'un ordinateur particulier ne prend en charge que la granularité en microsecondes , toutes les valeurs de date-heure générées n'auront que six chiffres de fraction de seconde, les trois derniers chiffres étant des zéros.
  • La précision des horloges matérielles varie considérablement. Tout simplement parce qu'une horloge génère une valeur avec plusieurs chiffres de fraction décimale de seconde, ces chiffres peuvent être inexacts, juste des approximations, à la dérive du temps réel comme on pourrait le lire à partir d'une horloge atomique . En d'autres termes, ce n'est pas parce que vous voyez un tas de chiffres à droite de la marque décimale que vous pouvez faire confiance au temps écoulé entre ces lectures pour être vrai à ce degré infime.

Ils peuvent se résoudre en nanosecondes, mais ils sont toujours basés sur System.currentTimeMillis, ils ajoutent donc simplement un tas de 0 à la fin. Cela n'aide pas du tout si vous essayez d'obtenir le temps en micro / nano secondes, l'utilisateur peut simplement ajouter des 0 à la milliseconde manuellement.
annedroiid

@annedroiid J'ai couvert cela dans ma réponse. Dans Java 8, vous pouvez stocker des moments avec une résolution de l'ordre de la nanoseconde, mais la capture du moment actuel ne se fait qu'en millisecondes. Correction dans Java 9 avec une nouvelle implémentation de Clock. Même ainsi, dans Java 8, les classes java.time sont utiles pour conserver les valeurs capturées par d'autres sources telles que les microsecondes sur la base de données Postgres. Mais méfiez-vous de l'inexactitude des valeurs dans la gamme des microsecondes et des nanosecondes; le matériel informatique commun peut ne pas avoir une heure de suivi d'horloge matérielle aussi précise. Une valeur avec six ou neuf chiffres de fraction de seconde peut ne pas être vraie.
Basil Bourque

N'est-ce pas ChronoUnit.MICROS ?
Roland

@Roland Oui, maintenant corrigé, merci. Pour info, sur Stack Overflow, vous êtes invité à modifier directement une réponse pour corriger une telle erreur.
Basil Bourque

55

Vous pouvez utiliser System.nanoTime():

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

pour obtenir le temps en nanosecondes mais c'est une mesure strictement relative. Cela n'a pas de sens absolu. Il n'est utile que pour comparer avec d'autres temps nano pour mesurer combien de temps il a fallu quelque chose.


14

Comme d'autres affiches l'ont déjà indiqué; votre horloge système n'est probablement pas synchronisée jusqu'à quelques microsecondes avec l'heure mondiale réelle. Néanmoins, les horodatages de précision à la microseconde sont utiles en tant qu'hybride pour à la fois indiquer l'heure actuelle du mur et mesurer / profiler la durée des choses.

J'étiquette tous les événements / messages écrits dans un fichier journal en utilisant des horodatages tels que "2012-10-21 19: 13: 45.267128". Ceux-ci transmettent à la fois le moment où cela s'est produit (temps "mur") et peuvent également être utilisés pour mesurer la durée entre cet événement et le prochain événement dans le fichier journal (différence relative en microsecondes).

Pour ce faire, vous devez lier System.currentTimeMillis () avec System.nanoTime () et travailler exclusivement avec System.nanoTime () à partir de ce moment. Exemple de code:

/**
 * 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'idée est bonne, mais vous devez vous resynchroniser de temps en temps. L'heure système (currentTime) et l'horloge du processeur (nanoTime) ne sont PAS synchronisées, car elles sont souvent basées sur des horloges matérielles différentes. De plus, le serveur de temps de votre système d'exploitation resynchronise l'horloge système avec une source de temps externe, si disponible. Par conséquent, votre classe apparemment très précise produira des horodatages qui pourraient être loin!
h2stein

3
Ce code ne semble pas être thread-safe. Deux appels simultanés MicroTimestamp.INSTANCE.get()pourraient être problématiques.
Ahmed

@Ahmed Pourquoi pensez-vous que le code n'est pas thread-safe? Il n'y a rien dans la get()méthode qui met à jour les variables membres; il n'utilise que des variables locales temporaires.
Jason Smith le

6

Vous pourriez peut-être créer un composant qui détermine le décalage entre System.nanoTime () et System.currentTimeMillis () et obtenir effectivement des nanosecondes depuis l'époque.

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

Le test suivant produit d'assez bons résultats sur ma machine.

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

La différence semble osciller dans une plage de + -3 ms. Je suppose que l'on pourrait modifier un peu plus le calcul du décalage.

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

2

Si vous êtes intéressé par Linux: Si vous récupérez le code source vers "currentTimeMillis ()", vous verrez que, sous Linux, si vous appelez cette méthode, elle récupère une microseconde. Cependant Java tronque ensuite les microsecondes et vous rend les millisecondes. C'est en partie parce que Java doit être multiplateforme, donc fournir des méthodes spécifiquement pour Linux était un grand non-non à l'époque (rappelez-vous que le support des liens souples à partir de la version 1.6 à l'envers?!). C'est aussi parce que, même si votre horloge peut vous rendre des microsecondes sous Linux, cela ne signifie pas nécessairement que ce sera bon pour vérifier l'heure. Au niveau de la microseconde, vous devez savoir que NTP ne réaligne pas votre temps et que votre horloge n'a pas trop dérivé lors des appels de méthode.

Cela signifie qu'en théorie, sous Linux, vous pouvez écrire un wrapper JNI identique à celui du package System, mais sans tronquer les microsecondes.


2

une solution "rapide et sale" avec laquelle j'ai finalement opté:

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

METTRE À JOUR:

Je suis allé à l'origine avec System.nanoTime, mais j'ai découvert qu'il ne devrait être utilisé que pendant le temps écoulé, j'ai finalement changé mon code pour qu'il fonctionne avec des millisecondes ou à certains endroits, utilisez:

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

mais cela ajoutera simplement des zéros à la fin de la valeur (micros = millis * 1000)

A laissé cette réponse ici comme un "signe d'avertissement" au cas où quelqu'un d'autre penserait à nanoTime :)


1
Le document dit explicitement de ne pas utiliser System.nanoTimepour l'heure de l'horloge murale: "Cette méthode ne peut être utilisée que pour mesurer le temps écoulé et n'est liée à aucune autre notion de temps système ou d'horloge murale."
Basil Bourque

1
@BasilBourque vous avez raison, après avoir écrit ceci, j'ai finalement découvert et changé mon code mais j'ai oublié de mettre à jour ici, merci pour le commentaire!
keisar

2

Java prend en charge les microsecondes via TimeUnitenum.

Voici la documentation java: Enum TimeUnit

Vous pouvez obtenir des microsecondes en java de cette manière:

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

Vous pouvez également convertir des microsecondes en une autre unité de temps, par exemple:

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

Ce n'est pas correct, vous obtiendrez l'heure en résolution / longueur MICRO, mais l'heure elle-même sera toujours uniquement avec une précision MILI (ce qui signifie que les 3 derniers chiffres seront toujours 0).
Michalsx

0

Si vous avez l'intention de l'utiliser pour un système en temps réel, java n'est peut-être pas le meilleur choix pour obtenir l'horodatage. Mais si vous allez utiliser if pour une clé unique, la réponse de Jason Smith fera assez. Mais juste au cas où, pour anticiper 2 éléments finissent par obtenir le même horodatage (c'est possible si ces 2 ont été traités presque simultanément), vous pouvez boucler jusqu'à ce que le dernier horodatage ne soit pas égal à l'horodatage actuel.

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

0

Utilisez Instant pour calculer les microsecondes depuis Epoch:

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


-3

Voici un exemple de création d'un horodatage courant UnsignedLong:

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

2
Réponse incorrecte. La classe java.util.Date a une résolution en millisecondes, et non en microsecondes spécifiée dans la question. Faire un java.sql.Timestamp ne tire pas de données supplémentaires hors de l'air.
Basil Bourque
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.