#19 Time/date problem

open
run-time (53)
1
2001-03-13
2001-03-13
No

Issues related to -1 being both an error indicator as
well as a real time. Multiple related issues are
including, see followup comments.

-bri

From: Olin Shivers <shivers@lambda.ai.mit.edu>
To: scsh@martigny.ai.mit.edu
Subject: Time/date problem
Date: 17 Apr 1997 19:58:36 -0400

I would like to describe a fundamental problem with
the Unix time system,
two possible ways to deal with it, and my decision as
to which one scsh
employs. This is your chance to lobby if you do or do
not like my choice.

The Unix function that converts a date value (a
structured description of time,
e.g. "3:24:15 am, September 30, 1985 EST") into a time
value (number of
seconds since 00:00:00 UTC, 1/1/1970) is called mktime
(). If mktime()
likes the argument you give it, it returns the time
value -- an integer.
If there's a problem, it returns -1 to indicate
something went wrong.

Consider for a moment the time value assigned to the
date
11:59:59 UTC, 12/31/1969
It's -1. The error value. Oops.

Scsh uses the mktime() function; it is called from the
Scheme TIME function.
When I first wrote the C code for Scsh's interface to
mktime(), I was very
careful about the error value. Scsh only reports an
error if *both* mktime()
returns -1 *and* the errno variable is set to a Unix
error code.

But not all errors have associated errno codes. So
this doesn't work.
Recently, scsh screwed Leif Nixon by silently
returning -1 as a real time
value when it was meant to indicate an error. Leif was
expecting a value
somewhere in the range corresponding to Spring of
1997. His program ran
forward using -1, a value from late 1969. Oops.

I have decided to change TIME so that it resolves the
ambiguity in favor of
errors -- it will always report an error when the time
returned from Unix is
-1. So if you try to find out the time for 11:59:59
1969, you will lose.

How do people feel about this? It's really bad to blow
up a program just
because the user accidentally tromped some specific
date, but it's *really*
bad not to notice when errors happen.

Unix -- there are no correct solutions, just ugly
choices.
-Olin

