Reverse of gmtime?

F

Francois Grieu

The gmtime function in header <time.h>
converts the calendar time pointed to by some
pointer to time_t into a broken-down time,
expressed as UTC.

Is there a canonical way to do the reverse?


Francois Grieu
 
J

James Kuyper

The gmtime function in header <time.h>
converts the calendar time pointed to by some
pointer to time_t into a broken-down time,
expressed as UTC.

Is there a canonical way to do the reverse?

The closest you can come to doing that is to extract the numbers from
the time string yourself, place them into a struct tm, and then call
mktime() to convert to time_t.

POSIX supports strptime(), which is essentially the inverse of
strftime(), which can do the first part of that process for you. I've no
idea why strftime() is in the C standard library, but strptime() is not.
 
J

Johann Klammer

Francois said:
The gmtime function in header <time.h>
converts the calendar time pointed to by some
pointer to time_t into a broken-down time,
expressed as UTC.

Is there a canonical way to do the reverse?


Francois Grieu
GNU/Linux and BSD has nonstandard function

NAME
timegm, timelocal - inverses of gmtime and localtime

CONFORMING TO
These functions are nonstandard GNU extensions that are also
present on the BSDs. Avoid their
use; see NOTES.
 
F

Francois Grieu

The closest you can come to doing that is to extract the numbers from
the time string yourself, place them into a struct tm, and then call
mktime() to convert to time_t.

I'm assuming I have a struct tm filled-in, as
on output of gmtime in the absence of error.

struct tm *gmtime(const time_t *timer);

Problem is, mktime uses local time, not UTC.

Auxiliary problem is, mktime is ill-specified.
How far in the past does it go without reporting
an error?
Is it allowed to account for past law changes, that
could have modified the offset from UTC to local
(non-DST) time?
That happens more rarely than change of DST rules,
but here are two recent precedents:
- in Lybia, that offset changed by 1 hour on
10 November 2012
http://www.timeanddate.com/worldclock/clockchange.html?n=252&year=2012
- in Samoa island, that offset changed by 24 hours
before the last day of 2011
http://en.wikipedia.org/wiki/Samoa#21st_century

Francois Grieu
 
F

Francois Grieu

No. Many systems have timegm(3) (Linux, *BSD, OS X), but many don't
(Solaris, Windows).

Indeed there is no such function. I hoped there was a
consensus on a portable method.
Writing timegm is fairly simple, though, using a prolepetic Gregorian
calendar, which is pretty much the de facto calendar for most Unix systems.

Computing (non-leap) seconds between two UTC dates passed as
struct tm is possible portably (and not too difficult if
we exclude dates before March 1, 1562, which are only
representable as struct tm by some stretch of imagination).

But portably converting that to time_t without mktime ?
If that was easy, difftime would be replaceable by a mere
subtraction of time_t, followed by cast to double.
I'm reading the C standard as allowing time_t to be e.g.
a 64-bit type with the date and time BCD-coded as read from
a physical clock IC such as
http://datasheets.maximintegrated.com/en/ds/DS1375.pdf
If that's the case, we can not even count on the linearity of
difftime.

Francois Grieu
 
J

James Kuyper

On 02/06/2013 04:51 AM, Francois Grieu wrote:
....
I'm reading the C standard as allowing time_t to be e.g.
a 64-bit type with the date and time BCD-coded as read from
a physical clock IC such as
http://datasheets.maximintegrated.com/en/ds/DS1375.pdf
If that's the case, we can not even count on the linearity of
difftime.

You're right. In C2011, the only requirement on time_t's type is that it
be real - it could be anything from _Bool to long double. In C99, it was
allowed to be any arithmetic type, but I guess somebody was listening to
my comments about the possibilities of using an _Imaginary type.

There are no requirements imposed on how time_t represents time.
(time_t)(-1) should be a value distinguishable from a normal return from
time(), but even that's just a matter of QoI, not an actual requirement.
 
H

Heinrich Wolf

....
Writing timegm is fairly simple, though, using a prolepetic Gregorian
calendar, which is pretty much the de facto calendar for most Unix
systems.

I believe, that timegm is not such simple to write in it's full complexity
with leap seconds. I have not tried timegm, only mktime with leap seconds,
using TZ="right/Etc/GMT". Maybe the unixes use some historical lookup table
for the leap seconds.
 
K

Keith Thompson

