在C中,有任何API将time()
函数返回的时间转换为特定时区吗?
有一个函数strftime()
可以转换为系统的当前时区。
但我想要的是函数输入是时间(由time()
返回)和timeZone,它将以所述时区格式转换特定的格式化时间。
有这样的API吗?
答案 0 :(得分:6)
POSIX指定tzset()
:
tzset()
函数应使用环境变量TZ的值来设置ctime
,localtime
,mktime
和strftime
使用的时间转换信息。如果环境中不存在TZ,则应使用实现定义的默认时区信息。
所以,理论上你可以使用这样的东西将t0
(一个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"
)并且打印时间是在一年中的适当时间(例如4月底),则可以获得EDT或PDT打印时间价值。
另请注意,有关如何处理时区的ISO标准存在冲突。 ISO 8601(日期/时间格式)表示UTC(格林威治)以东的时区为正,而UTC以西的时区为负。至少一些其他标准(例如SQL,ISO 9075)使用相同的表示法。另一方面,POSIX(又名ISO 9945)在TZ中使用相反的约定,如示例代码所示。只有长期存在的先例才能接受冲突。 C标准没有提及TZ环境变量的格式(或存在)(尽管C99§7.23.3.5 strftime
函数多次引用ISO 8601)。
并且(正如有用的提醒,为什么错误检查是一个好主意),在Mac OS X 10.7.3上,我的环境中没有设置TZ(但它通常是美国/太平洋地区,又名Cupertino,又名America / Los_Angeles,时间)。因此,当getenv()
返回空指针时,上面的代码崩溃了。我在环境中用TZ=CST6CDT
运行它,正如您从第一行输出中看到的那样。
答案 1 :(得分:0)
这里有三个步骤。首先,time_t
表示绝对时间戳 - 至少在概念上是UTC时间戳的顺序。它(通常)不在任何特定时区 - 它通常代表自某个时代(通常是1970年1月1日午夜)以来的秒数。
您可以将其转换为struct tm
,将时间戳分为实际日期和时间 - 年,月,日,小时,分钟和秒。转换有两个函数:gmtime
,它将其保留为UTC样式时间(即格林威治时区)。另一个是localtime
,它将其转换为环境配置为相信代表机器位置的任何时区。
下一步是使用strftime
将其转换为可读的内容。这是基于当前的语言环境,所以(例如)在我写这个时,我认为它是“星期日”。但是,如果我的机器配置为西班牙语,则可能会显示为“Domingo”,或者该订单上的某些内容。
你真的有两个相当不同的问题。您只能将time_t
转换为两个时区之一的时间:“本地”时区(至少,无论机器配置为“本地”),还是格林威治时间。如果你想要任何其他时区,那么你几乎要坚持转换为GM时间,然后调整到你选择的时区。
还有更多调整格式的规定。默认情况下,库将始终转换为“C”语言环境,这几乎是美国英语的简化版本。您还可以设置无名语言环境(“”),它为您提供了一个与机器配置方式相匹配的语言环境。作为第三种选择,大多数库都允许您指定特定区域设置的名称,因此如果您希望将日期格式化为加拿大法语区的某个人,则将区域设置设置为“French_Canada”这就是你得到的(尽管你使用的字符串因标准库而异,所以你可能需要使用像“fr-can”这样的东西。)