On 2024-01-14 06:20, Steve Summit wrote:
strftime %s really does have no choice but to do a mktime() based on barely-adequate information -- and part of that information is, alas, the global TZ environment variable.
Although that's one interpretation of the standard, it's not the only one. As I've been saying, although the POSIX and C standards can easily be misinterpreted, they have a better interpretation which says that on a system with tm_gmtoff and tm_zone strftime need not use mktime or equivalent, not even for %s. This interpretation is better because (a) it's how popular implementations work in many cases and (b) it's what users expect.
if you want to implement strftime %s, you can *not* make it easier on yourself by having localtime or gmtime helpfully stash extra information in struct tm, using fields like tm_gmtoff
Luckily you can - if you use the better interpretation.
I find myself half wishing for a big, bold warning on the strftime man page:
The struct tm handed to strftime must be one returned by an immediately preceding call to localtime or gmtime.
This is good advice, and (at least in a "should" form) it should be in POSIX. Come to think of it, it should be in tzcode's man page too. I installed the attached proposed patch to do that. While I was at it I noticed that the man page doesn't say strftime behaves as if tzset were called (even though this is no longer needed). The attached patch contains a fix for that as well.
callers *are* allowed to pass handcrafted struct tm values to strftime, and implementors are obliged to make this work
Yes, but the standards give leeway as to how to "make this work" for %z and %Z, and this leeway includes using members like tm_gmtoff and tm_zone that the C standard does not specify.
(Which brings me back to my conclusion that %s shouldn't exist, because it's impossible to implement correctly.
It's impossible only if one uses a too-strict interpretation of the standards. Let's not do that, as it would make our implementations worse, our users more confused, and our software buggier.
the big, bold warning is for people like me, who keep dreaming of a set of time-conversion functins that's halfway sane and coherent. I'm not sure where the warning goes, but it would say something like:
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, unrelated parts of the time-conversion logic. But no, this sequence is in fact utterly vulnerable to bugs in other parts of the time-conversion logic, because it is necessarily equivalent to the sequence
struct tm *tm = localtime(&t); time_t t2 = mktime(tm);
which sets t2 == t only in the presence of a perfectly- implemented mktime, and also given certain other constraints, such as that TZ has not changed.
Assuming that localtime and strftime both succeed (localtime returns non-null and strftime's output fits), then a warning stated this baldly would be incorrect for current tzcode as its strftime %s is indeed the inverse of localtime.
Perhaps there *is* a warning worthy of putting on the strftime man page, which is
Please rely on %s only if you're the implementor of date(1) or the equivalent. If you're using %s to print a time_t value that your program has explicitly, it is far less error-prone to print that value directly, than to convert it to a struct tm and print it with strftime %s.
It's true that strftime %s has problems on other platforms, so a portability warning is appropriate for tzcode strftime's man page. I put one into the attached proposed patch.
(Stay tuned for our next exciting episode, in which the programmers who used to clamor for the nonstandard timegm function now request a strftime variant whose %s specifier assumes UTC.)
NetBSD's strftime_z does that. But it's not needed in current tzcode, which addresses the problem in a simpler way.
awk 'BEGIN { print srand(srand()) }'
That is *hilarious* and it works on every machine I have easy access to! Alas, it's not guaranteed by POSIX, which merely says it outputs "time of day" not "number of seconds since the Epoch".