Date: Thu, 13 Aug 2020 16:18:13 -0700 From: Paul Eggert <eggert@cs.ucla.edu> Message-ID: <bd31f6a2-b365-88b6-f341-9a3324142fc7@cs.ucla.edu> | It's pretty common to design a general-purpose API I think "common" is a stretch here. Yes, I'm sure there are a few cases, but it is hardly common. Further, those that already exist either have had to already deal with this problem, or treat it as irrelevant. Either way we don't really need to be overly concerned (that is, if this issue were breaking applications it would have become more publicised long before now). Anything for the future can easily be designed to deal with it. | > The problem with this one is that all implementations need to support it, | > or it is worthless. | | That same objection applies to the EOVERFLOW errno change Not really, as in many (perhaps most) applications, the tm being passed to strftime came from a time_t (via localtime) and hence the tm is known to fit in a time_t and EOVERFLOW cannot occur. The only cases where it is possible is where the tm comes from elsewhere (eg: a parsedate variant that returned the tm instead of a time_t) and wasn't subject to an earlier mktime() or similar. I think that is quite rare. Apps can mostly ignore the EOVERFLOW possibility, as generally it cannot happen - it is there (though should be "may fail" in strftime) for completeness rather than any real necessity. That is, some implementation coding very carefully wondered "what do I do here?" It is safe to simply ignore that one. On the contrary it is also safe to check for it, ever in an implementation which never generates the error. Adding code to deal with an error that never occurs is wasteful perhaps, but harmless. This isn't true of ERANGE, if you're coding to deal with the possibility that it might not be implemented, then it is useless, if you're assuming that ERANGE will be generated, then your code breaks on implementations (standards compliant or not) which don't generate it. | > when more is needed, the check is not all that difficult | > (after a successful return, which is what it would be if the -1 indicates | > that 1969 date, the tm has been normalised, Dec 31, 1969 was a Wednesday, | > so set tm_wday to 0, and then check if on return it has become 3 or 4 | | I've used that hack too, but neither POSIX nor C11 require the hack to work. You mean because POSIX doesn't say what will be in the struct tm (*timeptr) after a failure, and you're imagining the possibility that the implementation might return -1, and simultaneously set the struct tm to be the Dec 31 23:59:59 UTC value ? Really? I suppose that might be technically allowed, as the spec doesn't indicate what will be in the struct after an error, but I don't really believe that this is something to be concerned about. We could have added to POSIX in the "ERRORS" case "the contents of the structure pointed to by timepointer shall be unspecified, except that they shall not represent any value which may have a time_t representation of (time_t)-1." (or words to that effect). That wouldn't break any actual implementations, and would allow this "hack" to work. | What I'd propose is that the standard specifies that mktime must | not not update tm_yday when it fails. | This matches all implementations that I know of, and | allows the hack to work. That's reasonable too. | > Another way, if -1 is returned, is to take the original struct tm | > do tm_sec++ and then mktime() again. | | This won't work if tm_sec == INT_MAX. Really! Can you find a reasonable value for INT_MAX where value%60 == 59 ? I can't... So all that is needed to deal with that case is if (tm_sec == INT_MAX) /* error case */; else { tm_sec++; if (mktime() != 0) /*error case*; else /* really was Dec 31 1969! */; } But if you're really concerned about that, then do it as if (tm_sec == INT_MAX) { tm_sec--; if (mktime() != (time_t)-2) /* error case */; else /* was Dec 31, 1969, UTC */; } else { tm_sec++; if (mktime() != (time_t)0) /* error case */; else /* was Dec 31, 1969 */; } which could be coded more compactly (just one mktime call) by adding a new var to hold the "not an error" value expected from the two cases. kre