Francois Grieu said:
Indeed there is no such function. I hoped there was a
consensus on a portable method.

For restricted values of "consensus" and "portable":

My Linux system has timegm(). Here's an excerpt from the man page (also
available at
<http://www.kernel.org/doc/man-pages/online/pages/man3/timegm.3.html>):

NOTES
The timelocal() function is equivalent to the POSIX standard
function mktime(3). There is no reason to ever use it.

For a portable version of timegm(), set the TZ environment
variable to UTC, call mktime(3) and restore the value of TZ.
Something like

#include <time.h>
#include <stdlib.h>

time_t
my_timegm(struct tm *tm)
{
time_t ret;
char *tz;

tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
}

My own notes on the above:

I'm not sure why it refers to mktime() as a "POSIX standard function".
It is, but it's also defined by the C standard.

The assumption that the time zone is controlled by the "TZ" environment
variable, and the existence of the setenv() and unsetenv() functions,
are portable to POSIX systems, but not to C implementations in general.

[...]
I'm reading the C standard as allowing time_t to be e.g.
a 64-bit type with the date and time BCD-coded as read from
a physical clock IC such as
http://datasheets.maximintegrated.com/en/ds/DS1375.pdf
If that's the case, we can not even count on the linearity of
difftime.

That's correct. clock_t and time_t are merely "real types capable
of representing times"; nothing more is guaranteed.
 
K

Keith Thompson

William Ahern said:
I should note that encoding leap seconds into time_t is inherently fuzzy,
because you cannot reliably predict leap seconds into the future. That makes
difftime() calculations indeterminate for dates merely years into the
future. (And perhaps partly explains why there's no strptime or timegm in
C.) For example, global climate change will unpredictably effect the earth's
rotation as the mass of water from the poles moves toward the equator. That
will accelerate the earth's shift to being tidally locked with the Sun
(notwithstanding it being incinerated before then). Changes in earth's core
as well as various tectonic events also unpredictably accelerate and
declerate the Earth's rotation. Even if these changes are too small to add
or subtract an entire second, they certainly shift the onset of the next
leap second.
[...]

Earth is expected to become tidally locked with the Moon, not the Sun.
 
E

Eric Sosman

William Ahern said:
I should note that encoding leap seconds into time_t is inherently fuzzy,
because you cannot reliably predict leap seconds into the future. That makes
difftime() calculations indeterminate for dates merely years into the
future. (And perhaps partly explains why there's no strptime or timegm in
C.) For example, global climate change will unpredictably effect the earth's
rotation as the mass of water from the poles moves toward the equator. That
will accelerate the earth's shift to being tidally locked with the Sun
(notwithstanding it being incinerated before then). Changes in earth's core
as well as various tectonic events also unpredictably accelerate and
declerate the Earth's rotation. Even if these changes are too small to add
or subtract an entire second, they certainly shift the onset of the next
leap second.
[...]

Earth is expected to become tidally locked with the Moon, not the Sun.

... in January 2038, right?
 
K

Kenny McCormack

William Ahern said:
I should note that encoding leap seconds into time_t is inherently fuzzy,
because you cannot reliably predict leap seconds into the future. That makes
difftime() calculations indeterminate for dates merely years into the
future. (And perhaps partly explains why there's no strptime or timegm in
C.) For example, global climate change will unpredictably effect the earth's
rotation as the mass of water from the poles moves toward the equator. That
will accelerate the earth's shift to being tidally locked with the Sun
(notwithstanding it being incinerated before then). Changes in earth's core
as well as various tectonic events also unpredictably accelerate and
declerate the Earth's rotation. Even if these changes are too small to add
or subtract an entire second, they certainly shift the onset of the next
leap second.
[...]

Earth is expected to become tidally locked with the Moon, not the Sun.

Is that what the C standard says?

If so (if that is what you are claiming), then chapter & verse, please?
 
G

Geoff

Yeah, I suppose it's a little scary that I don't have to follow the link
to know which cartoon it is.

I got a nice chuckle out of this. But it's no more scary than the fact I could
type it from memory. :)

Enjoy.
 
J

James Dow Allen

The gmtime function in header <time.h>
converts the calendar time pointed to by some
pointer to time_t into a broken-down time,
expressed as UTC.

Is there a canonical way to do the reverse?

