将boost :: posix_time :: ptime转换为NTP datetamp [英] Convert boost::posix_time::ptime to NTP datestamp
问题描述
我需要根据
I need to convert a boost::posix_time::ptime
into a NTP Datestamp according to
RFC 5905 represented by the following structure:
struct NtpDatestamp {
std::int32_t era_number;
std::uint32_t seconds_since_era_epoch;
std::uint64_t fraction_of_second;
};
RFC 5905规定以下内容:
RFC 5905 states the following:
要将任何格式的系统时间转换为NTP日期和时间戳格式,
要求从初始纪元到秒的秒数s
系统
的时间确定。要确定给定s
的整数era
和timestamp
,
era = s / 2^(32) and timestamp = s - era * 2^(32),
适用于正日期和负日期。在给定时代
和时间戳的情况下确定 s
,
which works for positive and negative dates. To determine s
given the era
and timestamp,
s = era * 2^(32) + timestamp.
因此,我尝试了以下操作:
Therefore I've tried the following:
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::date{1900, 1, 1}};
// Calculate the number of seconds from the prime epoch to the system time.
const boost::posix_time::time_duration time_duration{system_time - prime_epoch};
const std::int64_t s{time_duration.total_seconds()};
const std::int32_t era_number{static_cast<std::int32_t>(s / std::pow(2, 32))};
const std::uint64_t seconds_since_era_epoch{static_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_second{static_cast<std::uint64_t>(time_duration.total_microseconds() * 1e12)};
但这会给出错误的结果。
But that gives incorrect results.
此刻我完全被这个(实际上是简单的)问题所困扰。
I am completely stumped with this (actually simple) problem at the moment.
有人可以引导我朝正确的方向发展吗?如何从 boost :: posix_time获取NTP日期时间戳的时代编号,时代偏移和分数: :ptime
?
Can someone guide me into the correct direction? How can I obtain the era number, era offset and fraction of a NTP datestamp from a boost::posix_time::ptime
?
编辑:要么RFC 5905中的计算不够准确,否则我会误解它们。感谢注释,我将计算更改为以下内容(这次是一个完整的示例):
Either the calculations in RFC 5905 are not accurate enough or I do misinterpret them. Thanks to the comments I've changed the calculation to the following (this time a complete example):
#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::date{1900, 1, 1}};
// Calculate the number of seconds from the prime epoch to the system time.
const boost::posix_time::time_duration time_duration{prime_epoch -
system_time};
// s is correctly determined now.
std::int64_t s{time_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 era{static_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
现在正确,但是其他计算是错误的。我想我确实完全错过了一些重要的事情……
s
is calculated correctly now, but the other calculations are wrong. I think I do miss something important completely...
推荐答案
似乎我已经自己弄清楚了丢失的部分。我在可重用的类 ntp :: Datestamp
中实现了以下算法,并使用RFC 5905的引用日期对其进行了单元测试。所有测试最终都是绿色的。解决方法如下:
It seems that I've figured out the missing pieces by myself. I've implemented the following algorithm in a reusable class ntp::Datestamp
and unit tested it with the reference dates of RFC 5905. All tests finally are green. Here is the solution:
#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::date{1970, 1, 1}};
const boost::posix_time::time_duration diff{time - 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::date{1900, 1, 1}};
// Calculate the number of seconds from the prime epoch to the system time.
std::time_t s{to_time(system_time) - to_time(prime_epoch)};
const std::int32_t era{static_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 fraction{static_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 datetamp的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!