如何将 UTC 时间戳转换为本地时间,即整点后的秒数?

How can I convert a UTC timestamp to local time, seconds past the hour?

我有一个大型数据集,其时间戳采用 UTC 时间(以毫秒为单位)。我正在将此数据集与另一个具有 小时 微秒时间戳的数据集同步,因此我需要将第一个时间转换为本地时间,以小时过去的秒数为单位。

我读过的关于这个主题的大多数类似问题都是从获取当前时间的 time() 函数获取 UTC 时间。

我已经尝试实施从 C++ Reference.

中提取的以下内容

我尝试转换的时间戳是双精度值,但我不确定如何实际使用该值。

来自我的数据集的示例 ts:1512695257869

int main ()
{
  double my_utc_ts; //value acquired from the data set

  time_t rawtime;
  struct tm * ptm;

  time ( &rawtime ); //getting current time
  //rawtime = my_utc_ts;  //this is what I tried and is wrong, and results in a nullptr

  ptm = gmtime ( &rawtime );

  puts ("Current time around the World:");
  printf ("Phoenix, AZ (U.S.) :  %2d:%02d\n", (ptm->tm_hour+MST)%24, ptm->tm_min);

  return 0;
}

在我能够将它转换为可用的 gmtime 对象或其他对象之后,我需要得到整点后的秒数...我想如果我能得到 UTC,我就能弄清楚这部分成功转换的时间戳,但我没想到这么远。

非常感谢指导。提前致谢。

我首先将浮点数转换为 time_ttime_t 通常是自纪元以来的秒数(通常是 POSIX 纪元——1970 年 1 月 1 日午夜),所以这听起来只需要相当简单的数学.

因此,为了争论起见,我们假设您的输入使用了不同的纪元。只是为了争论,我们假设它使用的是 1900 年 1 月 1 日午夜的纪元(并且,如前所述,它以毫秒而不是秒为单位)。

因此,要将其转换为 time_t,您首先要除以 1000,将毫秒转换为秒。然后减去 1900 年 1 月 1 日午夜到 1970 年 1 月 1 日午夜之间的秒数。现在你有一个值,你可以将其视为标准库可以处理的 time_t1.

然后使用 localtime 获得与 struct tm 相同的时间。

然后将 tm 中的分钟和秒归零,并使用 mktime 得到表示该时间的 time_t

最后用difftime求出两者的区别


1. 目前,我假设您的标准库基于标准 POSIX 纪元,但这是一个非常安全的假设。

After I'm able to convert it to a usable gmtime object or whatever, I need to get seconds past the hour...

以下是如何使用基于 <chrono>Howard Hinnant's, free, open-source, C++11/14/17 timezone library 将表示自 1970-01-01 00:00:00 UTC 以来的毫秒数的双精度数转换为本地小时后的秒数:

#include "date/tz.h"
#include <iostream>

int
main()
{
    using namespace std::chrono;
    using namespace date;
    double my_utc_ts = 1512695257869;
    using ms = duration<double, std::milli>;
    sys_time<milliseconds> utc_ms{round<milliseconds>(ms{my_utc_ts})};
    auto loc_s = make_zoned(current_zone(), floor<seconds>(utc_ms)).get_local_time();
    auto sec_past_hour = loc_s - floor<hours>(loc_s);
    std::cout << utc_ms << " UTC\n";
    std::cout << sec_past_hour << " past the local hour\n";
}

这为我输出:

2017-12-08 01:07:37.869 UTC
457s past the local hour

如果您的本地时区不是与 UTC 相差的整数小时数,则输出的第二行将与您不同。

代码说明:

我们从您的输入开始 my_utc_ts

下一行创建一个自定义 std::chrono::duration,其中 double 作为表示,milliseconds 作为精度。此类型别名名为 ms.

下一行构造了utc_ms,这是一个持有1512695257869的std::chrono::time_point<system_clock, milliseconds>,表示时间点2017-12-0801:07:37.869 UTC。到目前为止,还没有进行任何实际计算。只需将 double 1512695257869 转换为表示自 1970-01-01 00:00:00 UTC 以来的整数毫秒数的类型。

这一行开始计算:

auto loc_s = make_zoned(current_zone(), floor<seconds>(utc_ms)).get_local_time();

这将创建一个能够在 UTC 和本地时间之间进行映射的 {time_zone, system_time} 对,使用 time_zone 作为该映射。它使用current_zone()找到计算机的当前时区,并将时间点utc_ms从毫秒精度截断到秒精度。最后,尾部 .get_local_time() 从这个映射中提取本地时间,精度为秒,并映射到当前时区。也就是说,loc_s 是自 1970 年 1 月 1 日以来的秒数 00:00:00 UTC,由 2017 年 12 月 8 日生效的本地时区 UTC 偏移量抵消 [=113] =] UTC.

现在,如果您将 loc_s 截断到小时的精度,并从 loc_s 中减去截断的时间点,您将得到本地小时后的秒数:

auto sec_past_hour = loc_s - floor<hours>(loc_s);

整个计算就是上面的两行代码。接下来的两行只是流出 utc_mssec_past_hour.

假设您的本地时区在 2017-12-08 01:07:37 UTC 时与 UTC 偏移了整数小时,您可以仔细检查:

457 == 7*60 + 37

确实,如果你可以假设你的本地时区总是与 UTC 偏移整数小时,上面程序可以通过完全不映射到本地时间来简化:

sys_time<milliseconds> utc_ms{round<milliseconds>(ms{my_utc_ts})};
auto utc_s = floor<seconds>(utc_ms);
auto sec_past_hour = utc_s - floor<hours>(utc_s);

结果将相同。

(警告:并非所有时区都与 UTC 偏移整数小时)

并且如果已知您的数据库生成时区不是计算机当前的本地时区,则可以通过将 current_zone() 替换为您的数据库所在的 IANA 时区标识符来考虑这一点生成的,例如:

auto loc_s = make_zoned("America/New_York", floor<seconds>(utc_ms)).get_local_time();

更新

整个库都基于 C++11 引入的 std <chrono> 库。 utc_msloc_s 上面的类型是 std::chrono::time_point 的实例化,而 sec_past_hour 的类型是 std::chrono::seconds (它本身就是 std::chrono::duration 的实例化)。

durations 可以使用 .count() 成员函数转换为它们的 "representation" 类型。对于 seconds,此表示类型将是带符号的 64 位整数。

有关 <chrono> 的更详细的视频教程,请参阅此 Cppcon 2016 presentation。本演示文稿将鼓励您尽可能避免使用 .count() 成员函数。

例如,不是将 sec_past_hour 转换为 long,以便您可以将其与数据集的其他值进行比较,而是将数据集的其他值转换为 std::chrono::duration,以便您可以将它们与 sec_past_hour.

进行比较

例如:

long other_data = 123456789;  // past the hour in microseconds
if (microseconds{other_data} < sec_past_hour)
    // ...

此代码段显示 <chrono> 将如何为您处理单位转换。这意味着您不会犯错误,例如本应乘以 1,000,000 时却除以 1,000,000,或将 "million" 拼写错误的零个数。