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