如何获取当前的TAI时间? [英] How to obtain current TAI time?

查看:187
本文介绍了如何获取当前的TAI时间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

How can I obtain the current TAI time in milliseconds in Linux using either Java or C++?

我需要这样做的原因是能够长时间准确地获取时间戳(以年为单位),并且仍然能够进行比较,而不必担心leap秒.有可能在a秒内进行多次测量,并且所有测量都必须是明确的,单调增加的和线性增加的.这将是一台专用的Linux服务器.这是一个科研项目,需要约0.5秒的精度.

The reason I need this is to be able to accurately take timestamps over a long period of time (on the order of years) and still be able to compare them, without worrying about leap seconds. It is possible for multiple measurements to take place during a leap second and all measurements need to be unambiguous, monotonically increasing, and linearly increasing. This will be a dedicated Linux server. This is for a scientific project which needs precision of about .5 seconds.

我目前不希望投资GPS计时器,并希望使用NTP来pool.ntp.org,以保持系统时钟正常.

I do not currently wish to invest in a GPS timekeeper and hope to use NTP to pool.ntp.org in order to keep the system clock on track.

我研究了以下解决方案:

I have looked into the following solutions:

Java 8或ThreeTen项目 获得TAIInstant的唯一方法是使用Instant,然后对其进行转换,根据规范,根据UTC-SLS,从Instant进行转换将不会完全精确到completely秒左右."它本身并不大(实际上,使用UTC-SLS也可以接受).但是,在Instant类中使用now()似乎也只是System.currentTimeMillis()的包装,这使我认为在the秒内,时间仍然是模棱两可的,并且该项目实际上不会给我TAI时间. Java 8规范还规定:

Java 8 or the ThreeTen Project The only way to obtain a TAIInstant is to use an Instant and then convert it which, according to the specs, "Conversion from an Instant will not be completely accurate near a leap second in accordance with UTC-SLS." That in and of itself is not a big deal (in fact, using UTC-SLS would also be acceptable). However, using now() in the Instant class also seems to just be a wrapper for System.currentTimeMillis(), which makes me think that during the leap second, the time will still be ambiguous and the project will not actually give me TAI time. The Java 8 specifications also state:

使用JSR-310 API实现Java时标不是 需要提供任何亚秒级精度的时钟,或者 单调或平稳地前进.因此实现 不需要实际执行UTC-SLS转换或以其他方式执行 知道leap秒.

Implementations of the Java time-scale using the JSR-310 API are not required to provide any clock that is sub-second accurate, or that progresses monotonically or smoothly. Implementations are therefore not required to actually perform the UTC-SLS slew or to otherwise be aware of leap seconds.

使用权利/?时区 这似乎可行,但是我不确定实现是否足够聪明以在to秒内继续工作,或者不确定System.currentTimeMillis()是否会给TAI时间.换句话说,底层实现是否仍将使用UTC,从而在the秒内给出一个不明确的时间,然后将该时间转换为TAI,或者使用正确的时区确实确实通过System.currentTimeMillis()与TAI一起使用(即,即使在second秒)?

Using a right/? timezone This seems like it would work, however I am not sure if the implementation is smart enough to continue working during a leap second or if System.currentTimeMillis() would even give TAI time. In other words, would the underlying implementation still use UTC, thus giving an ambiguous time during the leap second which is then converted to TAI, or does using a right/ timezone actually work with TAI using System.currentTimeMillis() always (ie even during leap second)?

使用CLOCK_TAI 我尝试在Linux内核中使用CLOCK_TAI,但在测试中发现它与CLOCK_REALTIME完全相同: 代码:

Using CLOCK_TAI I tried using CLOCK_TAI in the Linux kernel but found it to be completely identical to CLOCK_REALTIME in my test: Code:

#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 问题在于,即使计算机重新启动,时间戳也必须保持有效并具有可比性.

Using CLOCK_MONOTONIC The problem with this is that the timestamps need to remain valid and comparable even if the computer restarts.

推荐答案

除了正确的答案外,我还会提到免费的 Java库Time4J (

In addition to the correct accepted answer I would also mention the free Java library Time4J (min version v4.1) as possible solution because

  • 我写了它是为了填补Java世界中的空白(java.time不能全部解决),
  • 到目前为止给出的其他答案仅涉及C ++(但您也要求使用Java),
  • 它的工作原理与@ user3427419描述的原理相同.
  • I have written it to fill a gap in Java world (java.time cannot do all),
  • other answers given so far only talk about C++ (but you also asked for Java),
  • it works according to the same principles described by @user3427419.

它使用基于System.nanoTime()的单调时钟,但甚至允许通过接口TickProvider进行自定义实现.为了进行校准,可以使用net.time4j.SystemClock.MONOTONIC,也可以使用名为SntpConnector的SNTP时钟,只需进行一些简单的配置即可连接到所需的任何NTP时间服务器.借助内置的leap秒表,Time4J甚至可以在本月底向您显示已宣布的leap秒-以ISO-8601表示法,甚至可以是任何时区中格式化的本地时间戳字符串(使用i18n模块)

It uses a monotonic clock based on System.nanoTime() but even allows custom implementations via the interface TickProvider. For the purpose of calibration, you can either use net.time4j.SystemClock.MONOTONIC, or you use an SNTP-clock named SntpConnector which just needs some simple configuration to connect to any NTP-time-server you want. And thanks to the built-in leap-second-table Time4J can even show you the announced leap second at the end of this month - in ISO-8601-notation or even as formatted local timestamp string in any timezone (using i18n-module).

可以对时钟进行重新校准(如果是NTP-重新连接),这意味着可以将时钟调整为中间时间调整(尽管我强烈建议您在测量期间或leap秒内不要这样做).尽管重新连接SNTP时钟通常会导致时间倒退,但在某些情况下,Time4J会尝试应用平滑算法(如果在时钟配置中激活)以确保单调行为.详细文档可在在线中找到.

A recalibration (in case of NTP - reconnect) of the clocks is possible meaning the clocks can be adapted to intermediate time adjustments (although I strongly recommend not to do it during your measurements or during a leap second). Although such a reconnect of an SNTP clock would normally cause the time stepping back in some cases Time4J tries to apply a smoothing algorithm (if activated in clock configuration) to ensure monotone behaviour. Detailed documentation is available online.

示例:

// 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 上研究更多代码演示.

I doubt if any C++-based solution is more simple. More code demonstrations can also be studied on DZone.

更新(评论中的问题的答案):

一个稍微简化的解决方案,如下所示:如何自动为新的I秒下载给定的IETF资源并将其转换为Time4J特定的格式:

A slightly simplified solution how to automatically download the given IETF-resource for new leap seconds and to translate it into a Time4J-specific format might look like this:

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连接.该批处理文件最终将使用所提及的system属性调用主程序.如果您设置属性,则then秒将为从那里指定的文件中读取,然后任何最终可用的tzdata-module都将停止产生任何并发的leap秒信息.

I recommend to write this code as subprogram called by a simple batch program in order to avoid the main program being dependent on internet connectivity. This batch file would finally call the main program with the mentioned system property. If you set this property then the leap seconds will be read from the file specified there, and any eventually available tzdata-module would then stop to yield any concurrent leap second informations.

这篇关于如何获取当前的TAI时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