Anonymous | Login | 2024-12-12 13:27 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 | ||
0001627 | [1003.1(2016/18)/Issue7+TC2] System Interfaces | Objection | Enhancement Request | 2023-01-05 12:17 | 2024-06-11 09:07 | ||
Reporter | kre | View Status | public | ||||
Assigned To | |||||||
Priority | normal | Resolution | Accepted As Marked | ||||
Status | Closed | ||||||
Name | Robert Elz | ||||||
Organization | |||||||
User Reference | |||||||
Section | XSH 3/mktime | ||||||
Page Number | 1331 | ||||||
Line Number | 44310-44332, 44361 | ||||||
Interp Status | --- | ||||||
Final Accepted Text | Note: 0006415 | ||||||
Summary | 0001627: XSH 3 / mktime() is woefully underspecified | ||||||
Description |
Following on from notes added to bug:1614 and a lengthy mailing list discussion, it is evident that the specification of XSH/mktime is woefully inadequate. New text is specified in the Desired Action to remedy those defects. This is currently missing anything dealing with what should be done if the input tm_isdst is not < 0, and does not agree (in sign, if 0 can be said to have a sign) with the final value for tm_isdst in the struct tm on a successful return. That's because my inclination is to simply do nothing in that case, return the correct tm_isdst, but otherwise ignore it - but I admit that's not how the implementations behave, and that may be being depended upon by some applications (though the current behaviour is definitely not required by any standard). So I will leave it for someone who cares about that to add suitable text to (properly) specify what is to happen. Also, given that it is too late now to consider adding a timegm() function (an analog to mktime() which has existed for decades, but never been standardised) I thought what might be possible would be to specify enough in the FUTURE DIRECTIONS here to indicate that that will happen (since it is being added to the C standard, it will happen, eventually) and to indicate why using it is a much better idea when the purpose is time_t arithmetic than using localtime()/mktime(). The intent is to get applications to start writing safe code, rather than nonsense, and do that asap - since in practice, timegm() is already widely available. As usual, formatting and wording changes, which keep to the general intent expressed below are welcome. One thing I considered, but wasn't sure of a good way to handle, was to find some shorter way to express "the converted value of the struct tm referred to by timeptr" (or a field thereof) - which occurs far too often in the text below, and is unwieldy. |
||||||
Desired Action |
Delete the (CX shaded) paragraph that starts (line 44310) A positive or 0 value for tm_isdst ... and ends (line 44313) ... is in effect for the specified time. Replace the (CX) shaded paragraph that starts (line 44315) The relationship between the tm structure ... and ends(line 44321) ... the other tm structure members specified in <time.h> (excluding tm_wday). with the following (presumably also CX shaded) paragraphs: The mktime() function will first convert the tm_sec, tm_min, tm_hour, tm_mon, tm_mday and tm_mon (again) fields of the tm structure referenced by timeptr (or a local internal copy thereof), in that order, so that their values become within the ranges specified by <time.h>, but also within the ranges applicable to a Gregorian Calendar date (tm_sec shall not be more than 59, and tm_mday shall not be more than the number of days in the month specified by the tm_mon field of the year specified by the tm_year field). If _field_ represents the field being converted, and _next_ represents the field specified immediately after it in <time.h> then this shall be done, for each field, by an algorithm equivalent to: if (timeptr->_field_ < MINIMUM_VALUE) { while (timeptr->_field_ < MINIMUM_VALUE) { timeptr->_field_ += RANGE; timeptr->_next_--; /* check overflow */ } } else if (timeptr->_field_ > MAXIMUM_VALUE) { while (timeptr->_field_ > MAXIMUM_VALUE) { timeptr->_field_ -= RANGE; timeptr->_next_++; /* check overflow */ } } /* else do nothing, value of _field_ is OK */ where MINIMUM_VALUE is the minimum allowed value for the field _field_ as specified in <time.h> MAXIMUM_VALUE is the maximum allowed value for the field _field_ as specified in <time.h> except that it shall be 59 where _field_ is tm_sec, and shall be the appropriate number of days in the specific month selected by the tm_mon and tm_year fields, where _field_ is tm_mday, and thus is subject to change during each iteration of the loop, and RANGE is (MAXIMUM_VALUE - MINIMUM_VALUE + 1) (which is also subject to change, in each iteration of both loops above, where the field is tm_mday). Note that there is no requirement that the actual structure passed via *timeptr be the one being modified by this code. Should overflow (absolute value of the field becomes too large to be represented in an int) occur at the places indicated, the implementation shall return an error if the _next_ field is tm_year, and may return an error for other fields, though if _next_ is not tm_year, it may adjust the value of any later field, and reduce the magnitude of the _next_ field by an appropriate amount to compensate. Adjustments made this way should be chosen so as to minimise the effects of the adjustment upon the meaning of the later field, for example, if tm_hour were to overflow, the implementation might adjust tm_mday by 146101 (the number of days in a 400 year period - since in the Gregorian calendar, that is a constant) and reduce the magnitude of tm_hour by 3506424 (24*146101, the number of hours in 400 years). Alternatively it might alter tm_mon by 4800 (the number of months in a 400 year period), and adjust tm_hour by the same amount (3506424). Overflow produced when making any such adjustment should be handled in a similar way, including, if an adjustment to tm_mon requires an adjustment to tm_year, and that causes tm_year to overflow, then an error shall be returned. The tm_isdst field of the structure referred to by timeptr (or a local copy thereof) shall be converted by altering any value that is less than 0 to be -1, and any value that is greater than 0 to be 1. If supplied as 0, no change shall be made. Once all fields are within the appropriate ranges, the implementation shall determine if there is a unique value of the type returned by time() (which is expressed as a value in Coordinated Universal Time) which when converted to a struct tm by a function equivalent to localtime() would produce identical values for the tm_sec tm_min tm_hour tm_mday tm_mon and tm_year fields of the converted input struct tm. This may be accomplished by applying a formula, similar to that specified for Coordinated Universal Time in <xref XBD 4.17> adjusted to account for local timezone offsets, and time alterations, or by any other means. If such a unique result is found, then that shall be the result from mktime(). If no result is found because the tm structure represents a value outside the range of values that can be represented by a value returned by time(), then an error shall be returned. Otherwise if no result is able to be found, then the local time specified represents a time which does not exist as a local time value. In this case, if the value of tm_isdst in the struct tm specified by timeptr is greater than or equal to 0, and there are two values or the type returned by time(), representing times that are one second apart, (t1 and t2, where t2 == t1 + 1 second) which can be found of the type returned by time(), such that one of those, when converted by a function equivalent to localtime() returns a time which occurs before the converted time referred to by timeptr, and the other returns a time which occurs later, and also one of those would produce a struct tm with tm_isdst == 0, and the other when converted by localtime would produce a struct tm with tm_isdst == 1, then if the application's converted tm_isdst field the same as that produced by t1, then the implementation shall calculate the difference, in seconds, between the converted time specified by timeptr, and that produced by a conversion of t1, add the number of seconds to t1, and that shall be the result of mktime. Otherwise, if the applications converted tm_isdst is the same as that produced by t2, the implementation shall calculate the difference (in seconds) between the struct tm produced by t2, and that specified by the converted struct tm referred to by timeptr, and subtract that number of seconds from t2, and that shall be the result from mktime(). In any other case the result is unspecified. The implementation may arbitrarily return one of the results as if it had been one of the two specified cases, or may return an error. If more than one possible result is found, then if there are exactly two possible results, and one of those, when converted by a function equivalent to localtime(), produces a value with tm_isdst having the same value as the converted value of that field in the struct tm referred to by timeptr, and the other does not, then the result of mktime() shall be the single unique result which produces a struct tm with the same tm_sec tm_min tm_hour tm_mday tm_mon tm_year and tm_isdst fields as the converted values in the struct tm referred to by timeptr. In any other case, the result is unspecified. The implementation may arbitrarily return any of the plausible ambiguous results, or may return an error. This should then be followed by the new (bug 1613 inserted) text about what happens to the struct tm in the case of a successful return. This I believe has already replaced the "Upon successful completion, the values of the tm_wday..." paragraph. If not, delete whatever is left of it. A new paragraph (or just sentence perhaps) should be added after the 1613 inserted paragraph: When mktime() returns an error, the contents of the structure referred to by timeptr, after mktime() returns, shall be unspecified. The RETURN VALUE section (lines 44327-9) should be replaced by: The mktime() function shall return the calculated time since the epoch, as specified in the DESCRIPTION, encoded as a value of type time_t. If an error is to be returned, then the function shall return the value (time_t)-1, and set errno to indicate the error. The ERRORS section (lines 44331-2) should be replaced by The mktime() function shall fail if: [EOVERFLOW] The value of the time returned by time() which represents the converted struct tm passed by timeptr falls outside the range of values that can be represented as a time_t. [EOVERFLOW] While correcting the values of the fields of the struct tm referred to by timeptr to be within the required ranges, a required adjustment of the tm_year field caused that field to overflow. The mktime() function may fail if: [EOVERFLOW] Adjusting a field of the struct tm referred to by timeptr caused an adjustment to be required to another field, and that adjustment caused that other field to overflow. [EINVAL] The converted struct tm referred to by timeptr cannot be represented by a unique number of seconds past the epoch, Coordinated Universal Time, and the input values, and/or circumstances are not such that an alternative is required to be selected. In the FUTURE DIRECTIONS section (line 44361) replace "None." by A later edition of the standard is expected to add a timegm() function that is similar to mktime(), except that the struct tm referred to by timeptr represents a calendar time in Coordinated Universal Time (rather than the local time zone), where references to localtime() are replaced by references to gmtime(), and where there are no zone offset adjustments, or missing or ambiguous times, tm_isdst is always 0, and EINVAL cannot be returned. A combination of gmtime() and timegm() will be the expected way to perform arithmetic upon a time_t value and remain compatible with the C standard (where the internal structure of a time_t is not specified). Attempting such manipulations using localtime() and mktime() can lead to unexpected results. |
||||||
Tags | applied_after_i8d3, issue8 | ||||||
Attached Files | |||||||
|
Relationships | ||||||
|
Issue History | |||
Date Modified | Username | Field | Change |
2023-01-05 12:17 | kre | New Issue | |
2023-01-05 12:17 | kre | Name | => Robert Elz |
2023-01-05 12:17 | kre | Section | => XSH 3/mktime |
2023-01-05 12:17 | kre | Page Number | => 1331 |
2023-01-05 12:17 | kre | Line Number | => 44310-44332, 44361 |
2023-01-20 20:54 | mirabilos | Note Added: 0006118 | |
2023-07-25 14:09 | geoffclare | Relationship added | related to 0001614 |
2023-08-07 09:05 | geoffclare | Note Added: 0006415 | |
2023-08-07 09:06 | geoffclare | Note Edited: 0006415 | |
2023-08-14 12:01 | mirabilos | Note Added: 0006429 | |
2023-08-14 15:16 | geoffclare | Interp Status | => --- |
2023-08-14 15:16 | geoffclare | Final Accepted Text | => Note: 0006415 |
2023-08-14 15:16 | geoffclare | Status | New => Resolved |
2023-08-14 15:16 | geoffclare | Resolution | Open => Accepted As Marked |
2023-08-14 15:16 | geoffclare | Desired Action Updated | |
2023-08-14 15:17 | geoffclare | Tag Attached: issue8 | |
2023-09-04 10:34 | geoffclare | Status | Resolved => Applied |
2023-09-04 10:34 | geoffclare | Tag Attached: applied_after_i8d3 | |
2024-06-11 09:07 | agadmin | Status | Applied => Closed |
Mantis 1.1.6[^] Copyright © 2000 - 2008 Mantis Group |