Поточний час у мікросекундах в Java


104

Чи існує у системі Unix спосіб отримати часову позначку з точністю на рівні мікросекунди на Java? Щось на зразок gettimeofdayфункції С.


6
Майте на увазі, що годинники комп'ютерів не встановлені ніде поблизу такого рівня точності - два годинники на різних комп’ютерах зазвичай відрізнятимуться хоча б на кілька мілісекунд, якщо ви не доклали певних зусиль для налаштування процедури синхронізації з високою точністю. (коли таке можливо навіть фізично можливо). Я не можу уявити, який би сенс знав час комп'ютера до мікросекунди. (Якщо ви намагаєтеся точно виміряти інтервал на одному комп’ютері, то впевнено, це абсолютно розумно, але вам не потрібна повна мітка часу.)
David Z

2
Також деякі версії Unix / Linux повертають лише 1 мілісекундну або 10 мілісекундну зернистість у полі мікросекунди.
Стівен C

Непрямим шляхом - через JDBC , якщо він підключений до такої бази даних, як Postgres, яка фіксує поточний час у мікросекундах.
Василь Бурк

2
Java 9 і пізніші версії:Instant.now()
Василь Бурк

Використовуйте Миттєвий обчислення мікросекунд з епохи: val instant = Instant.now(); val currentTimeMicros = instant.getEpochSecond() * 1000_000 + instant.getNano() / 1000;
Майкл М.

Відповіді:


148

Ні, Java не має такої здатності.

Він має System.nanoTime (), але це просто дає зміщення від деякого відомого часу. Тому, хоча ви не можете взяти абсолютне число від цього, ви можете використовувати його для вимірювання наносекундної (або більшої) точності.

Зауважте, що JavaDoc говорить, що хоча це забезпечує точність наносекунди, це не означає точність наносекунди. Тож візьміть кілька відповідно великого модуля повернутого значення.


3
Можна припустити, що причини Java не мають getTimeInMicroseconds () включають в себе 1) точність, недоступна на багатьох платформах, 2) повернення точності в мілісекундах при виклику мікросекунди призводить до проблем переносу додатків.
Стівен C

9
В Linux, System.nanoTime () викликає clock_gettime (CLOCK_MONOTONIC, _). Брайан Окслі занурився у вихідний код Java, щоб знайти цей самородок.
Девід Вебер

7
Ця відповідь застаріла . Реалізації OpenJDK та Oracle Java 9 оснащені новою реалізацією, Clockяка передбачає захоплення поточного моменту з роздільною здатністю, меншою за мілісекунди. На MacBook Pro Retina я отримую поточний час у мікросекундах (шість цифр десяткового дробу). Фактичні результати та точність залежать від основного обладнання годинника хост-комп'ютера.
Василь Бурк

1
@BasilBourque багато систем все ще працює на Java 8 або навіть раніше, оскільки немає необхідності мігрувати їх. Хоча відповідь застаріла, вона все ще корисна.
Драгас

1
@Dragas Насправді, потрібно було б перенести їх… щоб отримати поточний час у мікросекундах, як це задається цим Питанням.
Василь Бурк

78

тл; д-р

Java 9 і пізніших версій: До наносекунд, роздільна здатність під час захоплення поточного моменту. Це 9 цифр десяткового дробу.

Instant.now()   

2017-12-23T12: 34: 56.123456789Z

Щоб обмежитися мікросекундами , усікайте .

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

На практиці ви побачите лише мікросекунди, захоплені, .nowоскільки сучасні звичайні годинники апаратних засобів комп’ютера не є точними в наносекундах .

Деталі

Інші відповіді дещо застаріли в порівнянні з Java 8.

java.time

Java 8 і пізніші версії поставляються з java.time . Ці нові класи витісняють хибні клопітні класи часу та часу, що постачаються з найстарішими версіями Java, такими як java.util.Date/.Calendar та java.text.SimpleDateFormat. Рамки визначені JSR 310, натхненний Joda-Time , розширений проектом ThreeTen-Extra.

Класи java.time дорівнюють наносекундам , набагато тоншим, ніж мілісекунди, що використовуються як старими класами дати, так і Joda-Time. І тонше, ніж мікросекунди, задані у Питання.

введіть тут опис зображення

Clock Впровадження