Discussion

  • Brian D. Carlstrom

    Logged In: YES
    user_id=27364

    From: Leif Nixon <nixon@softlab.se>
    To: scsh-news@martigny.ai.mit.edu
    Subject: Re: time/date bugs in 0.5.0
    Date: 24 Apr 1997 22:34:26 +0200

    Leif Nixon <nixon@softlab.se> writes:

    > The reason I wind up in this cond branch is that I'm
    getting
    > bogus results from time2date() in time1.c. Yes, it's
    another
    > mktime() call that returns -1. To be precise, this one:
    >
    > #ifdef HAVE_GMTOFF
    > *tz_secs = d.tm_gmtoff;
    > #else
    > { char **oldenv =
    environ; /* Set TZ to UTC \ */
    >
    environ=utc_env; /* time
    temporarily. \ */
    > tzset(); /* NetBSD, SunOS POSIX-noncompliance
    requires this. */
    > *tz_secs = mktime(&d) - t;
    > environ=oldenv;
    > }
    > #endif

    If I patch this up to set the d.is_dst field to 0 and
    to check for -1 return values from mktime(), the 'date'
    function returns legal values. The values even denote the
    correct time, but I still lose since it is only the first
    call to 'date' that returns date values in the local time
    zone:

    Scsh 0.5
    > (define d1 (date))
    > (define d2 (date))
    > (date:tz-name d1)
    "MET DST-2"
    > (date:tz-name d2)
    "UCT"
    > (time d1)
    861913321
    > (time d2)
    861913326
    > (date->string d1)
    "Thu Apr 24 22:22:01 1997"
    > (date->string d2)
    "Thu Apr 24 20:22:06 1997"
    >

    Leif Nixon SoftLab AB
    -------------------------------------------------
    E-mail: nixon@softlab.se Phone: +46 13 23 57 61
    -------------------------------------------------

     
  • Olin Shivers

    Olin Shivers - 2001-03-22

    Logged In: YES
    user_id=101304

    The following which msg, which I posted to
    comp.lang.scheme.scsh,
    explains the rationale behind the code for the time() call.
    I don't completely
    understand Leif's problem with mktime() yes, so that remains
    unadressed.
    -Olin

    From: Olin Shivers <shivers@tokyo.cc.gatech.edu>
    Subject: time() & errno
    Newsgroups: comp.lang.scheme.scsh
    Date: 21 Mar 2001 19:21:56 -0500

    This issue has been dragging on for a while. Unfortunately,
    my Posix spec went
    missing a few months ago. So today, I finally borrowed
    someone else's copy,
    sat down, read the relevant sections carefully, and thought
    the issue through
    again.

    Let me sum up some points about this time() error-detection
    issue.

    - The time() syscall has a clear ambiguity -- the -1
    error-return value
    is also a legal return value, meaning 23:59:59 December
    31, 1969.

    - It's never going to happen. Time() is very, very unlikely
    to fail.
    People are very, very unlikely to set their computer to
    December 1969.
    So we need to recognise that we are arguing a fairly fine
    point here.
    (Not that it's not important to be as correct as
    possible.)

    - A careful reading of Posix (including the rationale in
    appendix B) reveals
    that, just as Mike Sperber clalims, a conformant system is
    allowed to set
    errno to, say, 42 on a successful call. That is, the value
    of errno is *not
    defined* unless a syscall signals an error via some
    alternate means.

    This is also very, very unlikely to happen. But it's
    allowed.

    - Posix is very clear about the following:
    - if time() indicates an error condition (by returning
    -1), then it *must*
    also set errno to a non-zero value. This is specified in
    section 4.5.1.3,
    "Upon successful completion, time() returns the value of
    time. Otherwise,
    a value of ((time_t) -1) is returned and errno is set to
    indicate the
    error." All errno values in Posix are required to be
    non-zero.
    - errno is an assignable lvalue.
    In these two facts lies as much salvation as we can get.

    The scsh code currently does this:
    errno = 0;
    t = time(NULL);
    if( t == -1 && errno ) return ENTER_FIXNUM(errno);

    There are three cases pertaining to this code:
    1. If there is an error, then time() will return -1 *and*
    set errno
    to a non-zero value. So the conditional will catch it,
    and we
    will successfully detect the error. This is *absolutely*
    true.
    We *guaranteed* catch all errors. We *never* miss an
    error.

    2. If time() wins and the current time is not
    1969 December 31 23:59:59 GMT
    then time() returns a value other than -1, and we
    correctly treat
    that as a non-error value.

    3. The squirrel case: the current time is 1969 December 31
    23:59:59 GMT
    and time() wins, returning -1.
    3a Time() doesn't alter errno. This is quite likely.
    In this case, we correctly handle the situation as a
    success.

    3b Time() alters errno to a non-zero value. This is very
    unlikely:
    time() has to error out (very unlikely), the current
    time has to
    be 1969 December 31 23:59:59 GMT (very unlikely), and
    the guy
    that wrote the time() system call has to go out of his
    way to
    be a loser (setting errno to a non-zero value during a
    success).

    In this case, we *incorrectly* interpret the situation
    as
    an error return.

    In other words, this simple code
    t = time(NULL);
    if( t == -1 ) return ENTER_FIXNUM(errno);
    gets all of case 3 wrong, whereas the errno-frobbing code
    only gets 3b wrong.
    Both code fragments, however, will never allow an error to
    sneak by.

    In sum,
    - we *always* spot errors correctly;
    - in an ambiguous case, we may incorrectly decide an error
    has occurred when the function returned successfully;
    - we use errno-hacking in a Posix-compliant manner only to
    *affect
    the odds* of correctly resolving the ambiguous case;
    - ambiguity is rare, failing to resolve it correctly
    unbelievably
    rare.

    That's about as good as you can get it. OK; phew.
    -Olin

    P.S. There's another ambiguous case like this, mktime().
    Unfortunately,
    mktime() is not a Posix system call and doesn't use
    errno. It only
    returns an ambiguous -1 for error, with no other hint
    that can be used to
    disambiguate the error case from the success case. An
    early version of
    scsh incorrectly used the errno-frobbing trick on
    mktime(), which is not
    correct. The current version *doesn't* use the trick,
    consistently
    resolving the ambiguity in favor of an error return. So
    if you try to
    convert
    23:59:59 December 31, 1969 GMT
    to a time value, you will always lose, guaranteed. But
    there's nothing
    that can be done; it's just a limitation of Posix.
    Perhaps one day there
    will be an alternate function to call, with an
    unambiguous semantics,
    that is standard & widely available; then scsh could
    move to it.

     

Log in to post a comment.

Get latest updates about Open Source Projects, Conferences and News.

Sign up for the SourceForge newsletter:





No, thanks