time_t problem: representing an error value

Discussion in 'C Programming' started by Dom Bannon, May 6, 2010.

  1. Dom Bannon

    Dom Bannon Guest

    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
     
    Dom Bannon, May 6, 2010
    #1
    1. Advertising

  2. Dom Bannon

    Öö Tiib Guest

    On May 6, 12:04 pm, Dom Bannon <> wrote:
    > 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.
     
    Öö Tiib, May 6, 2010
    #2
    1. Advertising

  3. Dom Bannon

    Eric Sosman Guest

    On 5/6/2010 5:04 AM, Dom Bannon wrote:
    > 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.

    --
    Eric Sosman
    lid
     
    Eric Sosman, May 6, 2010
    #3
  4. Dom Bannon

    Dom Bannon Guest

    On Thu, 06 May 2010 06:03:16 -0500, Sam <> wrote:

    >(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
     
    Dom Bannon, May 6, 2010
    #4
  5. Dom Bannon

    Tom St Denis Guest

    On May 6, 8:27 am, Dom Bannon <> wrote:
    > On Thu, 06 May 2010 06:03:16 -0500, Sam <> wrote:
    > >(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


    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.
     
    Tom St Denis, May 6, 2010
    #5
  6. Sam <> writes:
    > Dom Bannon writes:
    >> 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?

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

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, May 6, 2010
    #6
  7. William Ahern <william@wilbur.25thandClement.com> writes:
    > Dom Bannon <> wrote:
    >> 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 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.

    [...]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, May 6, 2010
    #7
  8. On May 6, 2:04 am, Dom Bannon <> wrote:
    > 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;
     
    Michael Angelo Ravera, May 6, 2010
    #8
  9. William Ahern <william@wilbur.25thandClement.com> writes:
    > Keith Thompson <> wrote:
    >> William Ahern <william@wilbur.25thandClement.com> writes:
    >> > Dom Bannon <> wrote:
    >> >> 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 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.

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

    [...]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, May 6, 2010
    #9
  10. Dom Bannon

    James Kanze Guest

    On 6 May, 12:03, Sam <> wrote:
    > Dom Bannon writes:
    > > 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?


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

    --
    James Kanze
     
    James Kanze, May 7, 2010
    #10
  11. Dom Bannon

    James Kanze Guest

    On 6 May, 16:55, Keith Thompson <> wrote:
    > Sam <> writes:


    [...]
    > (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++).

    --
    James Kanze
     
    James Kanze, May 7, 2010
    #11
  12. Dom Bannon

    Puppet_Sock Guest

    On May 6, 8:02 am, Eric Sosman <> wrote:
    [snip]
    >      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
     
    Puppet_Sock, May 7, 2010
    #12
  13. On Thu, 06 May 2010 08:55:51 -0700, Keith Thompson <>
    wrote:

    > Sam <> writes:
    > > Dom Bannon writes:
    > >> 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.

    <snip>
    > > (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.

    <snip>
    > (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.
     
    David Thompson, May 17, 2010
    #13
  14. Dom Bannon

    Phil Carmody Guest

    David Thompson <> writes:
    > On Thu, 06 May 2010 08:55:51 -0700, Keith Thompson <>
    > wrote:
    >> 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)


    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
    --
    I find the easiest thing to do is to k/f myself and just troll away
    -- David Melville on r.a.s.f1
     
    Phil Carmody, May 17, 2010
    #14
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steven T. Hatton
    Replies:
    9
    Views:
    19,571
    marbac
    Apr 30, 2004
  2. Sven
    Replies:
    18
    Views:
    970
    Chris Torek
    Aug 20, 2006
  3. Mike Copeland
    Replies:
    2
    Views:
    315
    James Kanze
    Nov 20, 2008
  4. Chris McDonald

    The maximum value held by a time_t ?

    Chris McDonald, Apr 29, 2009, in forum: C Programming
    Replies:
    4
    Views:
    4,118
    Ian Collins
    Apr 30, 2009
  5. Dom Bannon
    Replies:
    13
    Views:
    1,163
    Phil Carmody
    May 17, 2010
Loading...

Share This Page