Хоча класи java.time підтримують дані, що представляють значення в наносекундах, класи ще не генерують значення в наносекундах. Ці now()методи використовують ту ж саму стару реалізацію годин , як і старі класи дати і час, System.currentTimeMillis(). У нас є новеClock інтерфейс в java.time, але реалізація для цього інтерфейсу - це той самий старий годинник мілісекунд.

Таким чином, ви можете відформатувати текстове зображення результату, ZonedDateTime.now( ZoneId.of( "America/Montreal" ) )щоб побачити дев'ять цифр дробової секунди, але тільки перші три цифри матимуть такі цифри:

2017-12-23T12:34:56.789000000Z

Новий годинник на Java 9

Реалізації OpenJDK та Oracle Java 9 мають новий за замовчуванням Clock реалізацію з більш тонкою деталізацією, аж до повної наносекундної можливості класів java.time.

Дивіться випуск OpenJDK, Підвищити точність впровадження java.time.Clock.systemUTC () . Це питання успішно реалізовано.

2017-12-23T12:34:56.123456789Z

На MacBook Pro (Retina, 15-дюймовий, кінець 2013 року) з macOS Sierra, я отримую поточний момент в мікросекундах (до шести цифр десяткового дробу).

2017-12-23T12:34:56.123456Z

Апаратний годинник

Пам'ятайте, що навіть при новій більш точній Clockреалізації, ваші результати можуть залежати від комп'ютера. Java залежить від основного годинника апаратного забезпечення комп'ютера, щоб знати поточний момент.

  • Дозвіл апаратних годин змінюватися в широких межах. Наприклад, якщо апаратний годинник конкретного комп'ютера підтримує лише мікросекунди деталізацію , будь-які згенеровані значення дати матимуть лише шість цифр дробової секунди, а останні три цифри - нулі.
  • Точність апаратних годин змінюватися в широких межах. Тільки тому, що годинник генерує значення з кількох цифр десяткового дробу секунди, ці цифри можуть бути неточними, лише наближеннями, даними від фактичного часу, як це може бути прочитано з атомного годинника . Іншими словами, те, що ви бачите купу цифр праворуч від десяткової позначки, не означає, що ви можете довіряти минулому часу між такими показаннями, що відповідає справжній хвилині.

Вони можуть вирішити наносекунди, але вони все ще базуються на System.currentTimeMillis, тому вони просто додають купу 0 в кінці. Це зовсім не допомагає, якщо ви намагаєтеся отримати час за мікро / нано секунди, користувач може просто додати 0 в мілісекундний час вручну.
annedroiid

@annedroiid Я висвітлював це у своїй відповіді. У Java 8 ви можете зберігати моменти з роздільною здатністю наносекунд, але фіксувати поточний момент лише в мілісекундах. Відшкодовано в Java 9 з новою реалізацією Clock. Тим не менш, в Java 8 класи java.time корисні для зберігання значень, захоплених іншими джерелами, такими як мікросекунди в базі даних Postgres. Але остерігайтеся неточності значень у діапазоні мікросекунд та наносекунд; звичайне обладнання для комп’ютера може не переносити апаратний годинний годинник так точно. Значення з шістьма або дев'ятьма цифрами дробової секунди може бути неправдивим.
Василь Бурк

Хіба це не ChronoUnit.MICROS ?
Роланд

@Roland Так, тепер виправлено, дякую. FYI, на переповнення стека вам пропонується безпосередньо відредагувати відповідь, щоб виправити таку помилку.
Василь Бурк

55

Ви можете використовувати System.nanoTime():

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

отримати час в наносекундах, але це суворо відносна міра. Він не має абсолютного значення. Корисно лише для порівняння з іншими нано-часом, щоб виміряти, як довго щось потрібно зайняти.


14

Як уже вказано в інших плакатах; ваш системний годинник, ймовірно, не синхронізований до мікросекунд до фактичного світового часу. Тим не менш, мікросекундні точні позначки часу корисні як гібрид як для вказівки поточного часу стіни, так і для вимірювання / профілювання тривалості речей.

Я позначаю всі події / повідомлення, записані у файли журналу, використовуючи часові позначки на зразок "2012-10-21 19: 13: 45.267128". Вони передають як тоді, коли це сталося ("стінний" час), а також можуть бути використані для вимірювання тривалості між цією та наступною подією у файлі журналу (відносна різниця в мікросекундах).

Для цього вам потрібно зв’язати System.currentTimeMillis () з System.nanoTime () і працювати з цим моментом виключно з System.nanoTime (). Приклад коду:

