【问题标题】:Convert unix timestamp (s) to day of week in C++?在 C++ 中将 unix 时间戳转换为星期几?
【发布时间】:2016-11-14 07:48:09
【问题描述】:

如何根据任意 Unix 时间戳(秒)确定加利福尼亚(太平洋时间)的星期几?我四处搜索,但没有找到 C++ 的内置库。

UTC 通常比 PT 早 8 小时,但简单地从 Unix 时间戳中减去 8 小时并创建 tm 结构是行不通的,因为这会降低夏令时的细微差别。

【问题讨论】:

  • 我没有对你投反对票,但std::localtime 怎么样?
  • 谢谢 - 如果本地时间对我来说不是 PT 怎么办?
  • strftime。或time_put。或Howard Hinnant's date - 指点,而不是答案,这就是评论的原因。
  • 如果您的程序仅适用于该时区,您可以使用_tzset。或this library,正在对其进行标准化。见open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0355r1.html
  • 所以......我已经阅读了文档,但我仍然无法弄清楚如何将 uint64 转换为 time_t 数字,从 time_t 中创建一个 tm 结构,然后获得通过从该结构中读取 PT 中的星期几。有人可以展示整个过程吗?谢谢!

标签: c++ datetime-conversion


【解决方案1】:

以下是使用Howard Hinnant's date library 的方法。这是一个免费的 MIT 许可的 C++11/14 库,基于 <chrono>:

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

int
main()
{
    using namespace std::chrono_literals;
    using namespace date;
    auto unix_timestamp = sys_seconds{1479664467s};
    auto zt = make_zoned("America/Los_Angeles", unix_timestamp);
    auto wd = weekday{floor<days>(zt.get_local_time())};
    std::cout << wd << '\n';
}

第一行只是构造 unix 时间戳(您可能从其他来源获得)。在 C++11 中,我们没有 chrono-literals,因此这一行将改为:

auto unix_timestamp = sys_seconds{seconds{1479664467}};

make_zoned() 基于unix_timestamptime_zone "America/Los_Angeles" 创建一个zoned_time&lt;seconds&gt;zoned_timetime_zone 和 unix 时间戳的集合。

您可以使用zt.get_local_time()zoned_time 中获取当地时间。这是一个chrono::time_point,但没有与之关联的时钟。此时间点的精度将等于您原始时间戳的精度(在本例中为秒)。

您可以通过将local_time 截断为days 的精度来获得local_time 的星期几:

floor<days>(zt.get_local_time())

而这一天精度 time_point 将转换为类型 weekday

可以打印出weekday,参与weekday 算术和比较,或者在[0, 6] 范围内显式转换为unsigned

上面的程序输出:

Sun

这个库的时区部分是IANA timezone database 的忠实代表。从 1800 年代中期到现在,它将在本地时区和 UTC 之间进行正确调整。如果您在 1800 年中期之前传递库时间戳,则已知最早的 UTC 偏移量将向后推算到 -32768 年。您还可以将时间戳传递到 32767 年,当前的偏移量和夏令时规则将向前传播。

由于这个库是基于&lt;chrono&gt; 构建的,它可以处理&lt;chrono&gt; 提供的任何精度。请注意,某些chrono::durations,例如nanoseconds 的范围更有限(nanoseconds 为+/-292 年)。

库/进程是线程安全的。也就是说,该程序直接与您指定的任何时区一起工作,而无需在后台更改计算机的时区或环境变量。它也不涉及 C 计时 API 的部分,由于非 const 函数局部静态,该部分已知不是线程安全的。

移植到最新版本的 gcc、clang 和 VS。

更新 C++20

上面的程序可以很容易地移植到 C++20(我写的时候有些供应商支持这个,有些还不支持):

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std::chrono;
    auto unix_timestamp = sys_seconds{1479664467s};
    auto zt = zoned_time{"America/Los_Angeles", unix_timestamp};
    auto wd = weekday{floor<days>(zt.get_local_time())};
    std::cout << wd << '\n';
}

【讨论】:

    【解决方案2】:

    使用标准 UNIX C(非线程安全版本)的hacky方式:

    #include <stddef.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    
    void print_times() {
      char my_buf[100];
      time_t now = time(NULL);
      printf(ctime(&now));
      strftime(my_buf, 100, "DoW=%w Tz=%Z\n", localtime(&now));
      printf(my_buf);
    }
    
    int main( int argc, char * argv[] ) {
      print_times();
      setenv("TZ", "America/Los_Angeles", 1);
      print_times();
      unsetenv("TZ");
      print_times();
    }
    

    不幸的是,TZ env var 或更改文件 /etc/localtime 是影响 UNIX 时间函数时区设置的唯一方法(在后台 tzset() 使用这些源来初始化一组共享变量,这些变量编码所有库函数的时区设置)。

    一个问题是您需要为您需要的区域提供 UNIX TZ 字符串。 zoneinfo 格式是最简单且推荐使用的格式。我使用tzselect 生成太平洋时间的 TZ 值。

    有关标准 UNIX 时区设置的信息,另请参阅 man 5 localtimeman tzselect

    【讨论】:

      【解决方案3】:

      你可以使用 Boost 库吗? 如果您能找出您的时区 (PST) 的 Posix 时区字符串(IEEE Std 1003.1,请参阅 http://www.boost.org/doc/libs/1_62_0/doc/html/date_time/local_time.html#date_time.local_time.posix_time_zone),那么下面的示例可能会有所帮助。

      #include <iostream>
      #include <ctime>
      #include <boost/date_time.hpp>
      
      int main(int argc, char ** argv)
      {
          std::string posix_time_zone_string = "EST-05:00:00";  // you need to change this to the posix time representation of your desired timezone
      
          time_t unix_time = 1479641855;  //1479641855 = Sun, 20 Nov 2016 11:37:35 GMT
      
          boost::posix_time::ptime pt = boost::posix_time::from_time_t(unix_time);
          std::cout << "time in UTC: " << boost::posix_time::to_iso_extended_string(pt) << std::endl;
      
      
          boost::local_time::time_zone_ptr zone(new boost::local_time::posix_time_zone(posix_time_zone_string));
      
          boost::local_time::local_date_time dt_with_zone(pt, zone);
          std::cout << "time in local timezone: " << boost::posix_time::to_iso_extended_string(dt_with_zone.local_time()) << std::endl;
      
          // Get the week day (alternative 1)
          std::cout << "local week day integer: " << boost::local_time::to_tm(dt_with_zone).tm_wday << std::endl;   // 0=sunday, 1=monday, etc.
      
          // Get the week day (alternative 2)
          std::cout << "local week day name: " << dt_with_zone.local_time().date().day_of_week() << std::endl;
          std::cout << "local week day integer: " << int(dt_with_zone.local_time().date().day_of_week()) << std::endl;
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-27
        • 1970-01-01
        • 2014-03-07
        • 2019-07-27
        • 2019-04-24
        • 1970-01-01
        • 2020-10-19
        • 2020-12-23
        相关资源
        最近更新 更多