【问题标题】:Nanoseconds & Chrono C++纳秒和计时 C++
【发布时间】:2020-05-01 02:05:24
【问题描述】:
2018-10-01 00:06:16.700000000

我有一个带有上述时间戳的时间序列数据文件。 我需要将其转换为从纪元开始的纳秒,然后我需要将毫秒、微米或纳秒添加到时间戳(移位)。最后,对于选择记录,将其恢复为上述格式。

我在创建时间点时遇到问题 - 以及如何表示纳秒......它适用于微秒。

我能否就下面的代码片段请求一些帮助......以及一旦我从纪元获得纳秒 - 我如何回到上面的时间戳。

std::string ts("23 01 2020 20:59:59.123456789");

XXXX (ts);

void XXXXX (string timestamp)
{
     stringstream temp_ss(timestamp);

     tm temp_time_object = {};

     temp_ss >> get_time (&temp_time_object, "%Y/%m/%d %H:%M:%S");

     chrono::system_clock::time_point temp_time_point = system_clock::from_time_t(mktime(&temp_time_object));
    // chrono::high_resolution_clock::time_point temp_time_point1 = temp_time_point;

    auto  nsecs           =  stol (timestamp.substr (timestamp.find_first_of('.')+1,9));

// +++ This is where I GET stuck forming the time_point....
// I've tried this so many different ways .... 
// Is it that the system_clock cannot accept nanos? 


    temp_time_point += nanoseconds (nsecs);

     auto micro_from_epoch = duration_cast<nanoseconds> (temp_time_point.time_since_epoch()).count();

     cout << micro_from_epoch << endl;

}

【问题讨论】:

  • 请发布您收到的错误消息
  • 上述代码的问题是纳秒内没有 += 运算符。如果我尝试以 1:100000000 的比率显式添加使用持续时间 - 它仍然抱怨我无法将其添加到时间点。
  • 您的基于字符串的时间戳是本地时间还是 UTC?如果是本地时间,是您计算机当前的本地时区设置,还是其他时区?
  • 假设所有时间戳都是 GMT, UTC + 0

标签: c++ datetime chrono


【解决方案1】:

我在创建时间点时遇到问题 - 以及如何表示纳秒......它适用于微秒。

这告诉我你的system_clock::time_point 的精度比纳秒还粗(在 llvm 上是微秒,在 Windows 上是 1/10 微秒)。将纳秒添加到这样的time_point 的最简单方法是:

auto tp = temp_time_point + nanoseconds (nsecs);

这形成了一个time_point,它仍然基于system_clock,但具有system_clock::durationnanoseconds 的“通用类型”的精度,实际上,它只是nanoseconds

假设所有时间戳都是 GMT、UTC + 0

现在的问题是 mktimelocal tm 转换为 UTC time_t。但是您想从 UTC 字段类型转换为 UTC 序列类型。

这在 C++20 中很容易实现(我知道你还没有,听我说):

#include <chrono>
#include <iostream>
#include <sstream>

std::chrono::sys_time<std::chrono::nanoseconds>
XXXXX(std::string const& timestamp)
{
    using namespace std;
    using namespace std::chrono;
    istringstream temp_ss{timestamp};
    sys_time<nanoseconds> tp;
    temp_ss >> parse("%F %T", tp);
    return tp;
}

int
main()
{
    auto tp = XXXXX("2018-10-01 00:06:16.700000000");
    std::cout << tp.time_since_epoch().count() << "ns\n";
    std::string s = std::format("{:%F %T}", tp);
    std::cout << s << '\n';
}

这会将string 转换为chrono::time_point&lt;system_clock, nanoseconds&gt;,这与您的system_clock::time_point 明显不同,只是您的system_clock::time_point 的精度比nanoseconds 更粗。

然后format 用于将time_point 转换回string

输出:

1538352376700000000ns
2018-10-01 00:06:16.700000000

现在我知道,如今完全符合 C++20 &lt;chrono&gt; 的东西很少见(即将推出)。在它到达这里之前,有一个 C++20 &lt;chrono&gt; preview library 与 C++11 兼容。它是免费和开源的。并且只需要很少的句法更改:

#include "date/date.h"
#include <chrono>
#include <iostream>
#include <sstream>

date::sys_time<std::chrono::nanoseconds>
XXXXX(std::string const& timestamp)
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;
    istringstream temp_ss{timestamp};
    sys_time<nanoseconds> tp;
    temp_ss >> parse("%F %T", tp);
    return tp;
}

int
main()
{
    using namespace date;
    auto tp = XXXXX("2018-10-01 00:06:16.700000000");
    std::cout << tp.time_since_epoch().count() << "ns\n";
    std::string s = format("%F %T", tp);
    std::cout << s << '\n';
}

输出:

1538352376700000000ns
2018-10-01 00:06:16.700000000

