C API在指定时区返回时间字符串?

u_p*_*ess 6 c timezone

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

有这样的API吗?

Jon*_*ler 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();
Run Code Online (Sandbox Code Playgroud)

这样可以保留旧时区的副本(当您使用这些东西时,不能依赖它不会更改或超出范围),然后在环境中设置目标时区.然后它告诉系统查看$ 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");
}
Run Code Online (Sandbox Code Playgroud)

测试输出

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)
Run Code Online (Sandbox Code Playgroud)

请注意,当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在环境中运行它,正如您从第一行输出中看到的那样.

  • 时间API不是线程安全的,期间.好吧,`strftime()`没关系,但是`localtime()`和`gmtime()`函数通常返回指向同一块存储的指针,例如,所以你必须在另一个线程调用之前捕获结果.这就是为什么在POSIX中定义`localtime_r()`和`gmtime_r()`接口的原因.但是,设置环境不是线程安全的,并且`tzset()`的设计不是线程安全的,因此整个机制不是线程安全的.标准的API很糟糕(我很想宣称'骇人听闻'!)因为它没有解决这个问题. (3认同)

Jer*_*fin 0

这里确实需要看三个步骤。首先,atime_t代表绝对时间戳——至少在概念上类似于 UTC 时间戳。它(通常)不位于任何特定时区——它通常表示自某个纪元(通常是 1970 年 1 月 1 日午夜)以来的秒数。

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

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

你确实有两个截然不同的问题。您只能将 a 转换time_t为两个时区之一的时间:“本地”时区(至少,无论机器已配置为“本地”时区)或格林威治时间。如果您想要任何其他时区,您几乎会遇到诸如转换为 GM 时间,然后调整到您选择的时区之类的问题。

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