39

我怎样才能获得std::chrono::duration自固定日期以来的信息?我需要它来将 a 转换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)相同。

4

5 回答 5

37

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

无论如何,让我们看一下 和 的一些平台time_t文档time()

Linux

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

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

窗户

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

Mac OS X

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

如果ISO C90''), and conform to ISO/IEC 9945-1:1996 (所选本地时区不包含闰秒表(参见 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 的chrono::date. (Howard Hinnant 是从事 C++11<chrono>库工作的人之一。)

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 还提供了一个库,其中包括处理闰秒以及解析时区数据库作为闰秒数据源的工具。

于 2013-01-24T15:46:09.997 回答
15

这个 C++11 实现怎么样

auto microsecondsUTC = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
于 2019-03-06T10:37:38.900 回答
8

长话短说,这是我用来获取 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 时间戳,那么您也可以这样做。

于 2016-11-30T22:55:51.227 回答
2

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

如果您需要 UTC 时间,则使用将该gmttime()time_t转换为 UTCtm结构,并从输出中找出当输入给.tmtime_tmktime()

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

于 2013-01-24T16:33:44.050 回答
0

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

从 C++20 和P0355R7 开始std::chrono::time_point::time_since_epochstd::chrono::time_pointstd::chrono::system_clock.

于 2021-05-07T15:08:38.037 回答