Reverse of gmtime?

G

Greg Martin

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.)



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.

I don't think I was very clear. It seems to me mktime shouldn't behave
this way. It would seem more useful to have mktime return a value in
UTC, not adjusted to the TZ field and to do that struct tm would need to
"know" what TZ it refers to. I suppose I'm presuming that somone doesn't
want to normalize to local time but until I'm viewing the time I don't
see and advantage to the conversion.

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.

Perhaps it's this poor support for time zones that's at issue but in a
globally networked environment it would make sense to me to stay in UTC
until a change is explicitly requested.
 
K

Keith Thompson

Greg Martin said:
On 13-02-07 11:47 AM, Keith Thompson wrote: [...]
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.

I don't think I was very clear. It seems to me mktime shouldn't behave
this way. It would seem more useful to have mktime return a value in
UTC, not adjusted to the TZ field and to do that struct tm would need to
"know" what TZ it refers to. I suppose I'm presuming that somone doesn't
want to normalize to local time but until I'm viewing the time I don't
see and advantage to the conversion.

Quoting the standard (N1570 7.27.2.3p2) (emphasis added):

The mktime function converts the broken-down time, *expressed
as local time*, in the structure pointed to by timeptr into
a calendar time value with the same encoding as that of the
values returned by the time function.

It's not the value returned by mktime() that's the issue; it's
mktime()'s interpretation of the struct tm value passed to it.

Your program, I think, demonstrates that it's far too easy to take
a struct tm value representing a time in one time zone and quietly
treat it as representing a time in a different time zone (e.g.,
noon PST and noon UTC). Adding timezone information to struct tm,
perhaps with a distinguished value meaning "local time, whatever
that happens to be"), might have alleviated that problem.

And the problem that started this thread is that mktime() is the
inverse of localtime(), but there is no inverse (in the C standard)
of gmtime().

If there were such a function, say mkgmtime(), you could have used
it in your program and gotten the expected results.

[...]
 
F

Francois Grieu

So the concern expressed in this thread with leap
seconds and mktime/timegm/struct tm is largely misguided in the sense that,
while there's is not portable way to handle this (because the details of
time_t and other routines are left largely unspecified), there are
absolutely ways of implementing these interfaces for the UTC case without
resort to huge historical databases, with the sole caveat that while you
cannot derive the actual UTC second, you can derive minutes, hours, days,
weeks, months, and years of the Gregorian calendar trivially and
indefinitely into the past and future.

Even disregarding leap seconds, it is difficult to make a portable
mkgmtime that is to gmtime what mktime is to localtime (my goal).
I have something that works when DST does not come into play: if
input is struct tm x, and transformed by mktime then gmtime becomes
struct tm y, I build z = (x-y)+x at the struct tm level [carefully:
strange things happen on month changes and new year, but that
can be fixed]; I then pass z to mktime, which gives the right result
(under the assumption that the offset from localtime to UTC is
constant near the UTC date x).

But things go bad during DST, and so far I fail to devise a simple
yet portable strategy. The two periods per year where a Y/M/D h:m:s
date is an ambiguous or invalid local time are especially problematic.

Francois Grieu
 
K

Keith Thompson

Francois Grieu said:
Even disregarding leap seconds, it is difficult to make a portable
mkgmtime that is to gmtime what mktime is to localtime (my goal).
I have something that works when DST does not come into play:
[...]

mkgmtime() would construct a time_t value from a struct tm that
represents a UTC time. DST should be irrelevant.
 
F

Francois Grieu

[...] it is difficult to make a portable
mkgmtime that is to gmtime what mktime is to localtime (my goal).
I have something that works when DST does not come into play:
[...]

mkgmtime() would construct a time_t value from a struct tm that
represents a UTC time. DST should be irrelevant.

DST should be irrelevant to the result of mkgmtime().

However the only functions producing a time_t value in C99 are
time(), which output is not under control; and mktime(), which
cares about DST, even when its input tm_isdst is set to 0.
Hence any portable mkgmtime() has to fight against DST.

