big-picture comments on your proposed extensions to ISO C <time.h>

Paul Eggert eggert at twinsun.com
Tue Sep 29 22:18:54 UTC 1998


   Date: Tue, 29 Sep 1998 20:56:18 +0100
   From: Markus Kuhn <Markus.Kuhn at cl.cam.ac.uk>

   >   Instead, let's just use a signed integer count of the number of time
   >   intervals since the epoch. We can define a type stime_t for this
   >   integer, and a stime_t macro STIMES_PER_SEC giving the number of
   >   time intervals per second.  This makes time arithmetic much, much
   >   easier.

   I am afraid, but I strongly disagree here. I think my current approach
   is more robust, functional, and therefore preferable. The reason: Most C
   9X implementations will not provide any type with more than 64-bits.

A 64-bit signed type with a 1970 epoch can represent years past 9999
with 25 bits for the fraction; this is better than 100 ns resolution,
which suffices for the vast majority of practical applications today.

Conversely, the xtime proposal would limit the implementation's
ability to supply very high quality clocks in a portable way.  For
example, I don't think it would fully support Bernstein's TAI-based
approach which (if memory serves) has a 64-bit fraction.

In contrast, using a single integer will suffice even for Bernstein's
approach, so long as the C compiler supports 128-bit integers.  As
processors get faster (supporting higher-res timestamps) and bigger
(supporting longer integers), I expect this is what high-quality
implementers will choose.

In other words, I fear that the struct xtime proposal is too specific
for a C-language standard.  Its clock has too much range and precision
for most practical implementations; and yet it is not precise enough
for the most demanding applications.  It's better for the spec to be
less specific, so that it can allow both practical and
very-high-quality implementations.

   C is and will always be a comparatively simple and quite
   inconvenient early-1970s language.

The C standard should cater to C programmers, as well as to people
implementing higher-level systems atop C.  The time interface should
be both simple and convenient, as much as possible.

I've done a lot of time programming, and I find the original POSIX.1
rules (a single integer timestamp) to be much easier to use, and much
more reliable to maintain, than the C89 rules (an opaque timestamp
with problematic constructors and extractors) or the POSIX.1-1996
extensions (a separate subsecond counter).  This also goes for code
that I've seen written by others.

Simplicity is a big virtue here, because most programmers (even
hard-core implementers) make a lot of mistakes in this area.  Even the
C _standard_ (as well as the draft C9x <time.h> changes) contains
several bugs in this area, partly due to complexity.  We should strive
to keep the basic interface as simple as possible.

   I do not understand, why it should be difficult to portably add and
   subtract struct xtime values.

I've seen it done wrong so often in real code (also with its similar
BSD predecessor struct timeval).  I've even seen errors in
time-arithmetic macros in the .h files supplied by vendors!  It really
_is_ error-prone, and we should have an interface that is less likely
to provoke errors.

   Then you just add sec and nsec separately and adjust for the nsec
   overflow.

The adjustment to sec can overflow.  This requires two overflow checks
if you're worried about time overflow, whereas my proposal requires just
one overflow check.

   Note that non-leap nsec values do not overflow the nsec type

True, but leap nsec values do.  I think it makes perfect sense to add
1 second to a leap-second timestamp; you said that arithmetic is not
well-defined in that case, but I don't see why.

There are other issues involved in adding, subtracting, multiplying
and dividing struct xtime values.  (Yes, it should be convenient to
multiply and divide them -- this is needed for many kinds of interval
calculations.)  It's a very buggy process to get this all right.
Requiring users to do this is just asking for trouble.

For example, when I added sub-second resolution to GNU make (a feature
that should appear in the next version), I found the struct timespec
approach of POSIX.1-1996 to be so difficult to integrate with the
existing logic, that I was forced to pack its two values into a 64-bit
integer and use the resulting integer.  I would have been much happier
with a single-integer timestamp.

In other words, I'm not making up this objection abstractly; I'm
basing it on implementation experience.  Struct timespec is simply a
pain to use.

   How would you represent leap seconds in your stime_t arithmetic type?

The most natural approach is to have xtime_get (or a variant) return a
boolean flag specifying whether the timestamp is within a leap second.
This would be needed only with TIME_UTC timestamps.

