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).
Another field is the timezone, stored as an offset in seconds from 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:
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:
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).
As a workaround, I suppose I can drop the %z from my format string and create that part myself.
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.
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?
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.
I prefer to use
mktime()
instead of modifying thetime_t
value. However, this applies theTZ
offset (just like thelocaltime()
solution), so amkgmtime()
implementation is required.