I'm writing an application in C that parses data files recorded by an external program (over which I have no control). It's storing binary data, one field of which is a time in standard UNIX "epoch" format (seconds since January 1st, 1970, UTC).
我正在用C编写一个应用程序,用于解析外部程序记录的数据文件(在这个程序中我没有控制权)。它存储二进制数据,其中一个字段是标准UNIX“纪元”格式的时间(从1970年1月1日开始的秒,UTC)。
Another field is the timezone, stored as an offset in seconds from UTC.
另一个字段是时区,以秒为单位存储在UTC。
Cool, I've got everything I need to make a date/time string representing that information in the timezone it was recorded in, right? Well... it doesn't seem so, and/or I'm not sure how to do it.
很好,我已经有了我需要的所有东西来创建一个日期/时间字符串来表示它所记录的时区中的信息,对吧?嗯…看起来不是这样的,我也不知道该怎么做。
I've boiled things down to a fairly simple test case:
我将其归结为一个相当简单的测试案例:
#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;
}
When I run this on MacOS X 10.6, I get:
当我在MacOS X 10.6上运行这个时,我得到:
before: 2011-02-23T00:53:04-0800
after: 2011-02-23T10:16:04-0800
What I would expect (and in fact get, on a Linux box) would be:
我所期望的(事实上,在Linux上)是:
before: 2011-02-23T00:53:04-0800
after: 2011-02-23T10:16:04+0123
Do I need to change the TZ
environment variable (and maybe call tzset
)? It seems like there ought to be a way to manipulate the data structures and get the right thing, but the above certainly isn't working (on MacOS X 10.6, anyway -- works great on Linux).
是否需要更改TZ环境变量(或者调用tzset)?似乎应该有一种方法来操作数据结构并得到正确的结果,但是上面的方法显然不能工作(无论如何,在MacOS X 10.6上——在Linux上工作得很好)。
As a workaround, I suppose I can drop the %z from my format string and create that part myself.
作为一个解决方案,我假设我可以从我的格式字符串中删除%z并自己创建那个部分。
Ideally, though, I'd like to have either a modification of my struct tm
, or some other function call that I can use (like strftime, but with an extra parameter or something, or perhaps an alternate form of localtime, instead), that would make things do the right thing.
不过,理想情况下,我希望对我的struct tm进行修改,或者使用一些其他函数调用(比如strftime,但是使用一个额外的参数或其他东西,或者使用另一种localtime形式),这样可以让事情做正确的事情。
Since Linux seems to behave (though even there, the above solution isn't quite ideal, because I'm fudging my time_t
value; I'd prefer to have a parameter that changes how the struct tm
is calculated), is this something that I should report as a bug against MacOS?
因为Linux似乎表现得很好(即使在那里,上面的解决方案也不是很理想,因为我在伪造我的time_t值;我希望有一个参数来更改struct tm的计算方式),这是我应该报告的针对MacOS的bug吗?
Alternately, is there a different set of library routines I could call, even if that ends up requiring a third-party (something from the GNU folks, I'm imagining) library? I'd prefer to keep to C, though I'd consider ObjC or C++ options.
或者,我是否可以调用另一组库例程,即使这最终需要一个第三方库(我想象的是出自GNU团队)?我倾向于保留C,尽管我认为ObjC或c++选项。
3 个解决方案
#1
2
I prefer to use mktime()
instead of modifying the time_t
value. However, this applies the TZ
offset (just like the localtime()
solution), so a mkgmtime()
implementation is required.
我更喜欢使用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;
}
#2
0
I think that the best approach would be
我认为最好的办法是
-
read your value into a
time_t
variable;将值读入time_t变量;
-
set the TZ environment variable following these guidelines. See also this page for info on how you should specify TZ;
按照这些指导原则设置TZ环境变量。有关如何指定TZ的信息,请参阅本页;
-
invoke
localtime()
.调用本地时间()。
This should be guaranteed to work, althought I can't test it on a MacOS platform.
这是可以保证的,我不能在MacOS平台上测试它。
#3
-1
I personally like to embed small python scripts in my bash code. Python has many powerful out-of-the-box libraries for functionalities like you require.
我个人喜欢在我的bash代码中嵌入小的python脚本。Python有许多功能强大的开箱即用的库,用于您需要的功能。
You can embed python code in a bash script as follows (Assuming python is installed)
可以将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
You can change the timezone setting as you wish to display in different timezones. Have a look at http://pytz.sourceforge.net/ for more details.
您可以更改时区设置,以便在不同的时区显示。有关详细信息,请参阅http://pytz.sourceforge.net/。
#1
2
I prefer to use mktime()
instead of modifying the time_t
value. However, this applies the TZ
offset (just like the localtime()
solution), so a mkgmtime()
implementation is required.
我更喜欢使用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;
}
#2
0
I think that the best approach would be
我认为最好的办法是
-
read your value into a
time_t
variable;将值读入time_t变量;
-
set the TZ environment variable following these guidelines. See also this page for info on how you should specify TZ;
按照这些指导原则设置TZ环境变量。有关如何指定TZ的信息,请参阅本页;
-
invoke
localtime()
.调用本地时间()。
This should be guaranteed to work, althought I can't test it on a MacOS platform.
这是可以保证的,我不能在MacOS平台上测试它。
#3
-1
I personally like to embed small python scripts in my bash code. Python has many powerful out-of-the-box libraries for functionalities like you require.
我个人喜欢在我的bash代码中嵌入小的python脚本。Python有许多功能强大的开箱即用的库,用于您需要的功能。
You can embed python code in a bash script as follows (Assuming python is installed)
可以将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
You can change the timezone setting as you wish to display in different timezones. Have a look at http://pytz.sourceforge.net/ for more details.
您可以更改时区设置,以便在不同的时区显示。有关详细信息,请参阅http://pytz.sourceforge.net/。