Greg Martin <> writes:
[...]
> 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.
--
Keith Thompson (The_Other_Keith)
kst- <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"