Austin Group Defect Tracker

Aardvark Mark IV

Viewing Issue Simple Details Jump to Notes ] Issue History ] Print ]
ID Category Severity Type Date Submitted Last Update
0001797 [Issue 8 drafts] System Interfaces Objection Error 2024-01-15 23:56 2024-02-12 16:11
Reporter eggert View Status public  
Assigned To
Priority normal Resolution Open  
Status New   Product Version Draft 4
Name Paul Eggert
Organization UCLA Computer Science Dept.
User Reference strftime-%s
Section strftime
Page Number 2136
Line Number 69836-69837
Final Accepted Text
Summary 0001797: strftime "%s" should be able to examine tm_gmtoff
Description strftime’s %s conversion specification is intended to be the inverse of localtime. That is, if strftime(buf, sizeof buf, "%s", localtime(&t)) succeeds, buf should contain the decimal representation of t.

Unfortunately when TZ is set to a geographic location, this does not work on many POSIX platforms in some unusual cases. It has even been argued that POSIX 202x/D4 requires cases like these to not work. If that argument is correct, this is a regression from POSIX 2017 and should be corrected. This correction can be made without invalidating existing POSIX platforms.

Here is an example of the problem, set in Caracas:

  #include <stdio.h>
  #include <stdlib.h>
  #include <time.h>

  main ()
    char buf[100];
    time_t t = 1197184500;
    setenv ("TZ", "America/Caracas", 1);
    strftime (buf, sizeof buf, "%s = %Y-%m-%d %H:%M:%S %z (%Z) in Caracas",
          localtime (&t));
    printf ("%lld prints as %s\n", (long long) t, buf);
    if (atoll (buf) != t)
      puts ("time_t mismatch!");

On Ubuntu 23.10, this outputs:

  1197184500 prints as 1197182700 = 2007-12-09 02:45:00 -0430 (-0430) in Caracas
  time_t mismatch!

In response to Dag-Erling Smørgrav’s bug report[1] against TZDB’s strftime implementation, I recently fixed[2] TZDB strftime to use tm_gmtoff when converting %s, so that the Caracas example is guaranteed to output this:

  1197184500 prints as 1197184500 = 2007-12-09 02:45:00 -0430 (-0430) in Caracas

This is what users would invariably expect from strftime.

However, on the TZ mailing list Robert Elz objected[3] that the fix does not conform to POSIX 202x/D4, because POSIX requires that strftime %s must work by calling the equivalent of mktime() and thereby ignoring tm_gmtoff, something that would inevitably result in "time_t mismatch!" output in some situations like this. (Although the original bug report was against gmtime+strftime, a similar bug can occur with localtime+strftime as in the Caracas example.)

If POSIX 202x/D4 actually requires the "time_t mismatch!" output in cases like [1] or like the Caracas example, this is a bug in 202x/D4 that was not present in POSIX 2017. Even if many implementations have this bug, POSIX should not require strftime %s to be incompatible with localtime, and it should allow the TZDB fix.

Here is a bit more explanation about why Ubuntu 23.10 and many other platforms misbehave on the Caracas example. When TZ="America/Caracas", the two timestamps 1197182700 and 1197184500 convert via localtime into struct tm values with identical tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, and tm_isdst members. This happens because Venezuela changed standard time by moving its clock back 30 minutes on 2007-12-09 at 03:00, and tm_isdst is therefore zero for both timestamps. This can be observed via the following shell transcript run on Ubuntu 23.10, using TZDB’s zdump utility:

  $ zdump -V -c 2007,2008 America/Caracas
  America/Caracas Sun Dec 9 06:59:59 2007 UT = Sun Dec 9 02:59:59 2007 -04 isdst=0 gmtoff=-14400
  America/Caracas Sun Dec 9 07:00:00 2007 UT = Sun Dec 9 02:30:00 2007 -0430 isdst=0 gmtoff=-16200

