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
0001627 [1003.1(2016/18)/Issue7+TC2] System Interfaces Objection Enhancement Request 2023-01-05 12:17 2023-01-20 20:54
Reporter kre View Status public  
Assigned To
Priority normal Resolution Open  
Status New  
Name Robert Elz
User Reference
Section XSH 3/mktime
Page Number 1331
Line Number 44310-44332, 44361
Interp Status ---
Final Accepted Text
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

                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

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 No tags attached.
Attached Files

- Relationships

-  Notes
mirabilos (reporter)
2023-01-20 20:54

Don’t break mktime of a valid struct with tm_sec=60 pointing to a leap second, please.

- 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

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