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