With the fix[2], TZDB strftime examines tm_gmtoff when converting %s; this lets strftime disambiguate struct tm values in cases like these. However, POSIX 202x/D4 does not list tm_gmtoff as one of the struct tm members that strftime can examine, and that is the basis for the objection[3] that POSIX prohibits the fix.

[1]: [^]
[2]: [^]
[3]: [^]
Desired Action In section strftime() DESCRIPTION conversion specifier ‘s’, page 2136 lines 69836-69837, replace this:

Replaced by the number of seconds since the Epoch as a decimal number, calculated as described for mktime(). [tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_isdst]

with this:

Replaced by the number of seconds since the Epoch as a decimal number, calculated as described for mktime(), except that tm_gmtoff is also available to determine seconds east of UTC. [tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_isdst, tm_gmtoff]

At the end of section strftime() RATIONALE, after page 2142 line 70105, add the following paragraph:

For the conversion specifiers %s, %z and %Z, it is unspecified whether strftime generates replacement text by examining tm_gmtoff and tm_zone, or by examining tm_isdst along with global state set by tzset(), or by some combination of the two. For example, when using strftime() on the output of gmtime(), it is unspecified whether %z converts to "+0000" or to a string suitable for the local time zone, and it is unspecified whether %s converts to the timestamp passed to gmtime() or to the same timestamp numerically adjusted by local time’s seconds east of UTC.
Tags No tags attached.
Attached Files

- Relationships
related to 0001533Applied 1003.1(2016/18)/Issue7+TC2 struct tm: add tm_gmtoff (and tm_zone) field(s) 

-  Notes
steffen (reporter)
2024-01-16 01:42

Wait, i see what is meant. I delete my note.
eblake (manager)
2024-02-12 16:11

Feedback from Paul Eggert via email:

On 2024-02-05 08:15, Eric Blake wrote:

> Did you consider the effect of the change on applications that
> populate struct tm directly (and don't currently set tm_gmtoff, except
> perhaps by zeroing the structure)?

Yes. Very few apps do that. (I looked for some in the GNU code I help
maintain, and found none.) They are greatly outnumbered by the applications
that call localtime/localtime_r/mktime/gmtime/gmtime_r/etc. and pass the
result to strftime, which is what this bug report is about.

> Does the latest tzdata code only use tm_gmtoff in the rare cases when
> it is necessary for disambiguation, or is it always used (overriding
> the timezone data)? The bug description implies the former, but the
> desired action would allow the latter.

The former. That is, TZDB 2024a strftime looks only at tm_gmtoff, tm_year,
tm_mon, tm_day, tm_hour, tm_min, and tm_sec to determine %s, because that's
all you need.

The desired action allows either the TZDB behavior, or the glibc behavior
which if I recall consults tm_gmtoff only when tm_isdst is ambiguous. The TZDB
behavior is technically better than the glibc behavior for three reasons: (1)
it removes a multithreading bottleneck, (2) even in a single-threaded platform
it's faster because mktime is slower than using tm_gmtoff, and (3) when user
code mistakenly calls gmtime and then strftime then %s does what the user
expects. The bug report that caused TZDB to behave this way was about (3), but
(1) and (2) also play a part.

- Issue History
Date Modified Username Field Change
2024-01-15 23:56 eggert New Issue
2024-01-15 23:56 eggert Name => Paul Eggert
2024-01-15 23:56 eggert Organization => UCLA Computer Science Dept.
2024-01-15 23:56 eggert User Reference => strftime-%s
2024-01-15 23:56 eggert Section => strftime
2024-01-15 23:56 eggert Page Number => 2136
2024-01-15 23:56 eggert Line Number => 69836-69837
2024-01-16 01:29 steffen Note Added: 0006623
2024-01-16 01:42 steffen Note Added: 0006624
2024-01-16 01:42 steffen Note Deleted: 0006623
2024-02-01 16:42 nick Relationship added related to 0001533
2024-02-12 16:11 eblake Note Added: 0006651

Mantis 1.1.6[^]
Copyright © 2000 - 2008 Mantis Group
Powered by Mantis Bugtracker