As long as you have a simple way to compare the
outputs of gmtime for before/after time-wise
(e.g. by using strcmp() after expressing them in
YYYY-MM-DDThh:mm:ss format), then there is a simple
solution based on binary search -- move a bit through
the (32?) bis of the input to gmtime(); call gmtime()
for each bit; compare the result to your target;
discard or keep the bit depending on the compare result.
May sound brutal, but it's a happy way, at least
if you don't let it get to be a habit.

<BORING anecdote> I did it this way 38 years ago to set the system
clock in a diagnostic program. Why did the diagnostic
need to set the clock correctly? The clock could have four
states
* Set to correct date
* Set to incorrect date
* Not set
* In "error" state
Machines normally powered up "Not set" which was usable --
the clock could be used for the comparative timings I needed
even though absolute time was wrong. But a hardware glitch
sometimes moved the clock to the unusable "error" state,
from which it could be changed to "Set" but not "Not set."

And then setting the clock to an INCORRECT value was a poor option.
Since the clock was "Set", the IBM MVS operating system would
blithely go forth without giving operator the option of changing
the date ... and then crash when it belatedly discovered the
date made no sense. ::whack::
There was an operator workaround, but the junior operators
on graveyard shift didn't know it, and that's when we perverse
nerds usually got our paws on the mainframe....
</boring anecdote>

James
 
J

James Dow Allen

William Ahern said:
I should note that encoding leap seconds into time_t is inherently fuzzy,
because you cannot reliably predict leap seconds into the future. Thatmakes
difftime() calculations indeterminate for dates merely years into the
future. (And perhaps partly explains why there's no strptime or timegmin
C.) For example, global climate change will unpredictably effect the earth's
rotation as the mass of water from the poles moves toward the equator.That
will accelerate the earth's shift to being tidally locked with the Sun
(notwithstanding it being incinerated before then). Changes in earth'score
as well as various tectonic events also unpredictably accelerate and
declerate the Earth's rotation. Even if these changes are too small toadd
or subtract an entire second, they certainly shift the onset of the next
leap second. [...]

Earth is expected to become tidally locked with the Moon, not the Sun.

     ... in January 2038, right?
 
J

James Dow Allen

     ... in January 2038, right?

Humanity survived the Cuban Missile Crisis (allegedly
a much closer thing than was revealed at the time);
we survived the Mayan Apocalypse; we even survived the
Janet Jackson wardrobe malfunction. But we we survive
the 68 years of Darkness caused by the Unix clock?

I think The Committee has rejected my suggestion:
to subtract 66.6 years and start a new epoch:
Anno Diaboli


(PS: I didn't want to post this, but clicked Send accidentally.)

James
 
G

Greg Martin

Francois Grieu said:
Indeed there is no such function. I hoped there was a
consensus on a portable method.

For restricted values of "consensus" and "portable":

My Linux system has timegm(). Here's an excerpt from the man page (also
available at
<http://www.kernel.org/doc/man-pages/online/pages/man3/timegm.3.html>):

NOTES
The timelocal() function is equivalent to the POSIX standard
function mktime(3). There is no reason to ever use it.

For a portable version of timegm(), set the TZ environment
variable to UTC, call mktime(3) and restore the value of TZ.
Something like

#include <time.h>
#include <stdlib.h>

time_t
my_timegm(struct tm *tm)
{
time_t ret;
char *tz;

tz = getenv("TZ");
setenv("TZ", "", 1);
tzset();
ret = mktime(tm);
if (tz)
setenv("TZ", tz, 1);
else
unsetenv("TZ");
tzset();
return ret;
}

My own notes on the above:

I'm not sure why it refers to mktime() as a "POSIX standard function".
It is, but it's also defined by the C standard.

The assumption that the time zone is controlled by the "TZ" environment
variable, and the existence of the setenv() and unsetenv() functions,
are portable to POSIX systems, but not to C implementations in general.

[...]
I'm reading the C standard as allowing time_t to be e.g.
a 64-bit type with the date and time BCD-coded as read from
a physical clock IC such as
http://datasheets.maximintegrated.com/en/ds/DS1375.pdf
If that's the case, we can not even count on the linearity of
difftime.

That's correct. clock_t and time_t are merely "real types capable
of representing times"; nothing more is guaranteed.



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.

Is the lack of a TZ field in the struct tm an oversight?


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;

}
 
K

Keith Thompson

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.
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top