[tz] strftime %s
Paul Eggert
eggert at cs.ucla.edu
Sun Jan 14 19:41:10 UTC 2024
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".
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-More-strftime-doc-improvements.patch
Type: text/x-patch
Size: 2527 bytes
Desc: not available
URL: <http://mm.icann.org/pipermail/tz/attachments/20240114/4ad48709/attachment.bin>
More information about the tz
mailing list