Anonymous | Login | 2024-05-13 07:26 UTC |
Main | My View | View Issues | Change Log | Docs |
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-03-11 09:55 | |||||||
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> int 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]: https://mm.icann.org/pipermail/tz/2024-January/033488.html [^] [2]: https://github.com/eggert/tz/commit/a707253f0c3c8cfd7b66fc5ca46cfb0a01e41dee [^] [3]: https://mm.icann.org/pipermail/tz/2024-January/033549.html [^] |
|||||||||||
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 | |||||||||||||||||||||||||
|
Notes | |
(0006624) steffen (reporter) 2024-01-16 01:42 |
Wait, i see what is meant. I delete my note. |
(0006651) 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. |
(0006677) kre (reporter) 2024-02-25 06:50 edited on: 2024-02-25 06:54 |
I do not like this proposed change - as strftime(%s) is specified in the drafts for Issue 8 is just fine - it and mktime() always produce the same value, given the same (in range) values in the struct tm passed to them. Never anything different. The change that was made to the tzcode implementation to use tm_gmtoff (which despite what was said in Note: 0006651, is always used if it exists - in that implementation what TZ is set to is immaterial to strftime(%s)) was based upon a user hope for what would happen (to get back the time_t that was used to produce a particular struct tm) completely ignoring that no such invocation is required to have occurred. strftime() simply formats as text values from the struct tm which the format string requests - regardless of how those values were inserted. The "very few apps" which "do that" must not be broken by a change like this. Applications which call gmtime() and then strftime(%s) upon the result, and expect the (formatted as a string) value that was used as gmtime()'s arg to be produced are simply broken. (That usage was exactly what led to the "bug" report that was "fixed" by the tzcode change - nonsense.) Simply reject this "bug". I'd also note that while NetBSD has the 2024a version of tzcode (in HEAD anyway) the change that was made to strftime(%s) in that release was reverted, we still do things the old way, using TZ, not using tm_gmtoff. |
(0006688) eggert (reporter) 2024-02-26 18:28 |
Unfortunately draft 4 for Issue 8 has significant problems here, and these problems need to be fixed somehow. The draft does not tell users that strftime %s might inspect tm_gmtoff - something that implementations other than TZDB already do - and it is not entirely clear what the draft means by values "outside the normal range" (line 69773 in draft 4) - for example, is tm_day outside the normal range when it equals 30, and tm_mon happens to be 1? We agree that applications that call gmtime and then strftime %s are broken (unless localtime happens to be UTC). The desired action doesn't change that. These apps will still not necessarily generate the output that the app authors wanted. The desired action does need improvement, along the lines of saying that struct tm values are "in range" if they are values that localtime could have set. Expect a revised proposal soon. |
(0006689) eblake (manager) 2024-02-26 19:32 |
Based on discussions through the 26 Feb 2024 meeting with feedback from Paul Eggert, the following wording is the Austin Group's starting point for an interpretations request to be started after Issue 8 is released. Note that we still need to flesh out the interpretation Rationale (currently a placeholder in the paste below). Among others: the fact that the standard required gmtime_r() (but not gmtime()) to set tm_zone to "UTC" is inconsistent with existing practice where some implementations set it to "GMT". We may also need to add more to the Rationale section of gmtime() and/or other functions on why "UTC" is preferred but why "GMT" is permitted for historical compatibility. Interpretation response ------------------------ The standard states that the strftime() %s conversion calculates the number of seconds since the Epoch as described for mktime(), and conforming implementations must conform to this. However, concerns have been raised about this which are being referred to the sponsor. Rationale: ------------- <Place Rationale, if any, here.> Notes to the Editor (not part of this interpretation): ------------------------------------------------------- Using draft 4.0 line numbering: On page 452 lines 15762-15763 section XBD <time.h>, add CX shading to the lines for tm_gmtoff and tm_zone. On page 1211 line 41381 section gmtime() DESCRIPTION, change (already in CX shading): ...where the names in the structure and in the expression correspond. to: ...where the names in the structure and in the expression correspond; additionally, the tm_gmtoff field of the struct tm shall be set to 0, and the tm_zone field shall be set to a pointer to an implementation-defined string set to "UTC" or "GMT", which shall have static storage duration. On page 1211 line 41397 section gmtime() RETURN VALUE, delete: The structure’s tm_zone member shall be set to a pointer to the string "UTC", which shall have static storage duration. On page 1428 line 47958 section mktime(), change: shall calculate the time since the Epoch value using either the offset in effect before the change or the offset in effect after the change. to: shall calculate the time since the Epoch value using either the offset in effect before the change or the offset in effect after the change; mktime( ) may use the value of tm_gmtoff to decide which of these two results is the more appropriate to return. On page 2135 line 69773 section strftime(), change: If any of the specified values are outside the normal range, the characters stored are unspecified. to: If any of the specified values are outside the normal range, as if set by localtime( ), the characters stored are unspecified. On page 2135 line 69777 section strftime(), change: Local timezone information shall be set as though strftime( ) called tzset( ). to: It shall be implementation-defined whether local timezone information is set as though strftime( ) called tzset( ). On page 2136 line 69836 section strftime(), change: 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] to: Replaced by the number of seconds since the Epoch as a decimal number, calculated as described for mktime( ), except that if the tm_gmtoff value does not match the local timezone information, it is implementation-defined whether the number of seconds since the Epoch is calculated using the offset specified by tm_gmtoff instead of the offset derived from the local timezone information. Note that, unlike mktime( ), the tm structure member values passed to strftime( ) need to be within the normal range, as if set by localtime( ), to avoid unspecified output. [tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_isdst, tm_gmtoff] After page 2140 line 70007 section strftime(), add a new paragraph to APPLICATION USAGE: The %s conversion should not be used with a tm structure populated solely using strptime( ), since strptime( ) does not set the tm_gmtoff field and the behavior of the strftime( ) %s conversion may depend on this value. Additionally, a tm structure populated by gmtime( ) or gmtime_r( ) is generally not suitable for portable use with the %s conversion of strftime( ); although some implementations of strftime may consult the tm_gmtoff field and reproduce the same value that was provided on input to the gmtime family of functions, this conversion is not required across all implementations. |
(0006690) eblake (manager) 2024-02-26 20:02 |
We may also need to tweak wording on page 2310 line 75181 tzset() about the relation between tzset() and strftime(), if our intent is to allow implementations that do not update or consult the local timezone information for strftime() (independently of whether we accept 0001816 about obsoleting the 3 global variables aspect of tzset() |
(0006691) geoffclare (manager) 2024-02-29 12:10 |
The following is an alternative resolution to be considered, which retains the requirement for strftime() %s to give the same result as mktime() (for in-range struct tm field values). It does allow strftime() %s - and mktime() - to make use of tm_gmtoff, but only as a means to choose between two valid results. It also includes fixes for the various other related issues raised during the discussion to far. Interpretation response ------------------------ The standard clearly states that the strftime() %s conversion calculates the number of seconds since the Epoch as described for mktime(), and conforming implementations must conform to this. Rationale: ------------- There is no requirement for applications to set the tm_gmtoff member and therefore implementations cannot rely on it having been set. However, in situations where there are two valid results of the conversion, an implementation may choose to make use of tm_gmtoff to decide between these two values (provided it can do so safely if tm_gmtoff is uninitialized), even though the strftime() %s conversion is not specified as making use of tm_gmtoff, since returning either of the values is allowed by the standard and the method used to choose between them is internal implementation detail that does not affect applications. Notes to the Editor (not part of this interpretation): ------------------------------------------------------- Using draft 4.0 line numbering: On page 452 lines 15762-15763 section XBD <time.h>, add CX shading to the lines for tm_gmtoff and tm_zone. On page 1211 line 41381 section gmtime() DESCRIPTION, change (already in CX shading): ...where the names in the structure and in the expression correspond. to: ...where the names in the structure and in the expression correspond; additionally, the tm_gmtoff field of the struct tm shall be set to 0, and the tm_zone field shall be set to a pointer to an implementation-defined string set to "UTC" or "GMT", which shall have static storage duration. On page 1211 line 41397 section gmtime() RETURN VALUE, delete: The structure’s tm_zone member shall be set to a pointer to the string "UTC", which shall have static storage duration. On page 1428 line 47958 section mktime(), change: shall calculate the time since the Epoch value using either the offset in effect before the change or the offset in effect after the change. to: shall calculate the time since the Epoch value using either the offset in effect before the change or the offset in effect after the change; mktime() may use the value of tm_gmtoff to decide which of these two results is the more appropriate to return, provided it can do so safely if tm_gmtoff is uninitialized. On page 2135 line 69773 section strftime(), change: If any of the specified values are outside the normal range, the characters stored are unspecified. to: If any of the specified values are outside the normal range, as if set by localtime(), the characters stored are unspecified. On page 2135 line 69777 section strftime(), change: Local timezone information shall be set as though strftime() called tzset(). to: It shall be implementation-defined whether local timezone information is set as though strftime() called tzset(). On page 2136 line 69836 section strftime(), change: 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] to: Replaced by the number of seconds since the Epoch as a decimal number, calculated as described for mktime(). Note that, unlike mktime(), the tm structure member values used by this conversion, other than tm_gmtoff, need to be within the normal range, as if set by localtime(), to avoid unspecified output; the value of tm_gmtoff can be uninitialized (see [xref to mktime()]). [tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_isdst, tm_gmtoff] After page 2140 line 70007 section strftime(), add a new paragraph to APPLICATION USAGE: Using a tm structure populated by gmtime() or gmtime_r() with the %s conversion of strftime() should be avoided, as the calculated seconds since the Epoch value will only correspond to the specified broken-down time if the offset from UTC of the local timezone happens to be zero at that time. |
(0006692) geoffclare (manager) 2024-02-29 12:15 |
I would prefer the resolution in Note: 0006691 over the one in Note: 0006689 (i.e. I think POSIX should not change to allow the new TZDB behaviour). I agree with the points kre made in Note: 0006677 and in particular with the statement that we must not break apps which use strftime() %s without setting tm_gmtoff. The counter-argument that there are very few such apps does not hold water, for two reasons: 1. It is only possible to survey apps for which we have access to the source. We cannot know how many closed-source apps would be affected. 2. We should not consider only apps which use %s explicitly. There are also apps which use user-specified or configurable time format strings. Although a lot of these would be passing in a struct tm obtained from localtime() (as with, for example, GNU ls -l --time-style=+%s), passing one that is populated a different way is also a reasonable thing for an app to do. For example, it could store broken-down times in a standard format and want to present them to the user in their preferred format; to do this it could use strptime() followed by strftime(). Another point in favour of disallowing "blind" use of tm_gmtoff is to consider what system implementers will do when faced with the choice of how to handle a change in POSIX to allow it. In order to continue to support apps written for POSIX.1-2017 and earlier (which did not have tm_gmtoff and therefore conforming apps cannot set it directly) and also have the TZDB behaviour for apps written for POSIX.1-2024 and later, they would need to provide two different versions of strftime(), selected according to _POSIX_C_SOURCE or _XOPEN_SOURCE. It seems highly unlikely that any system implementers would bother to do that (it has been done in the past, but only when there was no choice); the new TZDB-like behaviour would therefore be rare and become a portability gotcha. |
(0006693) shware_systems (reporter) 2024-02-29 16:19 |
Additionally, while the text above references how gmtime() should set tm_gmtoff, there is no equivalent text in localtime() for how that field should be set. Do current implementations just set it to zero, leave it as malloc() garbage, compute something if second TZ format is used? Also, it's questionable whether localtime_r() should behave as if tzset() is called, as the tm struct provided may have value set and unmodified that may conflict with the values in the TZ variable. |
(0006698) geoffclare (manager) 2024-03-01 10:09 |
> Additionally, while the text above references how gmtime() should set tm_gmtoff, there is no equivalent text in localtime() for how that field should be set. Good point. My take on this observation is that the addition in gmtime() is unnecessary. Both gmtime() and localtime() are required to set all of the fields in struct tm, and the description of tm_gmtoff in <time.h> ("Seconds east of UTC") is sufficient to require what value it is set to: in the case of gmtime(), the number of seconds east of UTC is zero because gmtime() returns the broken-down time as UTC. The reason an explicit statement is needed about tm_zone for gmtime() is because its description in <time.h> ("Timezone abbreviation") is not sufficiently clear what string this should be for gmtime(). |
(0006700) eggert (reporter) 2024-03-02 09:02 |
> 1. It is only possible to survey apps for which we have access to the source. We cannot know how many closed-source apps would be affected. Although that’s true, we can use our best judgment about which programs will benefit. We know that there are programs that benefit from the resolution in Note: 0006689, because we have a bug report to that effect and it’s plausible there are more programs like that. Although these programs may not follow the strict reading of the standard, they’re plausible uses of the standard functions. In contrast, we don’t know of programs that would benefit from the resolution in Note: 0006691, and these programs are less plausible. > 2.... There are also apps which use user-specified or configurable time format strings. Although a lot of these would be passing in a struct tm obtained from localtime() (as with, for example, GNU ls -l --time-style=+%s), passing one that is populated a different way is also a reasonable thing for an app to do. Unfortunately it’s not reasonable. These apps are broken regardless of which resolution is taken, because the issue comes up with formats other than %s. Consider the following program, which simulates a user-specified format string containing "%Z" used to format a struct tm set by hand or by strptime: #include <stdio.h> #include <time.h> #include <string.h> int main (void) { char buf[1000]; struct tm tm; /* Make TM "uninitialized". */ memset (&tm, 0x81, sizeof tm); tm.tm_isdst = 0; strftime (buf, sizeof buf, "%Z", &tm); puts (buf); } This program dumps core on GNU/Linux, on FreeBSD, and I expect on pretty much any other implementation that supports TZDB-style struct tm, because it uses a bad pointer in tm.tm_zone. And yet the program conforms to POSIX.1-2017, which says that the call to strftime sets the buffer to some string. In other words, programmers can’t trust user-specified strftime formats on anything other than completely filled-in struct tm values, and that’s true regardless of what we do about %s. I suppose we could say that GNU/Linux, FreeBSD, and pretty much everyone else is broken. But it’s better to be realistic and tell users that when using strftime’s %z, %Z and %s formats, tm_zone and tm_gmtoff must be set to in-range values. Insisting on a pedantic reading based on older POSIX would not serve users well because it would give users the wrong impression of what code will actually work. > consider what system implementers will do We implementers will figure it out. It’s the user code that matters here anyway. |
(0006701) kre (reporter) 2024-03-03 01:20 |
Re Note: 0006700 We know that there are programs that benefit from the resolution in Note: 0006689, because we have a bug report to that effect Incorrect bug reports should not be used to justify changes to anything (including tzcode). The code in that report was simply wrong, even with the wording in Note: 0006689 it would still be broken. Attempting to make broken code work is absurd. If I sent in a bug report claiming that my code, with a divide by 0, wasn't working as I expected it to, would you go fix the hardware/OS/something to generate the answer I demanded? In contrast, we don’t know of programs that would benefit from the resolution in Note: 0006691, and these programs are less plausible. Here's one, with all of the error handling, #includes, etc, removed to save space here (they exist in the real thing, naturally): main(int argc, char **argv) { struct tm tm; char *p; int i; char buf[64]; p = strptime(argv[1], "%Y-%m-%d %H:%M:%S", &tm); tm.tm_isdst = -1; for (i = 2; i < argc; i++) { setenv("TZ", argv[i], 1); tzset(); strftime(buf, sizeof buf, "%s", &tm); printf("%s %s\n", buf, argv[i]); } exit(0); } It can be run like prog 2024-03-03T07:23:46 UTC Asia/Bangkok Australia/Melbourne \ America/New_York Europe/Berlin Africa/Cairo Africa/Windhoek \ America/St_Johns America/Buenos_Aires America/Anchorage | sort -n to discover the east->west order of a set of random zones -- which in this case gives: 1709411026 Australia/Melbourne 1709425426 Asia/Bangkok 1709443426 Africa/Cairo 1709443426 Africa/Windhoek 1709447026 Europe/Berlin 1709450626 UTC 1709461426 America/Buenos_Aires 1709463226 America/St_Johns 1709468626 America/New_York 1709483026 America/Anchorage or it can be used to observe differences in summer time behaviour when used like: prog 2024-03-03T07:23:46 Australia/Darwin Australia/Adelaide \ Australia/Brisbane Australia/Sydney | sort -n 1709411026 Australia/Sydney 1709412826 Australia/Adelaide 1709414626 Australia/Brisbane 1709416426 Australia/Darwin prog 2024-06-03T07:23:46 Australia/Darwin Australia/Adelaide \ Australia/Brisbane Australia/Sydney | sort -n 1717363426 Australia/Brisbane 1717363426 Australia/Sydney 1717365226 Australia/Darwin 1717365226 Australia/Adelaide from which one observes that when summer time is not in effect, Brisbane and Sydney are the same, as are Darwin and Adelaide - with the former pair of zones achieving a wallclock time before the latter, but when summer time is in effect, all 4 are different, and Adelaide precedes Brisbane, rather than following it. There's no compelling reason to break this. I'm not sure I even really support allowing tm_gmtoff to be used (ever) to handle the very weird cases where nothing else is available to disambiguate the very odd cases, like the America/Caracas example given in the description, though if there's any good, and safe, way an implementation can truly: provided it can do so safely if tm_gmtoff is uninitialized. then it really all that important, provided the code given in this note always works as it should (where tm_gmtoff is entirely nonsense, one could init it to ~0 if desired - or leave it to generate a trap if referenced on hardware that supports that kind of thing). Also, I don't think that this: Note that, unlike mktime(), the tm structure member values used by this conversion, other than tm_gmtoff, need to be within the normal range, as if set by localtime(), to avoid unspecified output; is needed either. The reason that strftime() needs in range values in general, is not because of %s (which did not exist when that requirement was added) but to avoid problems with conversions like %a and %b if tm_wday or tm_mon are set to way out of range values (what is the name of the 29th day of the week?). But for that, all that is needed is that the values are within the ranges specified by <time.h> - there's no issue at all with tm.tm_mon = 1; /* Feb */ tm.tm_mday = 31; and then using strftime("%d %b"); The result might not make sense, but it is perfectly well defined. For %s we don't really need the "within range ..." restriction at all, because of the "calcluated as described by mktime()" - but nor do they do any harm really, as the only reason mktime() permits out of range values is to allow time arithmetic (which given the POSIX definition of time_t isn't really required by POSIX apps) and one thing I can't really imagine is anyone using strftime("%s") for that purpose. But there's no reason to make the ranges required for the %s conversion any tighter than for any other conversion that strftime() offers, (and in any case mktime() is a very poor, in fact, unworkable, interface for that feature, it works OK with gmtime()/timegm(), but it would actually be far better to create (not here, but by the implementations) a companion to difftime() (perhaps: time_t addtime(time_t, double); or something like that) to allow values to be added/subtracted from a time_t. (Then if there are implementations where time_t is not a simple integer count of seconds, they can do whatever is needed to make that work). The wording in Note: 0006691 looks OK to me (though I'd prefer the two changes just mentioned - that is, not to make the two changes referred to just above in this note). |
(0006706) geoffclare (manager) 2024-03-07 10:44 |
>> consider what system implementers will do > We implementers will figure it out. It’s the user code that matters here anyway. I think you missed my point. I expect almost all system implementers will "figure it out" by taking the easy option and sticking with Issue 7 compatible behaviour. (If they have TZDB as the upstream for this stuff, they will revert the strftime() tm_gmtoff change, as NetBSD has done.) The existence of any system where the strftime() implementation behaves like TZDB then creates a portability problem for user code written on any of the other systems; it will work fine on most systems but misbehave if/when it is ever ported to this unusual and rare system. |
(0006707) geoffclare (manager) 2024-03-07 11:04 |
> This program dumps core on GNU/Linux, on FreeBSD, and I expect on pretty much any other implementation that supports TZDB-style struct tm, because it uses a bad pointer in tm.tm_zone. And yet the program conforms to POSIX.1-2017, which says that the call to strftime sets the buffer to some string. The program conforms to POSIX.1-2017, and your results show that GNU/Linux and FreeBSD apparently do not; these implementations must be using tm_zone "blindly" and do not conform because POSIX.1-2017 requires that the only struct tm field used by %Z is tm_isdst. (This doesn't mean implementations can't use non-standard fields such as tm_zone, but if they are used, it must be in such a way that a conforming application can't tell the difference, i.e. it is just internal implementation detail. In the case of tm_zone this would mean checking the pointer is equal to a known valid value for the current timezone before using it.) |
(0006718) geoffclare (manager) 2024-03-11 09:55 |
> I suppose we could say that GNU/Linux, FreeBSD, and pretty much everyone else is broken. But it’s better to be realistic and tell users that when using strftime’s %z, %Z and %s formats, tm_zone and tm_gmtoff must be set to in-range values. For %z and %Z, requiring applications to set tm_zone and tm_gmtoff would create a conflict with the C standard. C17 says that the only member used by %z and %Z is tm_isdst. Of course, if an application explicitly sets tm_zone or tm_gmtoff then it is using an extension to C17 and this could alter the behaviour, and if the structure was populated by localtime() et al then use of tm_zone or tm_gmtoff can be an internal implementation detail, as per my previous note. However, the crucial case is when the application populates the structure in a way that does not set tm_zone or tm_gmtoff; for this case C17 requires that the conversion is done based on tm_isdst. |
Mantis 1.1.6[^] Copyright © 2000 - 2008 Mantis Group |