6

在 C 中,是否有任何 API 可以将time()函数返回的时间转换为特定时区?有一个函数strftime()可以转换为系统的当前时区。但我想要的是函数输入是时间(由返回time())和 timeZone ,它将以所述时区格式转换特定的格式化时间。

有这样的API吗?

4

2 回答 2

7

POSIX 指定tzset()

tzset()函数应使用环境变量 TZ 的值来设置 、 、 和 使用的ctime时间localtime转换mktime信息strftime。如果环境中不存在 TZ,则应使用实现定义的默认时区信息。

因此,理论上您可以使用类似这样的东西将t0(a time_t) 转换为美国/东部时间,而这不是您的默认 TZ:

char old_tz[64];
strcpy(old_tz, getenv("TZ"));
setenv("TZ", "EST5", 1);
tzset();
struct tm *lt = localtime(&t0);
char buffer[64];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
setenv("TZ", old_tz, 1);
tzset();

这会保留旧时区的副本(当您使用这些东西时,您不能依赖它不会更改或超出范围),然后在环境中设置目标时区。然后它告诉系统查看 $TZ 并相应地设置本地时区。然后使用 将给定time_t值转换为分解的本地时间localtime(),格式化字符串,然后重置 TZ 环境变量并告诉系统再次查看它tzset()

在实践中,这可能有效,也可能无效;这取决于系统。

如果您需要这些东西的线程安全版本,您将不得不更加努力。这绝对不是所写的线程安全的。

示例代码省略了错误检查——它没有任何错误检查。

我永远不会声称这是一种简单的方法。应该有更好的方法。

测试代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

static void time_convert(time_t t0, char const *tz_value)
{
    char old_tz[64];
    strcpy(old_tz, getenv("TZ"));
    setenv("TZ", tz_value, 1);
    tzset();
    char new_tz[64];
    strcpy(new_tz, getenv("TZ"));
    char buffer[64];
    struct tm *lt = localtime(&t0);
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
    setenv("TZ", old_tz, 1);
    tzset();
    printf("%ld = %s (TZ=%s)\n", (long)t0, buffer, new_tz);
}

int main(void)
{
    time_t t0 = time(0);
    char *tz = getenv("TZ");
    time_convert(t0, tz);
    time_convert(t0, "UTC0");
    time_convert(t0, "IST-5:30");
    time_convert(t0, "EST5");
    time_convert(t0, "EST5EDT");
    time_convert(t0, "PST8");
    time_convert(t0, "PST8PDT");
}

测试输出

1335761328 = 2012-04-29 23:48:48 (TZ=CST6CDT)
1335761328 = 2012-04-30 04:48:48 (TZ=UTC0)
1335761328 = 2012-04-30 10:18:48 (TZ=IST-5:30)
1335761328 = 2012-04-29 23:48:48 (TZ=EST5)
1335761328 = 2012-04-30 00:48:48 (TZ=EST5EDT)
1335761328 = 2012-04-29 20:48:48 (TZ=PST8)
1335761328 = 2012-04-29 21:48:48 (TZ=PST8PDT)

请注意,当 EST 和 PST 的字符串没有指定夏令时代码时,您将获得一个时区偏移量;当设置了夏令时代码(例如"EST5EDT""PST8PDT")并且要打印的时间在一年中的适当时间(例如四月底)时,您将打印 EDT 或 PDT 时间值。

还要注意,关于如何处理时区的 ISO 标准存在冲突。ISO 8601(用于日期/时间格式)表示 UTC(格林威治)以东的时区为正,UTC 以西的时区为负。至少一些其他标准(例如 SQL、ISO 9075)使用相同的符号。另一方面,POSIX(又名 ISO 9945)在 TZ 中使用相反的约定,如示例代码所示。仅由于长期存在的先例,才接受冲突。C 标准对 TZ 环境变量的格式(或存在)保持沉默(尽管 C99 §7.23.3.5strftime函数确实多次引用 ISO 8601)。

而且(就像一个有益的提醒,为什么错误检查是一个好主意),在 Mac OS X 10.7.3 上,我的环境中没有设置 TZ(但它通常是美国/太平洋,又名库比蒂诺,又名美国/洛杉矶, 时间)。所以上面的代码在getenv()返回空指针时会崩溃。我TZ=CST6CDT在环境中运行它,从输出的第一行可以看出。

于 2012-04-30T04:29:44.870 回答
0

这里真的有三个步骤。首先,atime_t代表一个绝对时间戳——至少在概念上是 UTC 时间戳的顺序。它不是(通常)在任何特定时区 - 它通常表示自某个时期(通常是 1970 年 1 月 1 日午夜)以来的秒数。

您可以将其转换为 a struct tm,将时间戳分解为实际的日期和时间——年、月、日、小时、分钟和秒。有两个函数可以进行这种转换:gmtime,它将其保留为 UTC 风格的时间(即,在格林威治时区)。另一个是localtime,它将其转换为环境已配置为认为代表机器位置的任何时区。

下一步是使用strftime将其转换为可读的内容。这是基于当前的语言环境,所以(例如)当我写这篇文章时,我认为它是“星期天”。但是,如果我的机器配置为西班牙语,那可能会显示为“Domingo”或该命令中的其他内容。

你真的有两个完全不同的问题。您只能将 atime_t转换为两个时区之一的时间:“本地”时区(至少,无论机器被配置为什么“本地”),或格林威治时间。如果您想要任何其他时区,您几乎会被转换为 GM 时间,然后调整到您选择的时区。

有更多用于调整格式的规定。默认情况下,该库将始终转换为“C”语言环境,这几乎是美国英语的简化版本。您还可以设置无名语言环境 (""),它会为您提供一个假定与机器配置方式相匹配的语言环境。作为第三种选择,大多数图书馆都允许您为特定语言环境指定名称,因此,如果您希望将日期格式化为加拿大法语区的人所期望的,您可以将语言环境设置为“French_Canada”这就是你得到的(虽然你使用的字符串因标准库而异,所以你可能需要使用类似“fr-can”的东西)。

于 2012-04-30T04:13:49.723 回答