我有一个时间值,开始存储在uint64中。该值是自160年1月1日以来100纳秒间隔的数量。我了解Windows FILETIME类型使用此格式。我需要将这个uint64转换为一些对象,我可以用字符串格式读取年份,日期,小时,分钟等,这样我就可以构建一个自定义日期时间字符串。
如何将uint64转换为有用的东西。我尝试将uint64转换为文件时的所有方法都会出现编译错误,例如
uint64 big_int; // this will end up containing the nanosecond interval time
.
.
.
FILETIME t = static_cast<FILETIME>(big_int);
答案 0 :(得分:8)
我认为使用chrono
-Compatible Low-Level Date Algorithms,<chrono>
Utilities和C ++ 11 <chrono>
设施来执行此计算是一项挑战。
从chrono
-Compatible Low-Level Date Algorithms我们需要:
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept;
将y / m / d三元组转换为1970-01-01之前/之后的天数,并且:
template <class Int>
constexpr
std::tuple<Int, unsigned, unsigned>
civil_from_days(Int z) noexcept;
将1970-01-01之前/之后的天数转换为y / m / d三联。
从<chrono>
Utilities我们需要:
template <class To, class Rep, class Period>
To
floor(const std::chrono::duration<Rep, Period>& d);
与std::chrono::duration_cast
非常相似,只是它向负无穷大舍入而不是向零(对于舍入负值有所不同)。
首先,我们需要一个datetime对象来保存我们想要的所有信息:
struct datetime
{
int year;
int month;
int day;
int hour;
int minute;
int second;
int nanoseconds;
};
打印设施便于演示(应根据需要定制):
std::ostream&
operator<<(std::ostream& os, const datetime& dt)
{
char fill = os.fill();
os.fill('0');
os << dt.year << '-';
os << std::setw(2) << dt.month << '-';
os << std::setw(2) << dt.day << " T ";
os << std::setw(2) << dt.hour << ':';
os << std::setw(2) << dt.minute << ':';
os << std::setw(2) << dt.second << '.';
os << std::setw(9) << dt.nanoseconds;
os.fill(fill);
return os;
}
我选择了nanoseconds
的单位作为datetime
的精度,这有点矫枉过正。您可以轻松地将其设置为所需的任何内容。
接下来,可以方便地声明两个自定义std::chrono::duration
:
代表天数(恰好24小时):
typedef std::chrono::duration
<
int,
std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
> days;
一个代表问题陈述的100纳秒间隔:
typedef std::chrono::duration
<
std::int64_t, std::ratio_multiply<std::ratio<100>, std::nano>
> wtick;
现在,我们拥有了将std::uint64_t
转换为datetime
所需的所有工具,(此问题的主题):
datetime
datetime_from_wtick(wtick t)
{
// Get the number of days between 1601-01-01 and 1970-01-01
constexpr days epoch{days_from_civil(1601, 1, 1)};
// eppoch is a negative number of days, so add it to get time since 1970-01-01
wtick utc_time = t + epoch;
days d = floor<days>(utc_time); // Get #days before/since 1970-01-01
datetime r;
// Split #days into year/month/day
std::tie(r.year, r.month, r.day) = civil_from_days(d.count());
utc_time -= d; // Subtract off #days to leave hours:minutes:seconds.fractional
auto h = floor<std::chrono::hours>(utc_time); // Get hours
r.hour = h.count();
utc_time -= h; // Subtract off hours to leave minutes:seconds.fractional
auto m = floor<std::chrono::minutes>(utc_time); // Get minutes
r.minute = m.count();
utc_time -= m; // Subtract off minutes to leave seconds.fractional
auto s = floor<std::chrono::seconds>(utc_time); // Get seconds
r.second = s.count();
utc_time -= s; // Subtract off seconds to leave fractional seconds
std::chrono::nanoseconds ns = utc_time; // Get nanoseconds
r.nanoseconds = ns.count();
return r;
}
这个解决方案的工作原理是简单地将纪元转移到1970-01-01,然后截断天数,然后截断小时数,然后是分钟数等,直到我们下降到几分之一秒。天数进一步分为三个:y / m / d。
改变时代的唯一原因是利用chrono
-Compatible Low-Level Date Algorithms中完全调试,高性能和极其强大的公式。
此解决方案忽略了leap seconds的存在。我的猜测是Windows FILETIME也是如此。但是,如果没有,您可以通过构建一个将datetime
映射到#seconds以添加的表来考虑leap seconds。例如,如果datetime
超出当前leap seconds表,则添加25秒以获得now和1601-01-01之间的“真实”差异。我的经验是,计算机正在使用Unix Time,这只是leap seconds。
用以下方式测试全部:
int
main()
{
std::cout << datetime_from_wtick(wtick(130330211760000005)) << '\n';
}
应该给出:
2014-01-01 T 03:39:36.000000500
用于表示UTC时区中的datetime
。
这篇文章有两个主要观点:
使用正确,<chrono>
可用于虚拟消除所有转换常量。此示例仅介绍一些用于定义自定义std::chrono:duration
的转换常量和一个纪元转换。在此之后,机器正常工作,消除了常见的错误。
chrono
-Compatible Low-Level Date Algorithms有一些非常有用和有效的算法。
<强>更新强>
使用无符号持续时间确实容易出错,并且wtick
的63位有足够的范围。已将wtick::rep
更改为int64_t
。这会产生更好的结果:
int
main()
{
std::cout << datetime_from_wtick(wtick(0)) << '\n';
std::cout << datetime_from_wtick(wtick(864000000000)) << '\n';
std::cout << datetime_from_wtick(wtick(130330211760000005)) << '\n';
std::cout << datetime_from_wtick(wtick(0x7FFFFFFFFFFFFFFF)) << '\n';
}
1601-01-01 T 00:00:00.000000000
1601-01-02 T 00:00:00.000000000
2014-01-01 T 03:39:36.000000500
30828-09-14 T 02:48:05.477580700