C:如何在特定时区(按偏移量)打印特定的“时间”值?
Posted
技术标签:
【中文标题】C:如何在特定时区(按偏移量)打印特定的“时间”值?【英文标题】:C: How to print a particular `time` value in a particular timezone (by offset)? 【发布时间】:2011-07-02 14:37:51 【问题描述】:我正在用 C 语言编写一个应用程序,它解析由外部程序记录的数据文件(我无法控制)。它存储二进制数据,其中一个字段是标准 UNIX“纪元”格式的时间(自 1970 年 1 月 1 日以来的秒数,UTC)。
另一个字段是时区,存储为距 UTC 的秒数。
太棒了,我已经拥有了创建一个日期/时间字符串所需的一切,该字符串表示记录时区中的该信息,对吧?嗯...看起来不是这样,和/或我不知道该怎么做。
我将事情归结为一个相当简单的测试用例:
#include <stdio.h>
#include <time.h>
int main(void)
time_t t;
struct tm *tm;
char buf[BUFSIZ];
int offset = 4980; /* slightly bizarre, just to test this - an hour
* and 23 minutes ahead of UTC */
t = time(NULL);
tm = localtime(&t);
strftime(buf, BUFSIZ, "%FT%T%z", tm);
printf("before: %s\n", buf);
/* since we're not telling localtime anything different,
* compensate here (by subtracting applied offset, and adding
* desired one): */
t += offset - tm->tm_gmtoff;
tm = localtime(&t);
tm->tm_zone = "XYZ"; // not used -- but it was in an earlier version
tm->tm_gmtoff = offset;
// on macos, I used to also have %+, which referenced tm_zone
strftime(buf, BUFSIZ, "%FT%T%z", tm);
printf("after: %s\n", buf);
return 0;
当我在 MacOS X 10.6 上运行它时,我得到:
before: 2011-02-23T00:53:04-0800
after: 2011-02-23T10:16:04-0800
我所期望的(实际上是在 Linux 机器上得到的)是:
before: 2011-02-23T00:53:04-0800
after: 2011-02-23T10:16:04+0123
我是否需要更改TZ
环境变量(并且可能调用tzset
)?似乎应该有一种方法来操纵数据结构并获得正确的东西,但上面肯定行不通(无论如何,在 MacOS X 10.6 上——在 Linux 上运行得很好)。
作为一种解决方法,我想我可以从格式字符串中删除 %z 并自己创建该部分。
不过,理想情况下,我希望修改我的struct tm
,或者我可以使用的其他一些函数调用(比如 strftime,但带有额外的参数或其他东西,或者可能是本地时间的另一种形式,而是),这将使事情做正确的事。
由于 Linux 似乎表现良好(尽管即使在那里,上述解决方案也不是很理想,因为我在捏造我的 time_t
值;我希望有一个参数来改变 struct tm
的计算方式),这是我应该报告为针对 MacOS 的错误吗?
或者,我可以调用一组不同的库例程,即使最终需要第三方(我想是来自 GNU 人的东西)库?尽管我会考虑使用 ObjC 或 C++ 选项,但我更愿意继续使用 C。
【问题讨论】:
【参考方案1】:我更喜欢使用mktime()
而不是修改time_t
值。但是,这应用了TZ
偏移量(就像localtime()
解决方案一样),因此需要mkgmtime()
实现。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static void set_timezone(char *tz)
static char var[1024];
snprintf(var, sizeof(var), "TZ=%s", tz);
putenv(var);
tzset();
static time_t mkgmtime(struct tm *tm)
char var[1024];
time_t t;
snprintf(var, sizeof(var), "%s", getenv("TZ") ? getenv("TZ") : "");
set_timezone("GMT0");
t = mktime(tm);
set_timezone(var);
return t;
int main(void)
time_t t;
struct tm tm;
char buf[BUFSIZ];
int offset = 4980; /* slightly bizarre, just to test this - an hour
* and 23 minutes ahead of UTC */
t = time(NULL);
tm = *localtime(&t);
strftime(buf, BUFSIZ, "%FT%T%z", &tm);
printf("before: %s\n", buf);
tm = *gmtime(&t);
tm.tm_sec += offset;
mkgmtime(&tm);
strftime(buf, BUFSIZ, "%FT%T%z", &tm);
printf("after: %s\n", buf);
return 0;
【讨论】:
当我(在 MacOS 上)运行它时,我得到:before: 2011-02-25T14:13:57-0800 after: 2011-02-25T23:36:57+0000
(注:0000)。我期待:before: 2011-02-25T14:13:57-0800 after: 2011-02-25T23:36:57+0123
(注:0123)。听起来也许改变环境中的 TZ 是答案?如果是这样,我想我可以这样做......我只是不明白为什么 strftime 不尊重 tm_gmtoff。 ?【参考方案2】:
我认为最好的方法是
将您的值读入time_t
变量;
按照these 指南设置 TZ 环境变量。有关如何指定 TZ 的信息,另请参阅 this 页面;
调用localtime()
。
这应该可以保证工作,虽然我无法在 MacOS 平台上测试它。
【讨论】:
【参考方案3】:我个人喜欢在我的 bash 代码中嵌入小型 python 脚本。 Python 具有许多功能强大的开箱即用库,可满足您所需的功能。
您可以将python代码嵌入bash脚本中,如下所示(假设安装了python)
python << END
from pytz import timezone
south_africa = timezone('Africa/Johannesburg')
sa_time = datetime.now(south_africa)
print sa_time.strftime('%Y-%m-%d_%H-%M-%S')
END
您可以更改您希望在不同时区显示的时区设置。请查看http://pytz.sourceforge.net/ 了解更多详情。
【讨论】:
不过,我在这里使用的是 C,而不是 bash。我想,嵌入 Python(或 Python 库,或其他类似语言的类似语言)可以完成……但实际上,这似乎是 C 库应该有办法处理的事情。不过,谢谢。以上是关于C:如何在特定时区(按偏移量)打印特定的“时间”值?的主要内容,如果未能解决你的问题,请参考以下文章
使用 Joda-Time 获取给定日期和时区的 UTC 偏移量