从1601年1月1日起,将100纳秒间隔的uint64时间转换为日期时间字符串
Convert uint64 time for number of 100 nanosecond intervals since jan 1st 1601 to date time string
我有一个开始存储在uint64中的时间值。该值是自1601年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);
我认为使用chrono
-兼容的低级别日期算法、<chrono>
实用程序和C++11 <chrono>
工具执行此计算是一项挑战。
来自chrono
-兼容的低级别日期算法我们需要:
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>
实用程序我们需要:
template <class To, class Rep, class Period>
To
floor(const std::chrono::duration<Rep, Period>& d);
它与std::chrono::duration_cast
非常相似,只是它向负无穷大取整,而不是向零取整(对负值取整有影响)。
首先,我们需要一个日期时间对象来保存我们想要的所有信息:
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
兼容低级别数据算法中经过充分调试、高性能和极其稳健的公式。
这个解决方案忽略了闰秒的存在。我的猜测是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
s的转换常数和epoch移位。在那之后,机器就可以正常工作,消除了常见的错误。chrono
兼容的低级别数据算法有一些非常有用和有效的算法。
更新
使用无符号持续时间确实很容易出错,63位wtick
s的范围很大。将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
C++20更新
多年来,自从这个答案首次发布以来,chrono
-Compatible Low Level Date Algorithms演变成了一个免费的、开源的日期/时间库,它进一步演变成了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});
}
这个简单的小函数给出了与前面所示完全相同的输出。但它更容易阅读和理解。
- C++为构建时间获取QDateTime的可靠方法
- 从持续时间构造std::chrono::system_clock::time_point
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- while循环中while循环的时间复杂度是多少
- 使用简单类型列表实现的指数编译时间.为什么
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在已经使用Git的情况下减少编译时间
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何将包含epoch时间的十六进制字符串转换为time_t
- 从文本文件中读取时钟时间和事件时间并进行处理
- 具有未知值时的时间复杂性
- 如何减少花费的时间
- C++在变量给定的指定时间内关闭电脑
- rcpp函数中的清理时间很长
- C++:floor unix时间戳到UTC月份
- 如何在c++中录制具有精确帧时间戳的视频
- 在两台机器之间进行时间戳的最佳c++chrono函数是什么
- KMP算法和LPS表构造的运行时间
- 为什么std::互斥需要很长的、非常不规则的时间来共享
- 从1601年1月1日起,将100纳秒间隔的uint64时间转换为日期时间字符串