A portable mkgmtime() IS feasible, if we are willing to exclude
say 16 hours at the extremities of the system time range (I'm
taking that as a maximum for the combined effect of time
zone and DST), and be off by one second on a leap second.
The technique in my previous post gets the exact result with two
mktime() and a single gmtime() when DST and leap seconds do not
come into play.
As a proof of concept that it can be done despite these hurdles,
we can first use arithmetic to normalize the struct tm on input
of mkgmtime(), ignoring the issue of leap seconds. Then, search
in an interval of 32 hours centered on this normalized input,
trying mktime(), with tm_isdst of 0 and 1, searching for the
struct tm which is accepted by mktime() and which time_t output,
when back-converted to struct tm with gmtime(), gives the struct
tm closest to the normalized input. Dichotomy can be used for
a decent speed, rather than 230402 calls to mktime() and a fair
fraction of that many gmtime().

Note: baring an out-of-range input, it is still possible that we
find no exact match, should we be passed a date corresponding to
suppression of a leap second (an event so rare there is no
precedent, and should ever happen only when the second in the
normalized input is 59). In this case I suggest returning the
time_t for the next second, and is_dst of -1 to indicate
negative leap second.

Francois Grieu
 
K

Keith Thompson

Francois Grieu said:
[...] it is difficult to make a portable
mkgmtime that is to gmtime what mktime is to localtime (my goal).
I have something that works when DST does not come into play:
[...]

mkgmtime() would construct a time_t value from a struct tm that
represents a UTC time. DST should be irrelevant.

DST should be irrelevant to the result of mkgmtime().

However the only functions producing a time_t value in C99 are
time(), which output is not under control; and mktime(), which
cares about DST, even when its input tm_isdst is set to 0.
Hence any portable mkgmtime() has to fight against DST.

Ah, I see what you mean.

If mkgmtime() were to be added to the standard, or if some
implementation were to provide it (as in face some do), there
would be no need for it to be implemented portably. It could take
advantage of the fact that, for example, a time_t represents seconds
since 1970-01-01 00:00:00 UTC (or whatever it is on a particular
system), making just a matter of arithmetic.

But yes, implementing mkgmtime() on top of the existing <time.h>
functions is a bit more complicated. (Which, IMHO, is exactly why
it *should* be in the standard.)

[...]
 
F

Francois Grieu

If mkgmtime() were to be added to the standard, or if some
implementation were to provide it (as in face some do), there
would be no need for it to be implemented portably. It could take
advantage of the fact that, for example, a time_t represents seconds
since 1970-01-01 00:00:00 UTC (or whatever it is on a particular
system), making just a matter of arithmetic.

But yes, implementing mkgmtime() on top of the existing <time.h>
functions is a bit more complicated. (Which, IMHO, is exactly why
it*should* be in the standard.)

Agreed 100%.

