如何使用Java或C ++获取Linux中当前的TAI时间(以毫秒为单位)?

我需要这个的原因是能够准确地获取很长一段时间(大约数年)的时间戳,并且仍然可以比较它们,而不必担心闰秒。 在闰秒期间可以进行多次测量,并且所有测量需要是明确的,单调增加的和线性增加的。 这将是一个专用的Linux服务器。 这是一个需要大约0.5秒精度的科学项目。

我目前不想投资GPS计时器,并希望将NTP用于pool.ntp.org,以保持系统时钟正常运行。

我研究了以下解决方案:

Java 8或ThreeTen项目获得TAIInstant的唯一方法是使用Instant然后转换它,根据规范,“根据UTC-SLS,从瞬间转换在闰秒附近不会完全准确。 “ 这本身并不重要(事实上,使用UTC-SLS也是可以接受的)。 但是,在Instant类中使用now()似乎也只是System.currentTimeMillis()的包装器,这让我觉得在闰秒期间,时间仍然是模棱两可的,项目实际上不会给我TAI时间。 Java 8规范还声明:

使用JSR-310 API实现Java时标不需要提供亚秒精确或单调或平滑进展的任何时钟 因此,实现不需要实际执行UTC-SLS转换或以其他方式了解闰秒。

使用右/? 时区这似乎可以工作,但我不确定实现是否足够聪明,可以在闰秒期间继续工作,或者System.currentTimeMillis()是否会给TAI时间。 换句话说,底层实现是否仍然使用UTC,从而在闰秒期间给出一个模糊的时间然后转换为TAI,或者使用右/时区实际上使用System.currentTimeMillis()实际使用TAI(即使在闰秒)?

使用CLOCK_TAI我尝试在Linux内核中使用CLOCK_TAI但发现它与我的测试中的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这个问题是,即使计算机重新启动,时间戳也需要保持有效和可比较。

#1楼 票数:4

CLOCK_REALTIMECLOCK_TAI返回相同的内容,因为内核参数tai_offset为零。

使用adjtimex(timex tmx)并读取值。 我认为ntpd会设置它,如果它足够新( >4.2.6 )并且有一个闰秒文件。 它也可以从上游服务器获取它,但我无法验证。 当以root tai_offset运行时,调用adjtimex()可以手动设置tai_offset 您将需要一个用于adjtimex的new-ish man页来查看要设置的参数。 我的debian man页太旧了,但命令有效。

#2楼 票数:3

我需要这个的原因是能够准确地获取很长一段时间(大约数年)的时间戳,并且仍然可以比较它们,而不必担心闰秒。 在闰秒期间可以进行多次测量,并且所有测量需要是明确的,单调增加的和线性增加的。

那你的设计不是最理想的。 你不能使用时间然后以某种方式插入闰秒。 这实际上经常出现,人们陷入了使用挂钟进行时间戳测量的同一陷阱。

  1. 程序的时间戳启动并将其与计数器关联
  2. 使用计数器以固定间隔进行顺序测量
  3. 为保持数据充分同步所需的时间戳新计数器值

如果你在1秒钟内避免时间戳,那么可以发生闰秒(午夜!),你可以免费回家,因为这些可以在以后调整。

现在如果你坚持使用没有计数器的TAI,你只需要一个需要考虑闰秒的表。 然后只使用单调时间。 还有一些库可以为您完成此操作,但它们可能已过时,因此您必须自己维护它们,

http://skarnet.org/software/skalibs/libstddjb/tai.html

#3楼 票数:3 已采纳

除了正确接受的答案,我还会提到免费的Java库Time4J最低版本v4.1 )作为可能的解决方案,因为

  • 我写它是为了填补Java世界的空白( java.time无法做到全部),
  • 到目前为止给出的其他答案只谈C ++(但你也要求Java),
  • 它的工作原理与@ user3427419描述的原理相同。

它使用基于System.nanoTime()的单调时钟,但甚至允许通过接口TickProvider进行自定义实现。 出于校准的目的,您可以使用net.time4j.SystemClock.MONOTONIC ,或者使用名为SntpConnector的SNTP时钟, SntpConnector需要一些简单的配置即可连接到您想要的任何NTP时间服务器。 由于内置的​​闰秒表Time4J甚至可以显示本月底公布的闰秒 - 以ISO-8601表示法,甚至可以在任何时区使用格式化的本地时间戳字符串(使用i18n-module) 。

时钟的重新校准(在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)

一些说明:

我建议将此代码编写为简单批处理程序调用的子程序,以避免主程序依赖于Internet连接。 该批处理文件最终将使用提到的系统属性调用主程序。 如果你设置了这个属性,那么将从那里指定的文件中读取闰秒,然后任何最终可用的tzdata-module将停止以产生任何并发的闰秒信息。

#4楼 票数:0

你必须实现一个基于C ++ std :: steady_clock或类似的TAI时钟。 要同步您的TAI时钟,您可以依靠GPS或NTP。

来自NTP的选项TAI:您的TAI实施需要有关闰秒的知识。 可能NTP协议或引用的资源是当前和未来闰秒的最可靠来源。

来自GPS的选项TAI:GPS时钟对TAI有固定的偏移,你不必乱七八糟

  ask by Daniel Centore translate from so

未解决问题?本站智能推荐:

1回复

Java时间超过linux时间(闰秒)

我已将Linux系统日期更改为2012年6月30日星期六23:50:00。更改日期后,我正在运行一个简单的java程序来记录系统日期。 问题是:Java在Linux系统时间之前提前25秒进行记录。 例如:Java日志:2012-07-01 00:02:15 INFO调度程序:19 - Sun
2回复

将TAI时间转换为Java Instant

我可以访问TAI时间(以秒和纳秒为单位)-我仅将其称为T 我现在想创建一个与此值T对应的java Instant类。 我一直在寻找合适的构造函数来做到这一点,但是没有运气。
1回复

CLOCK_TAI的时代是什么?

从Linux内核版本3.10开始,函数clock_gettime()现在接受CLOCK_TAI 。 我没有设法找到这个时钟的详细描述。 它的时代是什么? 编辑1 :刚刚比较了我的Linux 3.19操作系统上的CLOCK_REALTIME和CLOCK_TAI的输出,它返回完全相同的
1回复

查找自2004年1月1日00:00:00以来在Java中的TAI秒数

正如标题所述,我需要找到自2004年1月1日00:00:00(Java)以来的TAI 秒数 。 我刚刚了解到TAI是什么,我试图解决上述问题让我有些困惑。 我尝试过的: 我知道在Java中你可以使用System.currentTimeMillis()来获得1970年1月1日UTC(
2回复

如何在Java 7中获得国际原子时

我正在开发一个Java7项目,我们需要一个国际原子时间的时间戳。 我发现了一些与此相关的问题,指向JSR-310和ThreeTen Project(正在实现JSR-310): 如何在Java中获得GPS时间和TAI时间? http://www.coderanch.com/t/549
3回复

闰秒持续时间

我需要在固定的日期时间在我的代码中安排任务。 为此,我使用ScheduledExecutorService和方法schedule(Runnable command, long delay, TimeUnit unit); 如何根据闰秒计算此延迟? 目前我使用的是Duration.b
3回复

如何获得当前的日期和时间? [重复]

这个问题在这里已有答案: 如何在C ++中获取当前时间和日期? 23个答案 如何获得当前日期d / m / y。 我需要他们有3个不同的变量而不是一个,例如day=d; month=m; year=y; day=d; month=m; year=y; 。
5回复

如何获得当前时区?

在我看到的大多数例子中: 但我只想获得运行代码的机器的当前时区。 我不想硬编码时区名称。