Как я могу получить текущее время TAI в миллисекундах в Linux, используя Java или C ++?
Причина, по которой я нуждаюсь в этом, заключается в том, чтобы иметь возможность точно брать метки времени в течение длительного периода времени (порядка лет) и при этом иметь возможность сравнивать их, не беспокоясь о дополнительных секундах. Можно выполнить несколько измерений в течение високосной секунды, и все измерения должны быть однозначными, монотонно увеличивающимися и линейно увеличивающимися. Это будет выделенный сервер Linux. Это для научного проекта, который требует точности около 0,5 секунд.
В настоящее время я не хочу вкладывать деньги в GPS-хронометрист и надеюсь использовать NTP для pool.ntp.org, чтобы поддерживать системные часы в рабочем состоянии.
Я рассмотрел следующие решения:
Java 8 или проект ThreeTen
Единственный способ получить TAIInstant — это использовать Мгновенное действие, а затем преобразовать его, что, согласно спецификациям, «Преобразование из Мгновенного действия не будет абсолютно точным в ближайшей секунде в соответствии с UTC-SLS». Это само по себе не имеет большого значения (на самом деле, использование UTC-SLS также было бы приемлемо). Однако использование now () в классе Instant также, по-видимому, является просто оберткой для System.currentTimeMillis (), что заставляет меня думать, что в течение високосной секунды время все еще будет неоднозначным, и проект фактически не даст мне время TAI , В спецификациях Java 8 также указано:
Реализации шкалы времени Java с использованием API JSR-310 не
требуется предоставить любые часы, которые с точностью до секунды, или что
прогрессирует монотонно или плавно. Реализации поэтому
не требуется на самом деле выполнить UTC-SLS убить или иным образом
осознавать високосные секунды.
Используя право /? часовой пояс
Кажется, что это будет работать, однако я не уверен, достаточно ли умна реализация, чтобы продолжать работать в течение секунды, или если System.currentTimeMillis () даже даст время TAI. Другими словами, будет ли базовая реализация по-прежнему использовать UTC, давая тем самым неоднозначное время в течение високосной секунды, которая затем преобразуется в TAI, или же использование правого / часового пояса фактически работает с TAI с использованием System.currentTimeMillis () всегда (т.е. даже во время второй прыжок)?
Использование CLOCK_TAI
Я попытался использовать CLOCK_TAI в ядре Linux, но обнаружил, что он полностью идентичен CLOCK_REALTIME в моем тесте:
Код:
#include <iostream>
#include <time.h>
long sec(int clock)
{
struct timespec gettime_now;
clock_gettime(clock, &gettime_now);
return gettime_now.tv_sec;
}
int main()
{
std::cout << sec(0) << std::endl; // CLOCK_REALTIME
std::cout << sec(1) << std::endl; // CLOCK_MONOTONIC
std::cout << sec(11) << std::endl; // CLOCK_TAI
return 0;
}
Выход был просто:
1427744797
6896
1427744797
Использование CLOCK_MONOTONIC
Проблема заключается в том, что временные метки должны оставаться действительными и сопоставимыми, даже если компьютер перезагружается.
В дополнение к правильному принятому ответу я бы также упомянул бесплатный Библиотека Java Time4J (минимальная версия v4.1) как возможное решение, потому что
java.time
не могу сделать все), Он использует монотонные часы на основе System.nanoTime()
но даже позволяет пользовательские реализации через интерфейс TickProvider
, В целях калибровки вы можете использовать net.time4j.SystemClock.MONOTONIC
или вы используете SNTP-часы с именем SntpConnector
который просто нуждается в некоторой простой конфигурации для подключения к любому NTP-серверу времени, который вы хотите. А благодаря встроенной таблице високосных секунд Time4J может даже показать вам объявленную високосную секунду в конце этого месяца — в формате ISO-8601 или даже в виде отформатированной строки местного времени в любом часовом поясе (с использованием модуля i18n) ,
Возможна повторная калибровка (в случае NTP — переподключения) часов, что означает, что часы можно адаптировать к промежуточным настройкам времени (хотя я настоятельно рекомендую не делать этого во время ваших измерений или во время високосной секунды). Хотя такое переподключение часов SNTP, как правило, вызывает замедление времени в некоторых случаях, Time4J пытается применить алгоритм сглаживания (если он активирован в конфигурации часов), чтобы обеспечить монотонное поведение. Подробная документация доступна онлайн.
Пример:
// Step 0: configure your clock
String ntpServer = "ptbtime1.ptb.de";
SntpConnector clock = new SntpConnector(ntpServer);
// Step 1: Timestamp start of the program and associate it with a counter
clock.connect();
// Step 2: Use the counter for sequential measurements at fixed intervals
Moment m = clock.currentTime();
System.out.println(m); // possible output = 2015-06-30T23:59:60,123456789Z
// Step 3: Timestamp new counter value(s) as necessary to keep your data adequately synced
clock.connect();
Я сомневаюсь, что какое-либо решение на основе C ++ является более простым. Другие демонстрации кода также можно изучить на DZone.
Обновление (ответ на вопрос в комментарии):
Несколько упрощенное решение о том, как автоматически загружать данный IETF-ресурс для новых високосных секунд и переводить его в специфичный для Time4J формат, может выглядеть следующим образом:
URL url = new URL("https://www.ietf.org/timezones/data/leap-seconds.list");
BufferedReader br =
new BufferedReader(
new InputStreamReader(url.openStream(), "US-ASCII"));
String line;
PlainDate expires = null;
Moment ntpEpoch = PlainTimestamp.of(1900, 1, 1, 0, 0).atUTC();
List<PlainDate> events = new ArrayList<PlainDate>();
try {
while ((line = br.readLine()) != null) {
if (line.startsWith("#@")) {
long expraw = Long.parseLong(line.substring(2).trim());
expires = ntpEpoch.plus(
expraw, TimeUnit.SECONDS)
.toZonalTimestamp(ZonalOffset.UTC).toDate();
continue;
} else if (line.startsWith("#")) {
continue; // comment line
}
// this works for some foreseeable future
long epoch = Long.parseLong(line.substring(0, 10));
// this is no leap second
// but just the official introduction of modern UTC scale
if (epoch == 2272060800L) {
continue;
}
// -1 because we don't want to associate
// the leap second with the following day
PlainDate event =
ntpEpoch.plus(epoch - 1, TimeUnit.SECONDS)
.toZonalTimestamp(ZonalOffset.UTC).toDate();
events.add(event); // we don't assume any negative leap seconds here for simplicity
}
} finally {
br.close();
}
// now let's write the result into time4j-format
// use a location relative to class path of main program (see below)
String path = "C:/work/leapseconds.txt";
Writer writer = new FileWriter(new File(path));
String sep = System.getProperty("line.separator");
try {
for (PlainDate event : events) {
writer.write(event + ", +" + sep);
}
writer.write("@expires=" + expires + sep);
} finally {
writer.close();
}
System.out.println(
"Leap second file was successfully written from IETF-resource.");
// And finally, we can start the main program in a separate process
// with the system property "net.time4j.scale.leapseconds.path"// set to our leapsecond file path (must be relative to class path)
Некоторые заметки:
Я рекомендую написать этот код как подпрограмму, вызываемую простой пакетной программой, чтобы избежать зависимости основной программы от интернет-соединения. Этот пакетный файл наконец вызовет основную программу с указанным системным свойством. Если вы установите это имущество затем будут считаны дополнительные секунды из указанного там файла, и любой доступный в конечном итоге tzdata-модуль перестанет выдавать любую параллельную информацию о дополнительных секундах.
Причина, по которой мне это нужно, заключается в том, чтобы иметь возможность точно брать отметки времени
в течение длительного периода времени (порядка лет) и все еще сможет
сравнить их, не беспокоясь о високосных секундах. Это возможно
для нескольких измерений в течение високосной секунды и все
измерения должны быть однозначными, монотонно увеличивающимися и
линейно увеличивается.
Тогда ваш дизайн будет неоптимальным. Вы не можете использовать время, а затем каким-то образом вмешиваться в високосные секунды. Это на самом деле происходит достаточно часто, и люди попадают в одну и ту же ловушку измерений с использованием настенных часов.
Если вы избегаете отметки времени в течение 1 секунды, когда может произойти скачок (полночь!), Вы свободны дома, поскольку их можно настроить позже.
Теперь, если вы настаиваете на использовании TAI без счетчика, все, что вам нужно, это таблица с дополнительными секундами, которые необходимо учитывать. Тогда просто используйте монотонное время. Есть также библиотеки, которые могут сделать это для вас, но они могут быть устаревшими, поэтому вам придется поддерживать их самостоятельно,
CLOCK_REALTIME
а также CLOCK_TAI
вернуть то же самое, потому что параметр ядра tai_offset
это ноль.
Проверьте с помощью adjtimex(timex tmx)
и прочитайте значение. я думаю что ntpd
установит его, если он достаточно новый (>4.2.6
) и имеет второй файл. Он также может быть в состоянии получить его от вышестоящих серверов, но я не смог проверить. Вызов adjtimex()
можно установить tai_offset
вручную при запуске от имени пользователя root. Вам понадобится новый выпуск man
страница для adjtimex
чтобы увидеть параметры для установки. Мой дебиан man
Страница была слишком старой, но команда работала.
Вы должны реализовать часы TAI на основе C ++ std :: stable_clock или подобного. Для синхронизации часов TAI вы можете использовать GPS или NTP.
Вариант TAI от NTP: вашей реализации TAI потребуются знания о високосных секундах. Возможно протокол NTP или ссылочные ресурсы являются наиболее надежными источниками текущих и будущих високосных секунд.
Опция TAI от GPS: часы GPS имеют фиксированное смещение к TAI, вам не нужно связываться с високосными секундами