time_t problem: representing an error value

D

Dom Bannon

I need to represent dates going back to, potentially, ~1900 (on
Windows and Linux). The obvious way to do this is to use a time_t, but
this seems to have a flaw in that various library routines (mktime and
time, at least) use (time_t)-1 as an error return.

(time_t)-1 is a valid representation of the last second in 1969, so
it's only a matter of time before I need this as a valid date.

I suppose I'm just going to have to ignore this, and work around
problems as they arise. Thoughts? A better way to do this? If you've
got a database with dates in it, do you use time_t, or something else?

Thanks -

Dom
 
Ö

Öö Tiib

I need to represent dates going back to, potentially, ~1900 (on
Windows and Linux). The obvious way to do this is to use a time_t, but
this seems to have a flaw in that various library routines (mktime and
time, at least) use (time_t)-1 as an error return.

time_t does not help if you need dates before 1970 because your dates
are outside of range for time_t then. Use something else that is also
portable (like Boost.Date_Time) for solutions where you need time
values (or granularity of time values) that time_t does not reach.

Otherwise it feels like you are trying to use signed chars where you
clearly need floats.
 
E

Eric Sosman

I need to represent dates going back to, potentially, ~1900 (on
Windows and Linux). The obvious way to do this is to use a time_t, but
this seems to have a flaw in that various library routines (mktime and
time, at least) use (time_t)-1 as an error return.

(time_t)-1 is a valid representation of the last second in 1969, so
it's only a matter of time before I need this as a valid date.

That's a common practice, but time_t is not actually required to
be encoded this way. (I've a faint recollection that old Windows
versions counted from a different zero point; perhaps they changed
it? No matter, really.)
I suppose I'm just going to have to ignore this, and work around
problems as they arise. Thoughts? A better way to do this? If you've
got a database with dates in it, do you use time_t, or something else?

If your dates will remain on the system where they were originally
computed (or on compatible systems), and if you know that they're in
range for that system's time_t, go ahead and use it. But if there's
a chance that they'll need to move to another system or that they might
get outside time_t's span (as in your case), I think you'd do better
to adopt another representation. Unless you have a compelling reason
not to use it, ISO 8601 looks attractive.
 
D

Dom Bannon

(time_t)-1 is used as an error indication by library functions whose domain
is defined solely as Jan 1, 1970, and after. There is no ambiguity in those
cases.

Curious - I haven't found that in the C spec (1999-12-01, 7.23.2.3,
7.23.2.4). I haven't checked the C++ spec.

-Dom
 
T

Tom St Denis

Curious - I haven't found that in the C spec (1999-12-01, 7.23.2.3,
7.23.2.4). I haven't checked the C++ spec.

-Dom

man 2 time

RETURN VALUE
On success, the value of time in seconds since the Epoch is
returned. On error, ((time_t) -1) is returned, and errno is set
appropriately.
 
K

Keith Thompson

Sam said:
(time_t)-1 is used as an error indication by library functions whose domain
is defined solely as Jan 1, 1970, and after. There is no ambiguity in those
cases.

If you need to work with date values before Jan 1, 1970, you'll have to
defined your own class, and provide methods for mapping your date value
to/from a time_t for the date ranges that time_t can represent.

(time_t)-1 is an error indication for the time() function, *not* for the
time_t type. No functions other than time() treat (time_t)-1 specially.

The C standard says only that time_t is an arithmetic type capable of
representing times. I presume the C++ standard says the same thing.
(The cross-post to comp.lang.c and comp.lang.c++ probably wasn't a good
idea.)

The most common time_t representation is a signed integer representing
the number of seconds since 1970-01-01 00:00:00 GMT. On systems I've
used, negative values are handled normally and can be used to
represent times before 1970. (time_t)-1 is going to be a problem
only if you set your system clock back to the end of 1969 and call
the time() function. But if time_t is 32 bits (which is still
very common), the earliest representable time is some time on December
13, 1901, so it's probably not going to be suitable for the OP's
requirements.

One possibility is to use a 64-bit signed integer representing seconds
since some epoch other than 1970. For most systems, converting between
time_t and this representation is just a matter of converting and adding
or subtracting an offset. If you ever need to run on a system that uses
some exotic time_t representation, you can write conversion functions
for that system.

I would have suggested using 1970 as the epoch, but then bugs in which
you forget to do the conversion would be very hard to track down.

Or you can just use seconds since 1970 (in 64 bits) and not worry about
supporting systems that use a different epoch and/or granularity. If
you ever do need to support such a system, you'll have a lot of redesign
to do, but that's unlikely to happen.

Or you can represent times as character strings in ISO 8601 format.
For example, the current time is 2010-05-06 15:54:58 UTC.

Decide in advance what you want to do about time zones. Since you're
dealing with times back to 1900, leap seconds probably aren't a
concern, but take a moment to convince yourself you can safely
ignore them.
 
K

Keith Thompson

William Ahern said:
time_t doesn't represent a calendar date, it stores a value from the system
clock. The only type in C defined to store a calendar date is struct tm.

Yes, but there's a one-to-one correspondence between calendar
dates and system clock values, and standard functions to convert
between them.

[...]
 
M

Michael Angelo Ravera

I need to represent dates going back to, potentially, ~1900 (on
Windows and Linux). The obvious way to do this is to use a time_t, but
this seems to have a flaw in that various library routines (mktime and
time, at least) use (time_t)-1 as an error return.

