首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >转换自1601年1月1日起至日期字符串的100纳秒间隔的uint64时间

转换自1601年1月1日起至日期字符串的100纳秒间隔的uint64时间
EN

Stack Overflow用户
提问于 2013-12-04 19:40:59
回答 1查看 2.7K关注 0票数 1

我有一个开始存储在uint64中的时间值。这个数值是自1601年1月1日以来每隔100纳秒的时间。我知道Windows类型使用这种格式。我需要将这个uint64转换成某种对象,在那里我可以以字符串格式读取年份、日期、小时、分钟等,这样我就可以构建一个自定义日期时间字符串。

我怎样才能把那个uint64转换成有用的东西。我尝试将uint64转换为文件时的所有方法都会出现编译错误,例如

代码语言:javascript
复制
uint64 big_int;  // this will end up containing the nanosecond interval time
.
.
.

FILETIME t = static_cast<FILETIME>(big_int);
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-01-03 00:59:47

我认为使用-Compatible低级数据算法实用程序和C++11 <chrono>工具执行此计算是一个挑战。

-Compatible低级数据算法我们需要:

代码语言:javascript
复制
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept;

它将y/m/d三倍转换为1970-01-01年以前的几天,并且:

代码语言:javascript
复制
template <class Int>
constexpr
std::tuple<Int, unsigned, unsigned>
civil_from_days(Int z) noexcept;

它将1970-01-01之前的几天转换为y/m/d三倍。

实用程序我们需要:

代码语言:javascript
复制
template <class To, class Rep, class Period>
To
floor(const std::chrono::duration<Rep, Period>& d);

它非常类似于std::chrono::duration_cast,只不过它向负无穷远,而不是向零旋转(这对舍入负值是有区别的)。

首先,我们需要一个datetime对象来保存我们想要的所有信息:

代码语言:javascript
复制
struct datetime
{
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
    int nanoseconds;
};

印刷设施便于演示(应按所需的任何格式定制):

代码语言:javascript
复制
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小时):

代码语言:javascript
复制
typedef std::chrono::duration
        <
            int,
            std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>
        > days;

其中一个代表问题陈述中的100纳秒间隔:

代码语言:javascript
复制
typedef std::chrono::duration
        <
            std::int64_t, std::ratio_multiply<std::ratio<100>, std::nano>
        > wtick;

现在,我们有了将std::uint64_t转换为datetime所需的所有工具(这个问题的主题):

代码语言:javascript
复制
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时间,这只是掩盖了闰秒

用以下方法对其进行测试:

代码语言:javascript
复制
int
main()
{
    std::cout << datetime_from_wtick(wtick(130330211760000005)) << '\n';
}

它应给予:

代码语言:javascript
复制
2014-01-01 T 03:39:36.000000500

它表示UTC时区中的datetime

这一职位主要有两点:

  1. 正确使用<chrono>可以实际消除所有转换常量。这个例子只介绍了几个用于定义自定义std::chrono:duration的转换常量,以及一个划时代的移位。之后,机器就可以工作了,消除了常见的错误。
  2. -Compatible低级数据算法有一些非常有用和有效的算法。

更新

使用无符号的持续时间确实容易出错,63位wtick的范围也很广。wtick::rep改为int64_t。这样做的结果要好得多:

代码语言:javascript
复制
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更新

多年来,自从这个答案第一次发布以来,-Compatible低级数据算法演变成了一个免费开放源码日期/时间库,它进一步演变成一个C++标准提案,并被接受到C++20的<chrono>库中(您的供应商可能实现它,也可能尚未实现)。

其结果是,现在可以更容易地实现datetime_from_wtick

代码语言:javascript
复制
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});
}

这个简单的小函数提供了与前面所示完全相同的输出。但阅读和理解要容易得多。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20384402

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档