【问题标题】:Convert std::chrono::time_point to unix timestamp将 std::chrono::time_point 转换为 unix 时间戳
【发布时间】:2013-01-08 10:00:45
【问题描述】:

我怎样才能在固定日期后获得std::chrono::duration?我需要这个将std::chrono::time_point 转换为unix 时间戳。

在XXX中插入代码

auto unix_epoch_start = XXX;
auto time = std::chrono::system_clock::now();
auto delta = time - unix_epoc_start;
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(delta).count();

我知道 time_point 有一个方法 time_since_epoch(),但不能保证这与 unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)相同。

【问题讨论】:

  • 这没有帮助吗? std::time_t t = std::chrono::system_clock::to_time_t(tp);(其中tp 的类型为time_point
  • @AndyProwl:不能保证(在非 POSIX 系统上)time_t 也基于 Unix 纪元。
  • 迈克是对的。这与 time_since_epoch() 的问题基本相同
  • @AndyProwl:mktime 以当地时间而不是 UTC 时间输入。我认为没有任何方法可以使用标准库将 UTC 日期/时间转换为 time_t
  • @MikeSeymour:是的,那么以某种方式使用gmttime() 怎么样?那个似乎适用于 UTC 时间

标签: c++ c++11 unix-timestamp chrono


【解决方案1】:

unix 时间戳定义为自 1970 年 1 月 1 日 UTC 以来的秒数,但不计算所有秒数。这有点荒谬,人们不得不怀疑它的意义何在,所以我同意这是一个愚蠢的问题。

无论如何,让我们看看time_ttime() 的一些平台文档。

Linux:

time() 以自 Epoch 1970-01-01 00:00:00 +0000 (UTC) 以来的秒数返回时间。

POSIX.1 使用一个公式定义自纪元以来的秒数,该公式近似于指定时间和纪元之间的秒数。这个公式考虑了所有能被 4 整除的年份都是闰年,但能被 100 整除的年份不是闰年,除非它们也能被 400 整除,在这种情况下它们就是闰年。该值与时间和纪元之间的实际秒数不同,因为闰秒和系统时钟不需要与标准参考同步。目的是自 Epoch 值以来对秒数的解释是一致的;请参阅 POSIX.1-2008 基本原理 A.4.15 了解更多基本原理。

Windows:

time 函数根据系统时钟返回自 1970 年 1 月 1 日午夜 (00:00:00) 以来经过的秒数,协调世界时 (UTC)。

Mac OS X:

函数 ctime()、gmtime() 和 localtime() 都作为参数 一个时间值,表示自 Epoch (00:00:00) 以来的秒数 UTC,1970 年 1 月 1 日;

asctime()、ctime()、difftime()、gmtime()、localtime() 和 mktime() 功能符合 ISO/IEC 9899:1990 (ISO C90''), and conform to ISO/IEC 9945-1:1996 (POSIX.1'') 提供选择的本地时区 不包含闰秒表(参见 zic(8))。

可以找到其他系统的类似文档,例如 AIX、HP-UX、Solaris 等。

因此,尽管在 C++ 中没有指定,但有一种简单且可广泛移植的方法来获取 Unix 时间戳:

auto unix_timestamp = std::chrono::seconds(std::time(NULL));

如果您想要自 1970 年 1 月 1 日 UTC 以来的毫秒数(同样不计算所有时间),那么您可以这样做:

int unix_timestamp_x_1000 = std::chrono::milliseconds(unix_timestamp).count();

请记住,这些值不是实时的,因此您通常不能在算术中使用 unix 时间戳。例如,减去 unix 时间戳不会给您准确的时间间隔秒数。或者如果你做了类似的事情:

std::chrono::steady_clock::now() - unix_timestamp;

您不会得到实际对应于 1970-01-01 00:00:00+0000 的时间点。


正如 Andy Prowl 建议的那样,你可以做一些愚蠢的事情:

// 1 Jan 1970 (no time zone)
std::tm c = { 0, 0, 0, 1, 0, 70, 0, 0, -1};

// treat it as 1 Jan 1970 (your system's time zone) and get the
// number of seconds since your system's epoch (leap seconds may
// or may not be included)
std::time_t l = std::mktime(&c);

// get a calender time for that time_point in UTC. When interpreted
// as UTC this represents the same calendar date and time as the
// original, but if we change the timezone to the system TZ then it
// represents a time offset from the original calendar time by as
// much as UTC differs from the local timezone.
std::tm m = *std::gmtime(&l);

// Treat the new calendar time as offset time in the local TZ. Get
// the number of seconds since the system epoch (again, leap seconds
// may or may not be counted).
std::time_t n = std::mktime(&m);

l -= (n-l); // subtract the difference

l 现在应该表示自 1970 年 1 月 1 日 UTC 以来的(错误)秒数。只要系统纪元和 1970 年 1 月 1 日(系统时区)之间没有闰秒,或者在系统纪元的另一个方向上的相同时间段内,那么任何计数的闰秒都应该取消,l就像 unix 时间戳错误一样,这也是错误的。


另一种选择是使用像 Howard Hinnant's chrono::date 这样的不错的日期库。 (Howard Hinnant 是 C++11 &lt;chrono&gt; 库的开发者之一。)

auto now = system_clock::now();
sys_days today = time_point_cast<days>(now);
system_clock::time_point this_morning = today;

sys_days unix_epoch = day(1)/jan/1970;
days days_since_epoch = today - unix_epoch;

auto s = now - this_morning;

auto tz_offset = hours(0);
int unix_timestamp = (days_since_epoch + s + tz_offset) / seconds(1);

如果您想处理闰秒,Howard Hinnant 还提供了a library,其中包括处理闰秒以及解析时区数据库作为闰秒数据源的工具。

【讨论】:

  • 参见上面 cmets 中的讨论
  • 好吧,这是一个很好的概述 + Howard Hinnant 的提议看起来很有希望。你知道它是否是为 c++ 标准提出的吗?找不到任何信息。
  • Howard Hinnant 的日期类似乎没有被特别提出,但委员会似乎正在考虑标准化某种日期格式。见link
  • 我的日期库也是基于 Unix 时间的(chrono::system_clock 的每个实现也是如此)。翻译:也没有考虑闰秒。底线:计算机通常将闰秒处理为对系统时间的常见校正,而不是自 1970 年新年以来的秒数添加。
  • 我不认为这是一个愚蠢的问题。在两个系统之间交换日历日期/时间很有用:两个系统必须就表示形式达成一致(例如,自 Epoch 以来的秒数),但 c++ 不提供这样的保证。特别是 time() 没有指定其返回值的格式。
【解决方案2】:

这个 C++11 实现怎么样

auto microsecondsUTC = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();

【讨论】:

  • 伟大的现代解决方案!
  • 优秀的解决方案 - 我喜欢把它除以 1000000.0 以获得双倍的秒数。据我估计,在双尾数的精度为 2^52 位的情况下,直到 2112 年 9 月 17 日,这将具有微秒级分辨率。
【解决方案3】:

长话短说,这是我用来获取 Unix 时间戳的函数(自 1970 年 1 月 1 日 UTC 以来的秒数):

static uint64_t getUnixTimeStamp(const std::time_t* t = nullptr)
{
    //if specific time is not passed then get current time
    std::time_t st = t == nullptr ? std::time(nullptr) : *t;
    auto secs = static_cast<std::chrono::seconds>(st).count();
    return static_cast<uint64_t>(secs);
}

这个功能的好处是默认参数值只是给你当前时间。但是,如果您想将某个特定时间转换为 unix 时间戳,那么您也可以这样做。

【讨论】:

  • 在带有unsigned long 的窗口上失败。将其更改为 unsigned long long 工作正常
  • 你是对的。我前段时间更新了我的函数,现在这个答案也更新了。
  • 请注意,Unix 时间戳是 ,而不是毫秒。代码返回正确的结果(以秒为单位),因为强制转换什么都不做。实际上,您只是将 time_t 转换为 uint64_t。作为副作用,您将当前时间设置为参数 t。
  • @VolkA - 好点!我已经更新了答案。但是我认为 long longuint64_t 的转换需要演员表。
  • 这并不能回答问题吗?这会将time_t 转换为时间戳,而不是chrono::time_point
【解决方案4】:

您可以使用mktime() 将在tm 结构中编码的所需日期转换为本地时间 time_t 值。

如果您需要 UTC 时间,则使用 gmttime() 将该 time_t 值转换为 UTC tm 结构,并从您得到的输出中找出哪个 tm 结构产生所需的 UTC time_t当输入到mktime()时。

有点复杂,但希望它会起作用或至少提供一个提示。

【讨论】:

  • 好吧,我当前的实际实现看起来有点不同 (liveworkspace.org/code/1OVIQ7$0)。我仍在寻找纯 c++11 解决方案。如果没有人提出更好的解决方案,我会在几个小时内接受这个
  • @Daniel 获取当前时区自 1970 年 1 月 1 日 00:00:00 以来的时间。您需要 UTC 时间 1970 年 1 月 1 日 00:00:00 以来的时间。
【解决方案5】:

我知道 time_point 有一个方法 time_since_epoch() 但它不是 保证这与 unix epoch 相同(00:00:00 UTC on 1 1970 年 1 月)。

从 C++20 和 P0355R7 开始,当 std::chrono::time_pointstd::chrono::system_clock 时,std::chrono::time_point::time_since_epoch 可以保证是 UNIX 纪元。

【讨论】:

  • C++20与它无关
猜你喜欢
  • 1970-01-01
  • 2018-07-27
  • 1970-01-01
  • 2017-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多