This all started with writing a technote showing how to obtain
the number of seconds since an UTC date, as used in an application
(payment of public transport) deployed worldwide on extremely
heterogeneous equipments. I hopped to use time() then difftime(),
but hit the problem of portably translating the reference from
1997, Jan 1 00:00:00 to time_t. Of course there's the portable
time() then mktime() then arithmetic on struct tm, but that's a
failure of <time.h>; and I think time() then difftime(, with
caching or precomputation of the result of mkgmtime(), would be
more efficient, at least on machines with good support of double,
or a pseudo-difftime reduced to integer subtraction.

Unless I missed something, that's still missing in C2011.

Francois Grieu
 
F

Francois Grieu

If mkgmtime() were to be added to the standard, or if some
implementation were to provide it (as in face some do), there
would be no need for it to be implemented portably. It could take
advantage of the fact that, for example, a time_t represents seconds
since 1970-01-01 00:00:00 UTC (or whatever it is on a particular
system), making just a matter of arithmetic.

But yes, implementing mkgmtime() on top of the existing <time.h>
functions is a bit more complicated. (Which, IMHO, is exactly why
it*should* be in the standard.)

Agreed 100%.

This all started with writing a technote showing how to obtain
the number of seconds since an UTC date, as used in an application
(payment of public transport) deployed worldwide on extremely
heterogeneous equipments. I hopped to use time() then difftime(),
but hit the problem of portably translating the reference from
1997, Jan 1 00:00:00 to time_t. Of course there's the portable
time() then gmtime() then arithmetic on struct tm, but that's a
failure of <time.h>; and I think time() then difftime(, with
caching or precomputation of the result of mkgmtime(), would be
more efficient, at least on machines with good support of double,
or a pseudo-difftime reduced to integer subtraction.

Unless I missed something, that's still missing in C2011.

Francois Grieu
[reposted with fix]
 
F

Francois Grieu

If mkgmtime() were to be added to the standard, or if some
implementation were to provide it (as in face some do), there
would be no need for it to be implemented portably. It could take
advantage of the fact that, for example, a time_t represents seconds
since 1970-01-01 00:00:00 UTC (or whatever it is on a particular
system), making just a matter of arithmetic.

But yes, implementing mkgmtime() on top of the existing <time.h>
functions is a bit more complicated. (Which, IMHO, is exactly why
it*should* be in the standard.)

Agreed 100%.

This all started with writing a technote showing how to obtain
the number of seconds since an UTC date, as used in an application
(payment of public transport) deployed worldwide on extremely
heterogeneous equipments. I hopped to use time() then difftime(),
but hit the problem of portably translating the reference from
1997, Jan 1 00:00:00 to time_t. Of course there's the portable
time() then gmtime() then arithmetic on struct tm, but that's a
failure of <time.h>; and I think time() then difftime(, with
caching or precomputation of the result of mkgmtime(), would be
more efficient, at least on machines with good support of double,
or a pseudo-difftime reduced to integer subtraction.

Unless I missed something, ability to build a representation of
time from an UTC date is still missing in C2011.

Francois Grieu
[reposted with fix]
 
K

Keith Thompson

Francois Grieu said:
If mkgmtime() were to be added to the standard, or if some
implementation were to provide it (as in face some do), there
would be no need for it to be implemented portably. It could take
advantage of the fact that, for example, a time_t represents seconds
since 1970-01-01 00:00:00 UTC (or whatever it is on a particular
system), making just a matter of arithmetic.

But yes, implementing mkgmtime() on top of the existing <time.h>
functions is a bit more complicated. (Which, IMHO, is exactly why
it*should* be in the standard.)

Agreed 100%.

This all started with writing a technote showing how to obtain
the number of seconds since an UTC date, as used in an application
(payment of public transport) deployed worldwide on extremely
heterogeneous equipments. I hopped to use time() then difftime(),
but hit the problem of portably translating the reference from
1997, Jan 1 00:00:00 to time_t. Of course there's the portable
time() then gmtime() then arithmetic on struct tm, but that's a
failure of <time.h>; and I think time() then difftime(, with
caching or precomputation of the result of mkgmtime(), would be
more efficient, at least on machines with good support of double,
or a pseudo-difftime reduced to integer subtraction.

Unless I missed something, ability to build a representation of
time from an UTC date is still missing in C2011.

Francois Grieu
[reposted with fix]

In principle, of course, you can't portably make any assumptions
about how a time_t value represents times. In practice,
POSIX requires a time_t value to represent seconds since
1970-01-01 00:00:00 UTC (though it could be signed, unsigned, or
floating-point), and I think a lot of non-POSIX systems use the
same representation. (Windows does, for example.)

Is it possible that all the "extremely heterogeneous equipments"
do the same thing?

You could use test for this by, for example, building a struct tm
value representing 2001-01-01 00:00:00 (local time), passing it
to mktime(), and checking whether the result is within a day or so
of 978307200, using assert() to detect systems that don't satisfy
that requirement.

Of course of any of the systems you care about fail that test,
you'll have to deal with that -- but you might still be able to cover
all the systems you care about with a small set of epoch values.
It's even more likely that all systems of interest encode time_t
as the number of seconds since *some* epoch than that they all use
the POSIX epoch.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,077
Messages
2,570,566
Members
47,202
Latest member
misc.

Latest Threads

Top