【问题标题】:How can I convert a UTC timestamp to local time, seconds past the hour?如何将 UTC 时间戳转换为本地时间,一小时后的秒数?
【发布时间】:2018-05-23 05:58:35
【问题描述】:

我有一个大型数据集,其时间戳以 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 时间戳,我将能够弄清楚这部分成功转换,但我没想到这么远。

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

【问题讨论】:

    标签: c++ utc


    【解决方案1】:

    在我能够将其转换为可用的 gmtime 对象或其他任何东西之后,我需要在整点后获得秒...

    您可以使用基于<chrono>Howard Hinnant's, free, open-source, C++11/14/17 timezone library 将表示自 1970 年 1 月 1 日 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,它是一个std::chrono::time_point&lt;system_clock, milliseconds&gt;,持有1512695257869,表示时间点2017-12-08 01: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();
    

    这将创建一个 {time_zone, system_time} 对,能够在 UTC 和本地时间之间进行映射,使用 time_zone 作为该映射。它使用current_zone() 查找计算机的当前时区,并将时间点utc_ms 从毫秒精度截断为秒精度。最后尾随的.get_local_time() 从此映射中提取本地时间,精度为秒,并映射到当前时区。也就是说,loc_s 是自 1970-01-01 00:00:00 UTC 以来的秒数,由您的本地时区在 2017-12-08 01:07:37 生效的 UTC 偏移量抵消世界标准时间。

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

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

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

    假设您的本地时区在 2017 年 12 月 8 日 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&lt;chrono&gt; 库。以上utc_msloc_s的类型是std::chrono::time_point的实例化,sec_past_hour的类型是std::chrono::seconds(它本身就是std::chrono::duration的实例化)。

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

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

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

    例如:

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

    这个 sn-p 显示 &lt;chrono&gt; 将如何为您处理单位转换。这意味着您不会犯错误,例如当您应该乘以除以 1,000,000 时,或者用错误的零个数拼写“百万”。

    【讨论】:

    • 我可以将 sec_past_hour 转换回原始数据类型吗?这样我就可以将它与其他数据集的值进行比较。这是惊人的帮助。非常感谢。
    • 是的,可以做到。但请参阅关于这一点的更新答案。
    【解决方案2】:

    我首先将浮点数转换为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 时代,但这是一个非常安全的假设。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-15
      • 2019-03-09
      • 2012-09-11
      • 2019-01-05
      • 2013-03-23
      • 2018-09-17
      • 2019-03-23
      • 2015-04-20
      相关资源
      最近更新 更多