Как узнать текущее время TAI?
Как я могу получить текущее время TAI в миллисекундах в Linux, используя Java или C++?
Причина, по которой я нуждаюсь в этом, заключается в том, чтобы иметь возможность точно брать временные метки в течение длительного периода времени (порядка лет) и при этом иметь возможность сравнивать их, не беспокоясь о дополнительных секундах. Можно сделать несколько измерений в течение високосной секунды, и все измерения должны быть однозначными, монотонно увеличивающимися и линейно увеличивающимися. Это будет выделенный сервер Linux. Это для научного проекта, который требует точности около 0,5 секунд.
В настоящее время я не хочу вкладывать деньги в GPS-хронометрист и надеюсь использовать NTP для pool.ntp.org, чтобы поддерживать системные часы в рабочем состоянии.
Я рассмотрел следующие решения:
Java 8 или проект ThreeTen Единственный способ получить TAIInstant - это использовать Instant, а затем преобразовать его, который, согласно спецификациям, "Преобразование из Instant" не будет полностью точным в мгновение ока в соответствии с UTC-SLS." Это само по себе не имеет большого значения (на самом деле, использование UTC-SLS также было бы приемлемо). Тем не менее, использование now() в классе Instant также кажется просто оберткой для System.currentTimeMillis(), что заставляет меня думать, что в течение високосной секунды время все еще будет неоднозначным, и проект фактически не даст мне времени TAI, В спецификациях Java 8 также указано:
Реализации шкалы времени Java с использованием API-интерфейса JSR-310 не обязаны предоставлять какие-либо часы с точностью до одной секунды или монотонно или плавно. Поэтому реализациям не требуется фактически выполнять UTC-SLS или иначе знать о дополнительных секундах.
Используя право /? timezone Кажется, что это будет работать, однако я не уверен, достаточно ли умна реализация, чтобы продолжать работать в течение секунды, или если 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 Проблема заключается в том, что временные метки должны оставаться действительными и сопоставимыми, даже если компьютер перезагружается.
4 ответа
В дополнение к правильному принятому ответу я бы также упомянул бесплатную библиотеку Java Time4J ( минимальная версия v4.1) как возможное решение, потому что
- Я написал это, чтобы заполнить пробел в мире Java (
java.time
не могу сделать все), - другие ответы, данные до сих пор, говорят только о C++ (но вы также спрашивали о Java),
- он работает в соответствии с теми же принципами, которые описаны @user3427419.
Он использует монотонные часы на основе 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-модуль перестанет выдавать любую одновременную информацию о дополнительных секундах.
CLOCK_REALTIME
а также CLOCK_TAI
вернуть то же самое, потому что параметр ядра tai_offset
это ноль.
Проверьте с помощью adjtimex(timex tmx)
и прочитайте значение. я думаю что ntpd
установит его, если он достаточно новый (>4.2.6
) и имеет второй файл. Он также может быть в состоянии получить его от вышестоящих серверов, но я не смог проверить. Вызов adjtimex()
можно установить tai_offset
вручную при запуске от имени пользователя root. Вам понадобится новый выпуск man
страница для adjtimex
чтобы увидеть параметры для установки. Мой дебиан man
Страница была слишком старой, но команда работала.
Причина, по которой я нуждаюсь в этом, заключается в том, чтобы иметь возможность точно брать временные метки в течение длительного периода времени (порядка лет) и при этом иметь возможность сравнивать их, не беспокоясь о дополнительных секундах. Можно сделать несколько измерений в течение високосной секунды, и все измерения должны быть однозначными, монотонно увеличивающимися и линейно увеличивающимися.
Тогда ваш дизайн будет неоптимальным. Вы не можете использовать время, а затем как-то вмешиваться в високосные секунды. Это на самом деле происходит достаточно часто, и люди попадают в одну и ту же ловушку измерений с использованием настенных часов.
- Отметка времени запуска программы и связывание ее со счетчиком
- Используйте счетчик для последовательных измерений через фиксированные интервалы
- Отметка времени нового значения счетчика, необходимого для адекватной синхронизации ваших данных
Если вы избегаете отметки времени в течение 1 секунды, когда может произойти скачок (полночь!), Вы свободны дома, поскольку их можно настроить позже.
Теперь, если вы настаиваете на использовании TAI без счетчика, все, что вам нужно, - это таблица с дополнительными секундами, которые необходимо учитывать. Тогда просто используйте монотонное время. Есть также библиотеки, которые могут сделать это для вас, но они могут быть устаревшими, поэтому вам придется поддерживать их самостоятельно,
Вы должны реализовать часы TAI на основе C++ std::stable_clock или подобного. Для синхронизации часов TAI вы можете использовать GPS или NTP.
Вариант TAI от NTP: вашей реализации TAI потребуются знания о високосных секундах. Вероятно, протокол NTP или ссылочные ресурсы являются наиболее надежными источниками текущих и будущих дополнительных секунд.
Опция TAI от GPS: часы GPS имеют фиксированное смещение к TAI, вам не нужно связываться с високосными секундами