kre wrote, quoting me:
| > You might think that the sequence | > struct tm *tm = localtime(&t); | > strftime(buf, sizeof buf, "%s", tm); | > is fundamentally guaranteed to place a decimal representation | > of t into buf, where "fundamentally" implies that it just | > *has* to work, even in the face of serious bugs in other,
Come on, be serious.
Oh, I was being perfectly serious, just not in the perfectly literal way you interpreted it. A good portion of the long message of mine you're referring to was, I admit, primarily an internal dialog with myself, and therefore not necessarily worthy of posting to this list. But what I was convincing myself of was precisely, as you put it, that the number generated by strftime %s:
...is *not* the time_t value that produced this struct tm - it cannot be, as no such thing need exist.
But before I convinced myself of this, whenever I used to see that 'date +%s' was for printing what is, for all intents and purposes, a raw time_t value, I imagined that's what it did: print the raw time_t value. So it follows that if someone is trying to implement all of date(1)'s '+' options using strftime, with the implication that strftime has to be able to do %s, it further follows that strftime has to be able to -- somehow -- access that raw time_t value. But, hang on, don't jump down my throat and correct me again, because, I know: that's wrong. But the way for me to *remember* that it's wrong, so I can avoid making these mistakes again, is to remind myself that strftime's computation of %s is *not* a simple operation: it's a complex transformation, more or less exactly equivalent to mktime. It's potentially lossy, and it does what you expect (if your expectation is even correct) only if you use it very carefully, paying attention to subtle facets of the documentation which are easy to overlook or misinterpret. One which has been mentioned is that TZ has not changed. Another is that tzset either has or has not been called. Yet another (which I don't think has been mentioned yet) is that tm_isdst is set correctly. But, yes, if you're careful of all those things, %s will work correctly. But will it do what you want? And, more importantly, are those really the only things you have to be careful of? Or are there others that you've forgotten? Or might there be others in the future? I shouldn't have said "even in the face of serious bugs", that was sort of a personal shorthand. The point is that, even if there *aren't* "bugs in other parts of the time-conversion logic", there might be bugs in your understanding of what %s does, and there might be bugs in your ability to uphold the guarantees that %s requires. So if you've got a time_t value you want to eventually print out in raw form, printf with a format of either %ld or %lld is a much, much more reliable way of doing it that going around the barn with localtime and strftime %s. (That's why I brought up the analogy of atof and printf %f.) But, anyway, I think your disagreement is not with my conclusion, but rather, with the arguments I used to reach my conclusion. I assure you, though, that in *my* head, those *are* the arguments necessary to reinforce the correct conclusion! (And then again: given the variety of interpretations just in this thread, and even if you do succeed in convincing everyone here that your interpretation of strftime %s is the correct one, how sure can we be that all other implementations will agree? Perhaps I shouldn't have walked back those words "even in the face of serious bugs"; it may be that those serious bugs -- in some other implementations -- are more or less inevitable!)
| > (Which brings me back to my conclusion that %s | > shouldn't exist, because it's impossible to implement correctly.
Nonsense. It is trivial to implement correctly.
A laughable conclusion, given the complexity of this thread! But I think you mean, the long and the short of a proper %s implementation is to call mktime on the struct tm handed to strftime, and interpolate the result. Right now I do agree that's the correct implementation, and you're right, it's trivial. (But it's like that old joke about the lecturer who, after being questioned about whether a certain result is truly "obvious", spends half an hour alternately deep in thought or scribbling abstrusely on the chalkboard, before triumphantly concluding, "Yes, I was right, it is obvious.")
Perhaps you mean that the specification does not achieve what you want it to produce - that's a different issue entirely.
Indeed, and that's partly what I meant -- but I don't believe it's irrelevant. An implementation that perfectly implements a useless specification isn't useful. strftime %s isn't useless, so let me amend that to, an implementation that perfectly implements a problematic specification may remain problematic.
| > Please rely on %s only if you're the implementor of | > date(1) or the equivalent.
Nonsense. Further the implementor of date(1) doesn't care about %s at all, the '+format' operand is simply passed directly to strftime
No, I know, but if the implementor of date(1) has a specification of the format specifiers accepted by '+', it might be prudent to vet that list against the specification of the strftime call that's about to be used.
Incidentally, a bug free localtime() is much harder to achieve than a bug free mktime(), as mktime() can easily be implemented simply by making calls to localtime() and comparing the results with the input struct tm, until the input time_t to locatime() which produces the expected results is found.
And of course that's precisely how some implementations of mktime *do* work!