C++:将Unix时间转换为非本地时区
C++: converting Unix time to non-local timezone
我正在尝试将保存在time_t
变量中的时间转换为某个时区(不是本地时区(的struct tm*
。
在这篇文章的基础上,讨论了从struct tm*
到time_t
的逆运算,我编写了以下函数:
struct tm* localtime_tz(time_t* t, std::string timezone) {
struct tm* ret;
char* tz;
tz = std::getenv("TZ"); // Store currently set time zone
// Set time zone
setenv("TZ", timezone.c_str(), 1);
tzset();
std::cout << "Time zone set to " << std::getenv("TZ") << std::endl;
ret = std::localtime(t); // Convert given Unix time to local time in time zone
std::cout << "Local time is: " << std::asctime(ret);
std::cout << "UTC time is: " << std::asctime(std::gmtime(t));
// Reset time zone to stored value
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
}
但是,转换失败,我得到
Time zone set to CEST
Local time is: Wed Aug 9 16:39:38 2017
UTC time is: Wed Aug 9 16:39:38 2017
即本地时间设置为 UTC 时间,而不是 CEST 的 UTC+2。
date
和其他工具显示的时区缩写不是时区的名称。 IANA 时区数据库改用相关区域内的主要城市。 原因是缩写不是唯一的,并且由于地点会切换时区,因此它们没有传达足够的信息来转换过去的日期。
此外,POSIX 指定必须以特定方式解析TZ
变量,并且它几乎建议将裸时区缩写视为 UTC(但使用不同的缩写(。
相反,您必须使用实际的时区名称(如Europe/Berlin
(或 POSIX 时区说明符(如CET-1CEST
(。
我提供了一个额外的答案,以防有人想以线程安全的方式执行此操作(无需设置环境变量等全局变量(。 这个答案需要C++11(或更好(和Howard Hinnant的免费开源时区库,该库已移植到Linux,macOS和Windows。
该库建立在<chrono>
的基础上,将其扩展到日历和时区。 可以与 C 时序 API 接口,例如与此库struct tm
接口,但库中未内置此类功能。 此库使 C API 变得不必要,除非您必须与使用 C API 的其他代码进行交互。
例如,有一个名为date::zoned_seconds
的类型将system_clock::time_point
(精度seconds
除外(与date::time_zone
耦合,以在任意时区中创建本地时间。 如此处更详细地描述,以下是将zoned_seconds
转换为std::tm
的方法:
std::tm
to_tm(date::zoned_seconds tp)
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto lt = tp.get_local_time();
auto ld = floor<days>(lt);
time_of_day<seconds> tod{lt - ld}; // <seconds> can be omitted in C++17
year_month_day ymd{ld};
tm t{};
t.tm_sec = tod.seconds().count();
t.tm_min = tod.minutes().count();
t.tm_hour = tod.hours().count();
t.tm_mday = unsigned{ymd.day()};
t.tm_mon = unsigned{ymd.month()} - 1;
t.tm_year = int{ymd.year()} - 1900;
t.tm_wday = unsigned{weekday{ld}};
t.tm_yday = (ld - local_days{ymd.year()/jan/1}).count();
t.tm_isdst = tp.get_info().save != minutes{0};
return t;
}
使用此构建基块,将任意时区的time_t
转换为tm
几乎变得微不足道(无需设置全局(:
std::tm
localtime_tz(time_t t, const std::string& timezone)
{
using namespace date;
using namespace std::chrono;
return to_tm({timezone, floor<seconds>(system_clock::from_time_t(t))});
}
如果您使用的是 C++17,则可以删除using namespace date
因为floor<seconds>
将在namespace std::chrono
内完全提供。
上面的代码从string
timezone
创建一个zoned_seconds
,并从time_t
t
创建一个派生system_clock::time_point
。zoned_seconds
在概念上是一个pair<time_zone, system_clock::time_point>
,但精度任意。 它也可以被认为是一种pair<time_zone, local_time>
,因为time_zone
知道如何在UTC和当地时间之间进行映射。 因此,上面的大多数用户编写的代码只是从zoned_seconds
中提取本地时间并将其放入struct tm
中。
上面的代码可以像这样执行:
auto tm = localtime_tz(time(nullptr), "America/Anchorage");
如果您不需要tm
中的结果,而是可以保留在<chrono>
系统中,则时区库中存在非常好的功能,用于解析和格式化zoned_seconds
,例如:
cout << zoned_seconds{"America/Anchorage", floor<seconds>(system_clock::now())} << 'n';
示例输出:
2017-08-10 16:50:28 AKDT
- 防止主数据类型C++的隐式转换
- 模板参数替换失败,并且未完成隐式转换
- 努力将整数转换为链表。不知道我在这里做错了什么
- 以线程安全的方式转换 C/C++ 中时区名称字符串的时区偏移量
- C++:将Unix时间转换为非本地时区
- 将带有时区的纪元时间转换为Excel格式的时间?
- 快速将带有时区的日期时间字符串转换为 UNIX 时间戳(C++)
- Qt将时间和日期字符串转换为带时区的QDateTime
- 将日期和时间数字转换为time_t并指定时区
- 将 UTC time_t转换为不同的时区C++线程安全
- 使用dst和boost将当前服务器时间转换为时区
- 使用boost将UTC时间转换为自定义时区
- 在C++/boost中将日期时间转换为不同的时区
- C/C++时区正确的时间转换(从epoch到秒)
- 如何使用ICU将update从一个时区转换为另一个时区
- 使用自定义时区将 boost::p osix_time::p time 转换为字符串
- 如何在c++中将时间转换为windows上的特定时区
- 将时间戳(以毫秒为单位)转换为boost中的时区
- C++:在时区之间转换日期时间以保持精度
- 时区之间的时间转换