A NOTE has been added to this issue. ====================================================================== https://austingroupbugs.net/view.php?id=1627 ====================================================================== Reported By: kre Assigned To: ====================================================================== Project: 1003.1(2016/18)/Issue7+TC2 Issue ID: 1627 Category: System Interfaces Type: Enhancement Request Severity: Objection Priority: normal Status: New Name: Robert Elz Organization: User Reference: Section: XSH 3/mktime Page Number: 1331 Line Number: 44310-44332, 44361 Interp Status: --- Final Accepted Text: ====================================================================== Date Submitted: 2023-01-05 12:17 UTC Last Modified: 2023-08-07 09:05 UTC ====================================================================== Summary: XSH 3 / mktime() is woefully underspecified ====================================================================== Relationships ID Summary ---------------------------------------------------------------------- related to 0001614 XSH 3/mktime does not specify EINVAL an... ======================================================================
---------------------------------------------------------------------- (0006415) geoffclare (manager) - 2023-08-07 09:05 https://austingroupbugs.net/view.php?id=1627#c6415 ---------------------------------------------------------------------- Suggested resolution ... On page 113 line 3186 section 4.16 Seconds Since the Epoch, change:<blockquote>The relationship between the actual time of day and the current value for seconds since the Epoch is unspecified.</blockquote>to:<blockquote>The relationship between the actual date and time in Coordinated Universal Time, as determined by the International Earth Rotation Service, and the system's current value for seconds since the Epoch is unspecified.</blockquote> On page 1331 line 44320 section mktime(), after applying bugs 1613 and 1614 change:<blockquote>The relationship between the <b>tm</b> structure (defined in the <b><time.h></b> header) and the time in seconds since the Epoch is that the result shall be as specified in the expression given in the definition of seconds since the Epoch (see [xref to XBD 4.19]) corrected for the offset of the timezone's standard time from Coordinated Universal Time and further corrected (if applicable--see below) for Daylight Saving Time, where the names other than <i>tm_yday</i> in the structure and in the expression correspond, and the <i>tm_yday</i> value used in the expression is the day of the year from 0 to 365 inclusive, calculated from the members of the <b>tm</b> structure specified above.</blockquote>to:<blockquote>The <i>mktime<i>() function shall calculate the time in seconds since the Epoch to be returned as if by manipulating the members of the <b>tm</b> structure according to the following steps. 1. The <i>tm_sec</i> member may, but should not, be brought into the range 0 to 60, inclusive. For each 60 seconds added to or subtracted from <i>tm_sec</i>, a decrement or increment, respectively, of 1 minute shall be saved for later application. 2. The <i>tm_min</i> member shall be brought into the range 0 to 59, inclusive, and any saved decrement or increment of minutes shall then be applied, repeating the range adjustment afterwards if necessary. For each 60 minutes added to or subtracted from <i>tm_min</i>, a decrement or increment, respectively, of 1 hour shall be saved for later application. 3. The <i>tm_hour</i> member shall be brought into the range 0 to 23, inclusive, and any saved decrement or increment of hours shall then be applied, repeating the range adjustment afterwards if necessary. For each 24 hours added to or subtracted from <i>tm_hour</i>, a decrement or increment, respectively, of 1 day shall be saved for later application. 4. The <i>tm_mon</i> member shall be brought into the range 0 to 11, inclusive. For each 12 months added to or subtracted from <i>tm_mon</i>, a decrement or increment, respectively, of 1 year shall be saved for later use. 5. The <i>tm_mday</i> member shall be brought into the range 1 to 31, inclusive, and any saved decrement or increment of days shall then be applied, repeating the range adjustment afterwards if necessary. Adjustments downwards shall be applied by subtracting the number of days (according to the Gregorian calendar) in month <i>tm_mon</i>+1 of the year obtained by adding/subtracting any saved increment/decrement of years to the value <i>tm_year</i>+1900, and then incrementing <i>tm_mon</i> by 1, repeated as necessary. Adjustments upwards shall be applied by adding the number of days in the month before month <i>tm_mon</i>+1 of the year obtained by adding/subtracting any saved increment/decrement of years to the value <i>tm_year</i>+1900, and then decrementing <i>tm_mon</i> by 1, repeated as necessary. During these adjustments, the <i>tm_mon</i> value shall be kept within the range 0 to 11, inclusive, by applying step 4 as necessary. 6. If the <i>tm_mday</i> member is greater than the number of days in month <i>tm_mon</i>+1 of the year obtained by adding/subtracting any saved increment/decrement of years to the value <i>tm_year</i>+1900, that number of days shall be subtracted from <i>tm_mday</i>, and <i>tm_mon</i> shall be incremented by 1. If this results in <i>tm_mon</i> having the value 12, step 4 shall be applied. 7. The number of seconds since the Epoch in Coordinated Universal Time shall be calculated from the range-corrected values of the relevant <b>tm</b> structure members (or the original value where a member was not range corrected) as specified in the expression given in the definition of seconds since the Epoch (see [xref to XBD 4.19]), where the names other than <i>tm_year</i> and <i>tm_yday</i> in the structure and in the expression correspond, the <i>tm_year</i> value used in the expression is the <i>tm_year</i> in the structure plus/minus any saved increment/decrement of years, and the <i>tm_yday</i> value used in the expression is the day of the year from 0 to 365 inclusive, calculated from the <i>tm_mon</i> and </i>tm_mday</i> members of the <b>tm</b> structure, for that year. 8. The time since the Epoch shall be corrected for the offset of the local timezone's standard time from Coordinated Universal Time. 9. The time since the Epoch shall be further corrected (if applicable--see below) for Daylight Saving Time.</blockquote> On page 1332 line 44357 section mktime(), change APPLICATION USAGE from "None" to:<blockquote>When using <i>mktime</i>() to add or subtract a fixed time period (one that always corresponds to a fixed number of seconds) to or from a broken-down time in the local timezone, reliable results for arbitrary <i>TZ</i> can only be assured by using <i>mktime</i>() to convert the original broken-down time to a time since the Epoch, adding or subtracting the desired number of seconds to that value, and then calling <i>localtime</i>() with the result. The alternative of adjusting the broken-down time before calling <i>mktime</i>() may produce unexpected results if the original and updated times are on different sides of a geographical timezone change. On implementations that follow the recommendation of not range-correcting <i>tm_sec</i> (see step 1 in the DESCRIPTION), reliable results can also be assured by adding or subtracting the desired number of seconds to <i>tm_sec</i> (and not modifying any other members of the <b>tm</b> structure). In applications needing to be portable to non-POSIX systems where the <b>time_t</b> encoding is not a count of seconds, it is recommended that conditional compilation is used such that the adjustment is performed on the <i>mktime</i>() return value when possible, and otherwise on the <i>tm_sec</i> member. For timezones that are known not to have geographical timezone changes, such as <tt>TZ=UTC0</tt>, adjustments using just <i>mktime</i>() do not have this problem. The way the <i>mktime</i>() function interprets out-of-range <b>tm</b> structure fields might not produce the expected result when multiple adjustments are made at the same time. For example, if an application tries to go back one day first and then one year by calling localtime(), decrementing <i>tm_mday</i> and <i>tm_year</i>, and then calling <i>mktime</i>() this would not produce the expected result if it was called on 2021-03-01 because <i>mktime</i>() would see the supplied year as 2020 (a leap year) and correct Mar 0 to Feb 29, whereas the intended result was Feb 28. Such issues can be avoided by doing multiple adjustments one at a time when the order in which they are done matters. Examples of how <i>mktime</i>() handles some adjustments are: <ul><li>If given Feb 29 in a non-leap year it treats that as the day after Feb 28 and gives back Mar 1.</li> <li>If given Feb 0 it treats that as the day before Feb 1 and gives back Jan 31.</li> <li>If given 21:65 it treats that as 6 minutes after 21:59 and gives back 22:05.</li> <li>If given <i>tm_isdst</i>=0 for a time when DST is in effect, it gives back a positive <i>tm_isdst</i> and alters the other fields appropriately.</li> <li>If there is a DST transition where 02:00 standard time becomes 03:00 DST and <i>mktime</i>() is given 02:30 (with negative <i>tm_isdst</i>), it treats that as either 30 minutes after 02:00 standard time or 30 minutes before 03:00 DST and gives back a zero or positive <i>tm_isdst</i>, respectively, with the <i>tm_hour</i> field altered appropriately.</li> <li>If a geographical timezone changes its UTC offset such that ``old 00:00'' becomes ``new 00:30'' and <i>mktime</i>() is given 00:20, it treats that as either 20 minutes after ``old 00:00'' or 10 minutes before ``new 00:30'', and gives back appropriately altered <b>struct tm</b> fields.</li> </ul> If an application wants to check whether a given broken-down time is one that is skipped over, it can do so by seeing whether the <i>tm_mday</i>, <i>tm_hour</i>, and <i>tm_min</i> values it gets back from <i>mktime</i>() are the same ones it fed in. Just checking <i>tm_hour</i> and <i>tm_min</i> might appear at first sight to suffice, but <i>tm_mday</i> could also change--without <i>tm_hour</i> and <i>tm_min</i> changing--if, for example, <i>TZ</i> is set to "ABC12XYZ-12" (which might be used in a torture test) or if a geographical timezone changes the offset from Coordinated Universal Time of its standard time by 24 hours.</blockquote> On page 1332 line 44359 section mktime(), change RATIONALE from "None" to:<blockquote>Implementations are encouraged not to range-correct <i>tm_sec</i> (see step 1 in the DESCRIPTION) in order for the results of making an adjustment to <i>tm_sec</i> always to be equivalent to making the same adjustment to the value returned by <i>mktime</i>(), even when the original and updated times are on different sides of a geographical timezone change. This provides a way for applications to do reliable fixed-period adjustment using only <i>mktime</i>(), as described in APPLICATION USAGE. The described method for range-correcting the <b>tm</b> structure members uses separate variables to hold adjustment values to be applied later to other members, or (for the year adjustment) used in later calculations, because this is one way of avoiding intermediate member values that are not representable as an <b>int</b>. Implementations may use other methods; all that is required is that <i>tm_year</i> is the only member for which an [EOVERFLOW] error can occur. The described method for range-correcting <i>tm_mday</i> would, if implemented that way, be highly inefficient for very large values. The efficiency can be improved by observing that any period of 400 years always has the same number of days, so the month-by-month correction method need only be applied for a maximum of 4800 months.</blockquote> On page 1332 line 44361 section mktime(), change FUTURE DIRECTIONS from "None" to:<blockquote>A future version of this standard may require that <i>mktime</i>() does not perform the optional range correction of the <i>tm_sec</i> member of the <b>tm</b> structure described at step 1 in the DESCRIPTION. A future version of this standard is expected to add a <i>timegm</i>() function that is similar to <i>mktime</i>(), except that the <b>tm</b> structure pointed to by <i>timeptr</i> contains a broken-down time in Coordinated Universal Time (rather than the local timezone), where references to <i>localtime</i>() are replaced by references to <i>gmtime</i>(), and where there are no timezone offset or Daylight Saving Time adjustments. A combination of <i>gmtime</i>() and <i>timegm</i>() will be the expected way to perform arithmetic upon a <b>time_t</b> value and remain compatible with the ISO C standard (where the internal structure of a <b>time_t</b> is not specified), since attempting such manipulations using <i>localtime</i>() and <i>mktime</i>() can lead to unexpected results.</blockquote> 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 ======================================================================