Greg Martin said:
I'm wrestling with this a little bit. Since "time_t = time(time_t)"
returns the number of seconds since the epoch, which is, as I understand
it, "1970-01-01 00:00:00 +0000 (UTC)", why is it in local time in the
example below? If time_t values were *always* specific to the epoch
_UTC_ then it would seem to simplify things.
I realize that this is going to be affected by the bios clock but in my
case the bios clock is set to UTC.
The following is applicable to POSIX systems, where a time_t value
represents seconds since the epoch. C makes no such guarantee.
A time_t value *always* represents seconds since the "epoch', which is
defined as 1970-01-01 00:00:00 UTC. It never refers to local time.
(Well, it can refer to anything you like, but C or POSIX standard
functions that deal with time_t values treat them as seconds since the
epoch, as defined above.) (I'm ignoring leap seconds.)
greg@satellite:~/devel/tests/c$ ./tm_test
now = 1360256972
PST 1360256972
UTC : 1360228172
greg@satellite:~/devel/tests/c$ date
Thu Feb 7 09:09:36 PST 2013
// the program
#define _XOPEN_SOURCE 700
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
time_t tz_mktime (const char* const tz, struct tm* t) {
setenv ("TZ", tz, 1);
tzset ();
return mktime (t);
}
int main () {
time_t now = time (NULL);
struct tm* t = localtime (&now);
char *tz = getenv ("TZ");
printf ("now = %lu\n", (unsigned long int) now);
time_t tzt;
if (tz) {
tzt = tz_mktime (tz, t);
printf ("%s %lu\n", tz, (unsigned long int) tzt);
} else {
tzt = tz_mktime ("PST8PST", t);
printf ("PST %lu\n", (unsigned long int) tzt);
}
tzt = tz_mktime ("", t);
printf ("UTC : %lu\n", (unsigned long int)tzt);
if (tz) {
setenv ("TZ", tz, 1);
} else {
unsetenv ("TZ");
}
tzset ();
return 0;
}
In your program, you compute a struct tm value by calling localtime()
with a time_t value returned by time(). That struct tm value refers to
the local time, not UTC.
For example, as I'm typing this, my local time is:
Thu 2013-02-07 11:35:50 PST
which results in a struct tm value of (omitting some members)
(struct tm){ .tm_year = 113,
.tm_mon = 1,
.tm_mday = 7,
.tm_hour = 11,
.tm_min = 35,
.tm_sec = 50 }
You then pass (a pointer to) that struct tm into your tz_mktime()
function, which sets the time zone to UTC (by setting the
TZ environment variable to the empty string -- that's also
POSIX-specific) and then uses mktime() to convert the struct tm
value to a time_t value.
mktime() treats the above struct tm value as 2013-02-07 11:35:50 *in
local time*. Since you've set the timezone to UTC, local time is UTC.
So the result is 1360236950, the time_t representation for 2013-02-07
11:35:50 UTC -- off by 8 hours from 2013-02-07 11:35:50 PST.
Is the lack of a TZ field in the struct tm an oversight?
I don't know if it's an oversight. <time.h> generally has poor
support for time zones.
There have been attempts to improve this (see, for example,
<
http://www.cl.cam.ac.uk/~mgk25/time/c/>), but they've never actually
been incorporated into the standard.