【讨论】:

  • 您使用 date.h 的最后一个示例非常简单易懂。我只是尝试将其降低到 Mac OS。执行时出现错误:./bootstrap-vcpkg.sh .... 将在早上再次查看它,看看我是否可以解决问题... 错误回复:eval:第 162 行:查找时出现意外 EOF匹配`''
  • 对于这个用例,基于 CMake 的构建工具(或任何其他包管理工具)将导致比它解决的问题更多。 date.h 是一个只有头文件的库。只需包括它,你就可以开始了。如果这仍然不能解决您的问题,请告诉我,我会帮助您解决问题。
  • 如此简单 - 头文件完美运行......我在哪里可以找到日期格式说明符列表......例如,如果我的日期是:由'/'或其他格式分隔......我还注意到该库检测您是否提供毫秒 - 并将其转换为纳秒等...谢谢您的帮助。
  • 这里是所有的解析标志:howardhinnant.github.io/date/date.html#from_stream_formatting 格式标志非常相似,并且在一个单独的表格中,就在这个表格之上。
【解决方案2】:

您可以将文本直接转换为纳秒级分辨率。基本上有两个关键库:

  • CCTZ 由一些 Google 工程师提供,但(与许多项目一样)不是正式发布的 Google 产品

  • date,作者是 Howard Hinnant,他可能会在我完成打字之前在这里回答;他的库是 C++20 中此内容的基础

我已经为 R(通过 Rcpp)包装了这两个文件,并且有很多例子。但是这两个 repos 中也有示例,所以也许从那里开始?

因此,由于缺乏更好的直接 CCTZ 示例,这里是使用 R 包的示例;你会看到输入:

R> library(RcppCCTZ)
R> example(parseDatetime)

prsDttR> ds <- getOption("digits.secs")

prsDttR> options(digits.secs=6) # max value

prsDttR> parseDatetime("2016-12-07 10:11:12",        "%Y-%m-%d %H:%M:%S")   # full seconds
[1] "2016-12-07 10:11:12 UTC"

prsDttR> parseDatetime("2016-12-07 10:11:12.123456", "%Y-%m-%d %H:%M:%E*S") # fractional seconds
[1] "2016-12-07 10:11:12.123456 UTC"

prsDttR> parseDatetime("2016-12-07T10:11:12.123456-00:00")  ## default RFC3339 format
[1] "2016-12-07 10:11:12.123456 UTC"

prsDttR> now <- trunc(Sys.time())

prsDttR> parseDatetime(formatDatetime(now + 0:4))               # vectorised
[1] "2020-05-01 02:16:27 UTC" "2020-05-01 02:16:28 UTC"
[3] "2020-05-01 02:16:29 UTC" "2020-05-01 02:16:30 UTC"
[5] "2020-05-01 02:16:31 UTC"

prsDttR> options(digits.secs=ds)
R> 

调用的解析器函数是(并忽略与 R 相关的位)

Rcpp::DatetimeVector parseDatetime(Rcpp::CharacterVector svec,
                                   std::string fmt = "%Y-%m-%dT%H:%M:%E*S%Ez",
                                   std::string tzstr = "UTC") {
    cctz::time_zone tz;
    load_time_zone(tzstr, &tz);
    sc::system_clock::time_point tp;
    cctz::time_point<cctz::sys_seconds> unix_epoch =
        sc::time_point_cast<cctz::sys_seconds>(sc::system_clock::from_time_t(0));

    // if we wanted a 'start' timer
    //sc::system_clock::time_point start = sc::high_resolution_clock::now();

    auto n = svec.size();
    Rcpp::DatetimeVector dv(n, tzstr.c_str());
    for (auto i=0; i<n; i++) {
        std::string txt(svec(i));

        if (!cctz::parse(fmt, txt, tz, &tp)) Rcpp::stop("Parse error on %s", txt);

        // time since epoch, with fractional seconds added back in
        // only microseconds guaranteed to be present
        double dt = sc::duration_cast<sc::microseconds>(tp - unix_epoch).count() * 1.0e-6;

        // Rcpp::Rcout << "tp: " << cctz::format(fmt, tp, tz) << "\n"
        //             << "unix epoch: " << cctz::format(fmt, unix_epoch, tz) << "\n"
        //             << "(tp - unix.epoch).count(): " << (tp - unix_epoch).count() << "\n"
        //             << "dt: " << dt << std::endl;

        dv(i) = Rcpp::Datetime(dt);
    }

    return dv;
}

它查看传入的字符串向量svec 并转换每个字符串。

编辑:这是另一个使用我们的 nanotime 包的示例,它利用并使用了 CCTZ 解析器:

R> library(nanotime)
R> as.nanotime("2020-01-29 13:12:00.000000001 America/New_York")
[1] 2020-01-29T18:12:00.000000001+00:00
R> 

完整的 9 + 9 位精度,使用自纪元以来的底层纳秒,可与 std::chrono 完全互操作。

【讨论】:

  • 不,你对我来说太快了。 :-)
  • 哇。这是以前从未发生过的。谢谢你这么细心!
  • 在我添加的代码中 - 微秒和毫秒有效......您可以将其添加到时间点。我不知道如何创建具有纳秒分辨率的完整日期时间的时间点。您上面的示例适用于 micros - 而不是 nano。
  • 如果我没记错的话,可用的 micro 和 nano 之间的区别取决于实现/操作系统。我生活在 Linux 上,我们非常有纳秒,因为我共同编写了一个包nanotime,它利用了 (Rcpp)CCTZ(现在还有 (Rcpp)Date),它非常好地得到纳秒 - - 这些天我每天都用它。
  • 也许您指的是我展示的示例,我可能展示了一个糟糕的示例,因为它转换为 R 的默认时间类型,而那些 微秒。让我添加第二个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-06
  • 1970-01-01
  • 1970-01-01
  • 2011-05-17
  • 1970-01-01
相关资源
最近更新 更多