[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