我有一个开始存储在uint64中的时间值。这个数值是自1601年1月1日以来每隔100纳秒的时间。我知道Windows类型使用这种格式。我需要将这个uint64转换成某种对象,在那里我可以以字符串格式读取年份、日期、小时、分钟等,这样我就可以构建一个自定义日期时间字符串。
我怎样才能把那个uint64转换成有用的东西。我尝试将uint64转换为文件时的所有方法都会出现编译错误,例如
uint64 big_int; // this will end up containing the nanosecond interval time
.
.
.
FILETIME t = static_cast<FILETIME>(big_int);发布于 2014-01-03 00:59:47
我认为使用-Compatible低级数据算法、实用程序和C++11 <chrono>工具执行此计算是一个挑战。
从-Compatible低级数据算法我们需要:
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三倍。
从实用程序我们需要:
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。
改变时代的唯一原因是利用-Compatible低级数据算法中的完全调试、高性能和非常健壮的公式。
此解决方案忽略了闰秒的存在。我猜Windows FILETIME也是如此。但是,如果没有,您可以通过构建一个将闰秒映射为datetime到添加的#秒的表来考虑它。例如,如果datetime超出了当前的闰秒表,则需要添加25秒才能得到现在和1601-01-01之间的“真”差。我的经验是,计算机正在使用Unix时间,这只是掩盖了闰秒。
用以下方法对其进行测试:
int
main()
{
std::cout << datetime_from_wtick(wtick(130330211760000005)) << '\n';
}它应给予:
2014-01-01 T 03:39:36.000000500它表示UTC时区中的datetime。
这一职位主要有两点:
<chrono>可以实际消除所有转换常量。这个例子只介绍了几个用于定义自定义std::chrono:duration的转换常量,以及一个划时代的移位。之后,机器就可以工作了,消除了常见的错误。更新
使用无符号的持续时间确实容易出错,63位wtick的范围也很广。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.477580700C++20更新
多年来,自从这个答案第一次发布以来,-Compatible低级数据算法演变成了一个免费开放源码日期/时间库,它进一步演变成一个C++标准提案,并被接受到C++20的<chrono>库中(您的供应商可能实现它,也可能尚未实现)。
其结果是,现在可以更容易地实现datetime_from_wtick:
std::string
datetime_from_wtick(wtick wt)
{
auto constexpr epoch_diff =
std::chrono::sys_days{} - std::chrono::sys_days{std::chrono::January/1/1601};
return std::format("{:%F T %T}", std::chrono::sys_time{wt - epoch_diff});
}这个简单的小函数提供了与前面所示完全相同的输出。但阅读和理解要容易得多。
https://stackoverflow.com/questions/20384402
复制相似问题