/**
 * 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
Ідея хороша, але вам потрібно раз і раз повторно синхронізувати. Системний час (currentTime) та тактовий процесор (nanoTime) НЕ синхронізуються, оскільки вони часто базуються на різних апаратних тактових часах. Крім того, сервер часу вашої операційної системи повторно синхронізує системний годинник із зовнішнім джерелом часу, якщо він є. Тому ваш, здавалося б, дуже точний клас створить часові позначки, які можуть бути далеко!
h2stein

3
Цей код не здається безпечним для потоків. Два одночасні дзвінки до MicroTimestamp.INSTANCE.get()можуть бути проблематичними.
Ахмед

@Ahmed Чому ви вважаєте, що код не є безпечним для потоків? У get()методі немає нічого, що оновлює змінні члена; він використовує лише тимчасові локальні змінні.
Джейсон Сміт

6

Ви можете, можливо, створити компонент, який визначає зміщення між System.nanoTime () та System.currentTimeMillis () та ефективно отримувати наносекунди з епохи.

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

Наступний тест дає досить хороші результати на моїй машині.

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

Здається, різниця коливається в межах + -3 мс. Я думаю, можна було б трохи змінити розрахунок зміщення.

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

2

Якщо вас цікавить Linux: Якщо ви виведете вихідний код на "currentTimeMillis ()", ви побачите, що в Linux, якщо ви зателефонуєте за цим методом, він отримує мікросекундний час назад. Однак Java тоді обрізає мікросекунди і повертає вам мілісекунди. Частково це пояснюється тим, що Java повинна бути крос-платформою, тому надання методів, спеціально для Linux, було великим часом, коли не було (пам’ятайте, підтримка жорстких м'яких посилань від 1.6 назад ?!). Це також тому, що, хоча годинник може повернути мікросекунди в Linux, це не обов'язково означає, що це буде добре для перевірки часу. На мікросекундних рівнях вам потрібно знати, що NTP не переставляє ваш час і що ваш годинник не дуже сильно перемістився під час викликів методів.

Це означає, що теоретично в Linux можна писати оболонку JNI, яка є такою ж, як і в системному пакеті, але не усікати мікросекунди.


2

"швидке і брудне" рішення, з яким я врешті-решт пішов:

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

ОНОВЛЕННЯ:

Спочатку я працював з System.nanoTime, але потім я з'ясував, що його слід використовувати лише минулий час, я врешті змінив свій код на роботу з мілісекундами або в деяких місцях:

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

але це просто додасть нулі в кінці значення (micros = millis * 1000)

Залиште цю відповідь тут як "попереджувальний знак", якщо хтось інший думає про nanoTime :)


1
Документ явно говорить НЕ використовувати System.nanoTimeдля часу настінного годинника: «Цей метод може бути використаний тільки для вимірювання витраченого часу і не пов'язаний з яким - або іншим поняттям системи або часу на стіну годин.»
Василь Бурк

1
@BasilBourque ви праві, після написання цього я врешті-решт дізнався і змінив свій код, але забув оновити тут, дякую за коментар!
keisar

2

Java підтримує мікросекунди до кінця TimeUnit enum.

Ось java doc: Enum TimeUnit

Ви можете отримати мікросекунди в Java таким чином:

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

Ви також можете конвертувати мікросекунди назад в інші одиниці часу, наприклад:

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

Це неправильно, ви отримаєте час у роздільній здатності / довжині MICRO, але сам час завжди буде лише з точністю MILI (тобто останні три цифри завжди будуть 0).
Michalsx

0

Якщо ви збираєтесь використовувати його для системи в режимі реального часу, можливо, Java не найкращий вибір, щоб отримати позначку часу. Але якщо ви збираєтесь використовувати, якщо для унікального ключа, то відповіді Джейсона Сміта буде достатньо. Але на всякий випадок, якщо передбачити, що у 2-х пунктах буде отримано однакову мітку часу (можливо, якщо ці 2 були оброблені майже одночасно), ви можете циклічити, поки остання часова мітка не дорівнює поточній часовій марці.

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

0

Використовуйте Миттєвий обчислення мікросекунд з епохи:

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


-3

Ось приклад того, як створити поточну мітку UnsignedLong:

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

2
Неправильна відповідь. Клас java.util.Date має роздільну здатність мілісекунд, а не мікросекунд, зазначених у питанні. Створення java.sql.Timestamp не витягує з повітря додаткові дані.
Василь Бурк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.