Ubuntu C/C++ 目前如何将 IANA 时区名称转换为 UTC 偏移量
How can I convert IANA time zone name to UTC offset at present in Ubuntu C/C++
在 Python 或 Java 中,您可以根据时区的 IANA 名称(例如 "America/Los_Angeles")获得 UTC 偏移量(当前时间)。例如,参见 Get UTC offset from time zone name in python。
如何在 Ubuntu 14.04 上使用 C/C++ 执行相同的操作?
编辑:最好以线程安全的方式(无环境变量)。
使用gmtime()
,首先将当前纪元时间转换为UTC时间,然后使用mktime()
重新计算纪元时间,然后将结果与真实纪元时间进行比较。
gmtime()
计算 UTC 中的 struct tm
,而 mktime()
假定 struct tm
表示当前本地日历时间。因此,通过这种循环计算,您可以间接计算出当前时区偏移量。
请注意,如果 struct tm
无法转换为纪元时间,mktime()
可能会 return 出错,这将在标准时间和备用时间之间的某些转换期间发生。在您的情况下,由您自己弄清楚这意味着什么。
食谱看起来像这样:
time_t t = time(NULL);
struct tm *tmp = gmtime(&t);
time_t t2 = mktime(tmp);
int offset = t - t2;
有关详细信息,请参阅 the documentation of these library functions。
要使用特定时区,请设置 TZ
环境变量,或者您可以像 Steve Summit 的回答那样尝试使用 localtime_rz
。如前所述,请注意 mktime
有时 return -1 无法转换。
你提到了这个事实,但重要的是要注意 UTC 和时区时间之间的偏移量不一定是恒定的。如果时区进行夏令时(夏令时)调整,偏移量会根据一年中的时间而变化。
找到偏移量的一种方法是将您感兴趣的时间交给 localtime()
函数,然后查看 tm_gmtoff
字段。将 TZ
环境变量设置为您感兴趣的区域名称来执行此操作。这是当前时间这样做的示例:
#include <time.h>
#include <stdio.h>
int main()
{
setenv("TZ", "America/Los_Angeles", 1);
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
printf("%ld\n", tmp->tm_gmtoff);
}
此时打印 -25200,表示洛杉矶在格林威治以西 25200 秒或 420 分钟或 7 小时。但是下周(实际上是明天)U.S 夏令时结束,此时此代码将开始打印 -28800。
这不能保证有效,因为 tm_gmtoff
字段不可移植。但我相信 Linux 的所有版本都会有它。 (您可能必须使用 -D_BSD_SOURCE
或其他方式进行编译,或者将该字段称为 __tm_gmtoff
。但根据我的经验,默认情况下它往往会工作,如普通的 tm_gmtoff
。)
另一种方法是使用 gmtime
和 mktime
来回切换,如 Sam Varshavchik 的回答所述。
附录:您询问了不使用环境变量的问题。有一种方法,但不幸的是它更不标准。有 BSD 函数 tzalloc
和 localtime_rz
可以完成这项工作,但它们在 Linux 上不存在。代码如下所示:
timezone_t tz = tzalloc("America/Los_Angeles");
if(tz == NULL) return 1;
time_t t = time(NULL);
struct tm tm, *tmp = localtime_rz(tz, &t, &tm);
printf("%ld\n", tmp->tm_gmtoff);
对我来说,这会打印 -28800(因为 PDT 几分钟前才回落到 PST)。
如果你有它,你也可以在 Sam Varshavchik 的回答中使用 localtime_rz
和 mktime
。当然,Howard Hinnant 的库显然是线程安全的,根本不需要处理 TZ
。
编辑(OP):localtime_rz
和 tzalloc
的代码可以从 https://www.iana.org/time-zones 下载并适用于 Ubuntu 14.04。
你可以使用这个 free open source C++11/14 library 来做到这一点:
#include "chrono_io.h"
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
auto zt = make_zoned("America/Los_Angeles", system_clock::now());
std::cout << zt.get_info().offset << '\n';
}
当前输出:
-25200s
或者您可以像这样设置不同的格式:
std::cout << make_time(zt.get_info().offset) << '\n';
当前输出:
-07:00:00
工厂函数 make_zoned
使用 IANA 名称 "America/Los_Angeles" 和来自 std::chrono::system_clock
的当前时间创建了一个 zoned_time
。 zoned_time
有一个 member getter to get the information about the timezone at that time. The information is a type called sys_info
,其中包含各种有用的信息,包括当前的 UTC 偏移量。
UTC 偏移量存储为 std::chrono::seconds
。 header "chrono_io.h"
将格式化 durations
用于流式传输。或者 make_time
实用程序可用于将持续时间格式化为 hh:mm:ss
.
上面的程序是thread-safe。您不必担心某些其他进程会从您的下方更改 TZ,或以任何其他方式更改计算机的当前时区。如果您想要 有关当前时区的信息,也可以使用,只需使用 current_zone()
代替 "America/Los_Angeles"
。
如果您想探索其他时间,那也很容易。例如,当地时间凌晨 2 点 Nov/6/2016 开始:
auto zt = make_zoned("America/Los_Angeles", local_days{nov/6/2016} + 2h);
输出变为:
-28800s
-08:00:00
有关此库的更多信息已在 Cppcon 2016 上展示,可在此处查看:
在 Python 或 Java 中,您可以根据时区的 IANA 名称(例如 "America/Los_Angeles")获得 UTC 偏移量(当前时间)。例如,参见 Get UTC offset from time zone name in python。
如何在 Ubuntu 14.04 上使用 C/C++ 执行相同的操作?
编辑:最好以线程安全的方式(无环境变量)。
使用gmtime()
,首先将当前纪元时间转换为UTC时间,然后使用mktime()
重新计算纪元时间,然后将结果与真实纪元时间进行比较。
gmtime()
计算 UTC 中的 struct tm
,而 mktime()
假定 struct tm
表示当前本地日历时间。因此,通过这种循环计算,您可以间接计算出当前时区偏移量。
请注意,如果 struct tm
无法转换为纪元时间,mktime()
可能会 return 出错,这将在标准时间和备用时间之间的某些转换期间发生。在您的情况下,由您自己弄清楚这意味着什么。
食谱看起来像这样:
time_t t = time(NULL);
struct tm *tmp = gmtime(&t);
time_t t2 = mktime(tmp);
int offset = t - t2;
有关详细信息,请参阅 the documentation of these library functions。
要使用特定时区,请设置 TZ
环境变量,或者您可以像 Steve Summit 的回答那样尝试使用 localtime_rz
。如前所述,请注意 mktime
有时 return -1 无法转换。
你提到了这个事实,但重要的是要注意 UTC 和时区时间之间的偏移量不一定是恒定的。如果时区进行夏令时(夏令时)调整,偏移量会根据一年中的时间而变化。
找到偏移量的一种方法是将您感兴趣的时间交给 localtime()
函数,然后查看 tm_gmtoff
字段。将 TZ
环境变量设置为您感兴趣的区域名称来执行此操作。这是当前时间这样做的示例:
#include <time.h>
#include <stdio.h>
int main()
{
setenv("TZ", "America/Los_Angeles", 1);
time_t t = time(NULL);
struct tm *tmp = localtime(&t);
printf("%ld\n", tmp->tm_gmtoff);
}
此时打印 -25200,表示洛杉矶在格林威治以西 25200 秒或 420 分钟或 7 小时。但是下周(实际上是明天)U.S 夏令时结束,此时此代码将开始打印 -28800。
这不能保证有效,因为 tm_gmtoff
字段不可移植。但我相信 Linux 的所有版本都会有它。 (您可能必须使用 -D_BSD_SOURCE
或其他方式进行编译,或者将该字段称为 __tm_gmtoff
。但根据我的经验,默认情况下它往往会工作,如普通的 tm_gmtoff
。)
另一种方法是使用 gmtime
和 mktime
来回切换,如 Sam Varshavchik 的回答所述。
附录:您询问了不使用环境变量的问题。有一种方法,但不幸的是它更不标准。有 BSD 函数 tzalloc
和 localtime_rz
可以完成这项工作,但它们在 Linux 上不存在。代码如下所示:
timezone_t tz = tzalloc("America/Los_Angeles");
if(tz == NULL) return 1;
time_t t = time(NULL);
struct tm tm, *tmp = localtime_rz(tz, &t, &tm);
printf("%ld\n", tmp->tm_gmtoff);
对我来说,这会打印 -28800(因为 PDT 几分钟前才回落到 PST)。
如果你有它,你也可以在 Sam Varshavchik 的回答中使用 localtime_rz
和 mktime
。当然,Howard Hinnant 的库显然是线程安全的,根本不需要处理 TZ
。
编辑(OP):localtime_rz
和 tzalloc
的代码可以从 https://www.iana.org/time-zones 下载并适用于 Ubuntu 14.04。
你可以使用这个 free open source C++11/14 library 来做到这一点:
#include "chrono_io.h"
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
auto zt = make_zoned("America/Los_Angeles", system_clock::now());
std::cout << zt.get_info().offset << '\n';
}
当前输出:
-25200s
或者您可以像这样设置不同的格式:
std::cout << make_time(zt.get_info().offset) << '\n';
当前输出:
-07:00:00
工厂函数 make_zoned
使用 IANA 名称 "America/Los_Angeles" 和来自 std::chrono::system_clock
的当前时间创建了一个 zoned_time
。 zoned_time
有一个 member getter to get the information about the timezone at that time. The information is a type called sys_info
,其中包含各种有用的信息,包括当前的 UTC 偏移量。
UTC 偏移量存储为 std::chrono::seconds
。 header "chrono_io.h"
将格式化 durations
用于流式传输。或者 make_time
实用程序可用于将持续时间格式化为 hh:mm:ss
.
上面的程序是thread-safe。您不必担心某些其他进程会从您的下方更改 TZ,或以任何其他方式更改计算机的当前时区。如果您想要 有关当前时区的信息,也可以使用,只需使用 current_zone()
代替 "America/Los_Angeles"
。
如果您想探索其他时间,那也很容易。例如,当地时间凌晨 2 点 Nov/6/2016 开始:
auto zt = make_zoned("America/Los_Angeles", local_days{nov/6/2016} + 2h);
输出变为:
-28800s
-08:00:00
有关此库的更多信息已在 Cppcon 2016 上展示,可在此处查看: