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

Antoine Leca Antoine.Leca at renault.fr
Wed Sep 30 14:01:33 UTC 1998


Paul Eggert wrote, discussing with Markus:
> 
>    >   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.

Hmm. I am not sure to follow you here, since for the vast majority of
applications, struct xtime arithmetic will be defined as following

struct xtime addxtime(struct xtime x, struct xtime y) {
  return (struct xtime){.sec = x.sec+y.sec};
}

and similar.  In words (instead of specific C9X code ;-), only
considering the seconds count, dropping completely the fractional part.

And I believe this is probably the right way to do most things
(while I agree with Paul that, in certain circumstances, this might
 prove insufficient, like his example about GNU make).

 
> 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.
<snip> 
> using a single integer will suffice even for Bernstein's
> approach, so long as the C compiler supports 128-bit integers.

This proposal have a clear drawback: like clock_t in C89, it requires
to specify the resolution of the type itself, thus requiring a lot of
multiplication/division with a constant like XTIME_PER_SEC (and
before it is noted, I note that with 128-bits integer, choosing
XTIME_PER_SEC to be 1LLL<<64 greatly eases things).


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

Well, I agree with you here, but OTOH, the present state of affairs
of time_t shows me that at least some specification is required.

Having an opaque struct xtime like the proposed struct tmx will be
the worst, of course.  But I do not like Paul's proposal if it
means a XTIME_PER_SEC either.
 

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

Agreed. Also, it should be fairly easy to build atop of it a convenient
interface using the (modern) tools provided by other programming
languages like C++, Ada or Java (not a limitative list).
But everybody agrees about that, and every proposal achieves it
in some sense, AFAIK.


> 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 is a more interesting point.
I agree with you POSIX.1's approach is by far the easiest to use.
But my understanding was that its position regarding leap seconds
(and/or handling TAI times) is far from being clear (clumbersome
is more appropriate here).
Also, *if* we disregard subseconds, Markus' proposal is essentialy
the same as the initial POSIX, isn't it?


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

Agreed here.
As I said above, most programmers *should* use only the sec field.
But certainly some will *try* to use also the nsec, and problems will
begin here.

Another idea is striking me right now:
 - keeping struct xtime
 - specifying that the first member, named sec, typed int_fast64_t, should
   be what it is now with Markus' draft
 - allowing others fields, providing they are self-containing, for
   counting more precise quantities
 - adding to the draft addxtime, diffxtime, mulxtime, divxtime
 - adding to the draft a way to convert from double to xtime and back,
   either with an API (well, two), or with a macro giving the precision
   or any subfield.

What about this sketch?

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

I do not like that.
This way way, we should add a parameter to both xtime_make and
xtime_breakup: either an input argument, or an output one.

Furthermore, there should be some way for xtime_breakup to disambiguate
between UTC and TAI: so one more parameter...

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

Yes, but it should not be *required* to be done this way: the Committee
have strong feeling against standardizing this way, since errno-mechanism
is very problematic to be used in some environments (this was tolerated
for math.h because of wide existing practice).

OTOH, I agree that errno-mechanism, or any other mechanism suitable to
the implemantation, should be relied upon, instead of standardizing
anything other (so tz_error should be dropped, IMHO).

Anyway, this is just my point of view, not the official answer from the
Committee  ;-)


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

Yes you can, as long as either:
 - all the platforms are 2's complement
 - or you isolate the sign bit in some way
 - or you deal only with positive values
because C9X now warrants that all the significants bits are the
low-order 63 ones, even if int_fast64_t happen to be say 72 bits long.

And remember you are still required to handle big-endian/little-endian
duality...

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

This one is a good point.


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

In textual form, I do not see the problem. What is wrong with

  fprintf(output, PRIdFAST64 ":" PRIdFAST32 "\n", xt.sec, xt.nsec);
then
  fscanf  (input, SCNdFAST64 ":" SCNdFAST32 "\n", xt.sec, xt.nsec);


>    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 believe you are both right.
Paul is right about the abstraction concept (also used in fpos_t, for
example, in C89).  And Markus is right, because what he implies is that
time_t could have been made much more precise in its definition (only
integer types, and/or only counts of seconds, if there was not broadly
different existing practices...)

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

This is not standard practice in the text of an ISO Standard (I think
this is even prohibited).
However, mentionning it in the Rationale, perhaps with bigger explanations,
seems more easy to do (I do not know about the URL).

 

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

Well, I too missed it (so since we are at least two, reasonably involved
on the matter, I believe it is worth a mention in the Rationale  ;-).

And I feel not comfortable with its implication: That is, to do arithmetic
with a struct tm, you need to do 

  xtime_make(&xt, &tm, &the_tz);
  if( xt.nsec>1000000000 )  // do something when a leap second occured
  xt.sec +=  3 * 86400L;
/* or
  xt.sec -= 30 * 86400L;
 */
  xtime_break(&tm, &xt, &the_tz);

in the latter case (varying of a given number of days), and

  if( --tm.tm_mon < 0 ) {
    --tm.tm_year;
    tm.tm_mon = 11;
  }
  if( tm.tm_mday < (last=last_day_in_month[isleap(tm.tm_year)][tm.tm_mon]) ) {
    tm.tm_mday = last;
  }

in the former case, assuming 1 month before Mar 31st should yield last day
of February (if it should yield Feb 28th + 3 days, the code is even more
complicated, because the last line should be followed by the snippet
above...)

The advantage is that the programmer is requested to specify the behaviour
in the latter case.  But that is certainly not 


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

Yes it does (using Markus' acceptation of xtime)...
unless of course you are referring to the point I avoided above, that
is if the beginning point was in a leap second.

In fact, this is the very point of Markus' choice for representing time.
UTC (unlike TAI) is a discontinuating scale that is viewed as continuating
for the vast majority of applications.
Markus' idea isolates the discontinuation in a separate space, thus
allowing the majority to handle times without being burdened with the
leap seconds, while keeping the notion present.

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

I think this should be left to the programmer, using first strfxtime("%z"...),
then parsing the result and multiplying the hour count by 3600, the minute
count by 60...  OK, you get the idea.
Shortly speaking, I do not see the real users' need.

(OTOH, adding a new specifier is certainly not a problem !)
 

Antoine



More information about the tz mailing list