Another possibility is to return a negative number instead (which
can't lead to any confusion).  I've toyed with this idea as well,
but it may be a bit too tricky.

   For stime_get however this is not an option: the return value is
   certainly required for comfortable error checking.

No, you can return a negative number -E such that E is suitable as an
argument to strerror.  This is a natural way to report errors for many
of the xtime functions.

   There is no practical intermediate size between a useful range and
   a 64-bit second counter.

There is if you have a single unified counter, as described above.

   > * We should specify better what happens before the epoch for TIME_UTC
   >   and TIME_TAI.

   Actually, I would suggest that we specify the functionality of a

     xtime_make(&xtp, &tmptr, NULL);

   call completely by providing an example of a correct implementation in
   the standard (should be possible in less than 50 lines). Real code is
   much clearer here than any pseudo-mathematic specification.

Good idea, though I would be surprised if it could be done in less
than 50 lines.

   As far as TIME_TAI is concerned, I do not expect *ANY*
   implementation to support xtime_conv with pre-1972 TIME_TAI
   values.

I think Bernstein's implementation does.

   >   but it's not truly portable, since int_fast64_t might
   >   have more than 64 bits.

   Please explain what the problem would be there. I don't see any.

You can't portably fwrite int_fast64_t values (or struct xtime values)
and read them back in again on another implementation.

Also, I expect C9x compiler vendors to want to have compiler options
that modify the type identified by int_fast64_t.  If such options are
used, you won't be able to fwrite a struct xtime out and read it back
in even among the same implementation.

It's that sense in which struct xtime is not a completely portable
type; you can't output it from one implementation and read it back
into another, even in textual form, without possibly having some
problems.

   The abstractness of types like time_t in ISO C 89 was not done because
   this abstractness was considered to be good and beautiful design. On the
   contrary, is was a necessary hack because the ISO C standard had to be
   backwards compatible with a few strange C implementations.

I don't agree with this assessment.  The *_t convention was more of a
POSIX creation.  For example, different vendors used different int
widths for user ids, so they had to institute uid_t for sanity's sake.
time_t was just more of the same.

   I don't know whether the Olson-style ones are standardizable unless
   we get some sort of official ISO registry for time zone
   specifications.

The IETF has talked about such a registry, but the latest relevant draft
ftp://ftp.isi.edu/internet-drafts/draft-ietf-calsch-ical-12.txt
punts, suggesting that implementers may just want to use the Olson names.
(Why should they go to the work of building a registry when us tz folks
are doing it for free?  :-)

I don't think the Olson-style names should be required; but they
should be suggested, at least in a footnote with a URL.

   Note that the tzstring will usually be directly entered by the user (say
   via a config file or via an environment variable), and the portable
   application does not have to be aware of the syntax used on this system.

Except for applications that help users choose the time zone!
There are a surprisingly large number of them.  (Emacs, say.  :-)

   > * stime_make still has the old mktime problem that the function can't
   >   distinguish a request for ``3 days after Feb 28'' from a request for
   >   ``1 month before Mar 31''.  It's silly that mktime thinks that 1
   >   month before Mar 31 is Mar 3 (or 2, if it's a leap year).  We should
   >   fix this.

   I think I fixed this very nicely by not requiring xtime_make to handle
   *any* invalid time representations.

Sorry, I missed that point.

   If you want 3 days after Feb 28, then you just add 3 * 86400 to the
   sec field.

That doesn't work in the presence of leap seconds.

   It is very difficult to define mktime overflow behavior nicely, so
   why bother if there is no need?

I'm somewhat sympathetic to this argument, but I fear that people who
are used to mktime will be less sympathetic.  With mktime, you can
easily ask for 3 months after a given date; with the new interface,
it's not so easy.

   If you need access to the zone name, then simply use strfxtime to access
   it.

OK, that sounds reasonable; but there is still one value that is quite
inconvenient to get via strftime: the UTC offset (to the nearest
second, please! :-).  Perhaps a new format spec could be added to get that?

   Timezone strings can be rather tricky to get right and a
   comfortable diagnostic might be useful here.

Having strerror decode the error number doesn't preclude having a
comfortable diagnostic.  (The error number could even encode the
position in the time zone string that contained the error.)  In
practice, though, I think this is way overkill and a simple
traditional errno value will suffice.



More information about the tz mailing list