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。)

另一种方法是使用 gmtimemktime 来回切换,如 Sam Varshavchik 的回答所述。


附录:您询问了不使用环境变量的问题。有一种方法,但不幸的是它更不标准。有 BSD 函数 tzalloclocaltime_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_rzmktime。当然,Howard Hinnant 的库显然是线程安全的,根本不需要处理 TZ

编辑(OP):localtime_rztzalloc 的代码可以从 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_timezoned_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 上展示,可在此处查看:

https://www.youtube.com/watch?v=Vwd3pduVGKY