(time_t)-1 is a valid representation of the last second in 1969, so
it's only a matter of time before I need this as a valid date.

I suppose I'm just going to have to ignore this, and work around
problems as they arise. Thoughts? A better way to do this? If you've
got a database with dates in it, do you use time_t, or something else?

I've used time64, the number of microseconds since the last calendral
convergence which also is the base for JulianDates.

struct timeb t2;
ftime (& t2);
return ((_int64)t2.time * 1000 + t2.millitm) * 1000 +
210866760000000000;
 
K

Keith Thompson

William Ahern said:
There isn't a one-to-one correspondence where the system clock has
sub-second granularity, nor is there a one-to-one correspondence for a clock
tracking TAI intead of UTC*. And there isn't necessarily any fixed
correspondence for some leap second schemes.

Conceded.

[...]
 
J

James Kanze

(time_t)-1 is used as an error indication by library functions
whose domain is defined solely as Jan 1, 1970, and after.
There is no ambiguity in those cases.

That's true for Unix, but the C standard is much, much looser.
The domain is implementation defined (and could reasonably start
much later than Jan. 1, 1970), and the C standard doesn't even
guarantee that time_t is an integral type (although it must be
numeric).
If you need to work with date values before Jan 1, 1970,
you'll have to defined your own class, and provide methods for
mapping your date value to/from a time_t for the date ranges
that time_t can represent.

More generally, if you're doing anything with dates more than
a few years from the present, you're unlikely to be able to use
time_t. (Note that this is true for the future as well: where
I work, we sometimes have to calculate 50 years into the future,
or more, and the 32 bit time_t common on most Unixes doesn't
work for this either.)
 
J

James Kanze


[...]
(time_t)-1 is an error indication for the time() function, *not* for the
time_t type. No functions other than time() treat (time_t)-1 specially.
The C standard says only that time_t is an arithmetic type capable of
representing times. I presume the C++ standard says the same thing.
(The cross-post to comp.lang.c and comp.lang.c++ probably wasn't a good
idea.)

All the C++ standard says about time_t is that "the contents [of
<ctime>] are the same ans the Standard C library header
<time.h>]", and refers to ISO C clause 7.12, Amendment 1 clause
4.6.4. This is one case where cross-posting to the two groups
is perfectly valid (as is talking about C/C++).
 
P

Puppet_Sock

     That's a common practice, but time_t is not actually required to
be encoded this way.  (I've a faint recollection that old Windows
versions counted from a different zero point; perhaps they changed
it?  No matter, really.)

October 28, 1955

Heh heh.
Socks
 
D

David Thompson

(time_t)-1 is an error indication for the time() function, *not* for the
time_t type. No functions other than time() treat (time_t)-1 specially.
mktime() also returns -1. But except for timezone (including DST), or
IMO better ignoring it, it's easy to write mkgmtime() in userland
given Unix-style linear seconds. Or easy to specialcase the one
problem value, but a bit unesthetic.
The C standard says only that time_t is an arithmetic type capable of
representing times. I presume the C++ standard says the same thing.
(The cross-post to comp.lang.c and comp.lang.c++ probably wasn't a good
idea.)

The most common time_t representation is a signed integer representing
the number of seconds since 1970-01-01 00:00:00 GMT. On systems I've
used, negative values are handled normally <snip>

Because it was the Unix convention, and (most? all?) other C's picked
it up. Precisely, seconds since that epoch is POSIX; it's not standard
to be an integer and signed, but all Unices I've seen are; and it's
not standard negatives work, but all I've had occasion to try did.
One possibility is to use a 64-bit signed integer representing seconds
since some epoch other than 1970. For most systems, converting between
time_t and this representation is just a matter of converting and adding
or subtracting an offset. If you ever need to run on a system that uses
some exotic time_t representation, you can write conversion functions
for that system.
NTP uses 1900. My recollection is that it uses 32bit seconds, with a
plan to handle wraparound (which will first occur in 2038 IIRC), and
32bit fraction of second (which can resolve somewhat better than 1ns).
But TTBOMK it still doesn't deal with leapseconds -- although there
are apparently serious proposals to stop doing leapseconds for now.

One interesting choice I've seen is Tandem^WHP^WCompaq NonStop
Guardian, which uses noon 1Jan4713BC-proleptic, the epoch of the
'Julian day' scale used in astronomy (no relation to the Julian
calendar or the day-of-year apparently miscalled Julian by IBM.)
Or you can represent times as character strings in ISO 8601 format.
For example, the current time is 2010-05-06 15:54:58 UTC.
At least 8601-style; last I looked, to my reading it required 'T' not
space between date and time, which I thought was patently insane.
FWIW, this order was also (US) ANSI X3.30 / FIPS 4 earlier.
And IME most(?) SQL db interfaces like it.
 
P

Phil Carmody

David Thompson said:
Because it was the Unix convention, and (most? all?) other C's picked
it up. Precisely, seconds since that epoch is POSIX; it's not standard
to be an integer and signed, but all Unices I've seen are; and it's
not standard negatives work, but all I've had occasion to try did.

NTP uses 1900. My recollection is that it uses 32bit seconds, with a
plan to handle wraparound (which will first occur in 2038 IIRC)

If it's 32-bit, then it's got ~136 years of life. 31-bits gave
the 1970-2038 range that you're thinking of.

Phil
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top