将 boost::posix_time::ptime 转换为 NTP 时间戳
Posted
技术标签:
【中文标题】将 boost::posix_time::ptime 转换为 NTP 时间戳【英文标题】:Convert boost::posix_time::ptime to NTP datestamp 【发布时间】:2017-02-23 16:52:52 【问题描述】:我需要将boost::posix_time::ptime
转换为 NTP 日期戳
RFC 5905 由以下结构表示:
struct NtpDatestamp
std::int32_t era_number;
std::uint32_t seconds_since_era_epoch;
std::uint64_t fraction_of_second;
;
RFC 5905 声明如下:
将任何格式的系统时间转换为 NTP 日期和时间戳格式 要求从黄金纪元到系统的秒数
s
时间确定。给定s
,确定整数era
和timestamp
,era = s / 2^(32) and timestamp = s - era * 2^(32),
适用于正数和负数日期。根据时代确定
s
和时间戳,s = era * 2^(32) + timestamp.
因此我尝试了以下方法:
const auto system_time = boost::posix_time::time_from_string("1899-12-31 00:00:00.000");
const boost::posix_time::ptime prime_epochboost::gregorian::date1900, 1, 1;
// Calculate the number of seconds from the prime epoch to the system time.
const boost::posix_time::time_duration time_durationsystem_time - prime_epoch;
const std::int64_t stime_duration.total_seconds();
const std::int32_t era_numberstatic_cast<std::int32_t>(s / std::pow(2, 32));
const std::uint64_t seconds_since_era_epochstatic_cast<std::uint64_t>(s - s / std::pow(2, 32) * std::pow(2, 32));
// The fraction of a NTP Datestamp is measured in Attoseconds.
const std::uint64_t fraction_of_secondstatic_cast<std::uint64_t>(time_duration.total_microseconds() * 1e12);
但这会给出不正确的结果。
我现在完全被这个(实际上很简单)问题难住了。
有人可以引导我走向正确的方向吗?如何从boost::posix_time::ptime
获取 NTP 时间戳的纪元数、纪元偏移和分数?
编辑: RFC 5905 中的计算不够准确,或者我确实误解了它们。感谢 cmets,我将计算更改为以下(这次是一个完整的示例):
#include <cmath>
#include <cstdint>
#include <iostream>
#include <boost/date_time.hpp>
int main()
const auto system_time =
boost::posix_time::time_from_string("1899-12-31 00:00:00.000");
const boost::posix_time::ptime prime_epoch
boost::gregorian::date1900, 1, 1;
// Calculate the number of seconds from the prime epoch to the system time.
const boost::posix_time::time_duration time_durationprime_epoch -
system_time;
// s is correctly determined now.
std::int64_t stime_duration.total_seconds();
if (prime_epoch > system_time)
// boost::posix_time::time_duration does not take the sign into account.
s *= -1;
// TODO(wolters): The following calculations do not return the correct
// results, but the RFC 5905 states them
const std::int32_t erastatic_cast<std::int32_t>(s / std::pow(2, 32));
const std::uint64_t timestamp
static_cast<std::uint64_t>(s - era * std::pow(2, 32));
// The fraction of a NTP Datestamp is measured in Attoseconds.
// TODO(wolters): `boost::posix_time::ptime` does NOT resolve to attoseconds,
// but doesn't the target format expect the value to be specified as
// attoseconds? Doesn't the following depend on Boost compile options?
const std::uint64_t fraction
static_cast<std::uint64_t>(time_duration.fractional_seconds());
std::cout << "s = " << std::dec << s << '\n';
// TODO(wolters): This does still not match the expected results; taken from
// Figure 4 of https://www.ietf.org/rfc/rfc5905.txt
std::cout << "Era (expected: -1) = " << std::dec << era << '\n';
std::cout << "Timestamp (expected: 4294880896) = " << std::dec << timestamp
<< '\n';
std::cout << "Fraction (expected: 0) = " << std::dec << fraction << '\n';
s
现在计算正确,但其他计算错误。我想我确实完全错过了一些重要的事情......
【问题讨论】:
一个问题:seconds_since_era_epoch 应该计算 s - era_number * 2^32,但是你计算它的方式总是为零。 第二个问题:fraction_of_second 应该使用 time_duration.fractional_seconds() 第三个问题:ptime 无法解析到 attoseconds... 【参考方案1】:看来我已经自己找出了缺失的部分。我在可重用类ntp::Datestamp
中实现了以下算法,并使用 RFC 5905 的参考日期对其进行了单元测试。所有测试最终都是绿色的。这是解决方案:
#include <cmath>
#include <cstdint>
#include <ctime>
#include <iostream>
#include <boost/date_time.hpp>
static std::time_t to_time(const boost::posix_time::ptime& time)
static const boost::posix_time::ptime epoch_time
boost::gregorian::date1970, 1, 1;
const boost::posix_time::time_duration difftime - epoch_time;
return (diff.ticks() / diff.ticks_per_second());
int main()
const auto system_time =
boost::posix_time::time_from_string("1899-12-31 00:00:00.123");
const boost::posix_time::ptime prime_epoch
boost::gregorian::date1900, 1, 1;
// Calculate the number of seconds from the prime epoch to the system time.
std::time_t sto_time(system_time) - to_time(prime_epoch);
const std::int32_t erastatic_cast<std::int32_t>(std::floor(s / std::pow(2, 32)));
const std::uint32_t timestamp
static_cast<std::uint32_t>(s - era * std::pow(2, 32));
const std::uint64_t fractionstatic_cast<std::uint64_t>(
system_time.time_of_day().fractional_seconds());
std::cout << "s = " << std::dec << s << '\n';
std::cout << "Era (expected: -1) = " << std::dec << era << '\n';
std::cout << "Timestamp (expected: 4294880896) = " << std::dec << timestamp
<< '\n';
std::cout << "Fraction (expected: 123000) = " << std::dec << fraction << '\n';
【讨论】:
以上是关于将 boost::posix_time::ptime 转换为 NTP 时间戳的主要内容,如果未能解决你的问题,请参考以下文章
Javascript 将正则表达式 \\n 替换为 \n,将 \\t 替换为 \t,将 \\r 替换为 \r 等等