ISO/ IEC JTC1/SC22/WG14 N793

SC22/WG14 N793


New time functions
Clive D.W. Feather
clive@demon.net
1997-09-23


Abstract
========
This paper provides final words for the work suggested by N764 item H,
as amended by discussion at Menlo Park.


Discussion
==========
The paper does two basic things: firstly, it clarifies the specification
of the <time.h> functions in various ways, and secondly, it defines a new
extensible type /struct tmx/ to replace /struct tm/ and three new functions
to manipulate it: /mkxtime/, /zonetime/, and /strxftime/.


Proposal
========
References are of the form "T.3"; 'T' is to be replaced by the subclause
number for <time.h>, currently 7.16.

Add the following items to T.1, also adjusting the numbers in the first
paragraph and the uses of "and".

Macros:
    _NO_LEAP_SECONDS
    _LOCALTIME              (must be outside the range [-14400, +14400])

Types:
        /struct tmx/
    which is an extended version of /struct tm/. It shall contain all the
    members of /struct tm/ in a manner such that all these members are part
    of a common initial subsequence. In addition it contains the members:
        int tm_version;    /* version number */
        int tm_zone;       /* time zone offset in minutes from UTC
                              [-1439, +1439] */
        int tm_leapsecs;   /* number of leap seconds applied */
        void *tm_ext;      /* extension block */
        size_t tm_extlen;  /* size of the extension block */
    The meaning of /tm_isdst/ is also different: it is the positive number
    of minutes offset if Daylight Saving Time is in effect, zero of Daylight
    Saving Time is not in effect, and -1 if the information is not available.
    A positive value for /tm_zone/ indicates a time that is ahead of UTC.
    The implementation or a future version of this Standard may include
    further members in a separate object. If so, the /tm_ext/ member shall
    point to this object and the /tm_extlen/ object shall be its size.
    Otherwise the /tm_ext/ member shall be a NULL pointer and the value of
    the /tm_extlen/ object shall be unspecified.

Append to T.2.3 (the mktime function), after the end of the description
(paragraph 2):

    The normalization process shall be as described in subclause T.2.Y.

    If the call is successful, a second call to the /mktime/ function
    with the resulting /struct tm/ value shall always leave it unchanged
    and return the same value as the first call. Furthermore, if the
    normalized time is exactly representable as a /time_t/ value, then
    the normalized broken-down time and the broken-down time generated by
    converting the result of the /mktime/ function by a call to
    /localtime/ shall be identical.

Add the following function to T.2 after mktime():

    T.2.X The mkxtime function

    Synopsis

        #include <time.h>
        time_t mkxtime (struct tmx *timeptr);

    Description

    The /mkxtime/ function has the same behavior and result as the /mktime/
    function except that it takes account of the additional members.

    If the value of the /tm_version/ member is not 1, the behavior is
    undefined. If the implementation cannot determine the relationship
    between local time and UTC, it shall set the /tm_zone/ member of the
    pointed-to structure to /_LOCALTIME/. Otherwise, if the /tm_zone/
    member was /_LOCALTIME/, it shall set be set to the offset of local
    time from UTC, including the effects of the value of the /tm_isdst/
    member; otherwise the original value of the /tm_isdst/ member does not
    affect the result.

    If the /tm_leapsecs/ member is equal to /_NO_LEAP_SECONDS/, then the
    implementation shall determine the number of leap seconds that apply
    and set the member accordingly (or use 0 if it cannot determine it).
    Otherwise it shall use the number of leap seconds given. The
    /tm_leapsecs/ member shall then be set to the number of leap seconds
    actually applied to produce the value represented by the structure,
    or to /_NO_LEAP_SECONDS/ if it was not possible to determine it.

    If the call is successful, a second call to the /mkxtime/ function
    with the resulting /struct tmx/ value shall always leave it unchanged
    and return the same value as the first call. Furthermore, if the
    normalized time is exactly representable as a /time_t/ value, then
    the normalized broken-down time and the broken-down time generated by
    converting the result of the /mkxtime/ function by a call to
    /localtime/ (with /zone/ set to the value of the /tm_zone/ member)
    shall be identical.

Add a new subclause T.2.Y at the end of T.2:

    T.2.Y Normalization of broken-down times

    A broken-down time is normalized by the /mkxtime/ function in the
    following manner. A broken-down time is normalized by the /mktime/
    function in the same manner, but as if the /struct tm/ structure
    had been replaced by a /struct tmx/ structure containing the same
    values except:
        tm_version     is 1
        tm_zone        is _LOCALTIME
        tm_leapsecs    is _NO_LEAP_SECONDS
        tm_isdst       is -1, 0, or an implementation-defined positive
                       value according to whether the original member
                       is less than, equal to, or greater than zero

    If any of the following members is outside the indicated range (where
    L is LONG_MAX/8), the behavior is undefined:
        tm_year        [-L/366,  +L/366 ]
        tm_mon         [-L/31,   +L/31  ]
        tm_mday        [-L,      +L     ]
        tm_hour        [-L/3600, +L/3600]
        tm_min         [-L/60,   +L/60  ]
        tm_sec         [-L,      +L     ]
        tm_leapsecs    [-L,      +L     ] or _NO_LEAP_SECONDS
        tm_zone        [-L/60,   +L/60  ]
        tm_isdst       [-L/60,   +L/60  ] or _LOCALTIME
    The tm_version member shall be 1.

    Values S and D shall be determined as follows:
        #define QUOT(a,b) ((a) > 0 ? (a) / (b) : -(((b) - (a) - 1) / (b)))
        #define REM(a, b) ((a) - (b) * QUOT(a,b))

        SS = tm_hour * 3600 + tm_min * 60 + tm_sec +
             (tm_leapsecs == _NO_LEAP_SECONDS ? X1 : tm_leapsecs) -
             (tm_zone == _LOCALTIME ? X2 : tm_zone) * 60;

        /*
         *  X1 is the appropriate number of leap seconds, determined by
         *  the implementation, or 0 if it cannot be determined.
         *  X2 is the appropriate offset from local time to UTC,
         *  determined by the implementation, or
         *  /(tm_isdst >= 0 ? tm_isdst : 0)/.
         */

        M = REM (tm_mon, 12);
        Y = tm_year + 1900 + QUOT (tm_mon, 12);
        Z = Y - (M < 2 ? 1 : 0);
        D = Y * 365 + (Z / 400) * 97 + (Z % 400) / 4 +
            M [(int []){0,31,59,90,120,151,181,212,243,273,304,335}] +
            tm_mday + QUOT (SS, 86400);
        S = REM (SS, 86400);

    The normalized broken-down time shall produce the same values of S
    and D (though possibly different values of M, Y, and Z) as the
    original broken-down time. [*]

    [*] The effect of the above rules is to consistently use the Gregorian
    calendar, irrespective of which calendar was in use in which year. In
    particular, the years 1100 and -300 are not leap, while the years 1200
    and -400 are (these 4 years correspond to tm_year values of -800, -2200,
    -700, and -2300 respectively, and the last of these is 401 B.C.). In
    the normalized broken-down time, tm_wday is equal to /QUOT (D - 2, 7)/.

Add the following function to T.3 after localtime():

    T.3.X The zonetime function

    Synopsis

        #include <time.h>
        struct tmx *zonetime (const time_t *timer, int zone);

    Description

    The /zonetime/ function converts the calendar time pointed to by
    /timer/ into a broken-down time as represented in the specified time
    zone. The /tm_version/ member is set to 1. If the implementation
    cannot determine the relationship between local time and UTC, it shall
    set the /tm_zone/ member to /_LOCALTIME/. Otherwise it shall set the
    /tm_zone/ member to the value of /zone/ unless the latter is
    /_LOCALTIME/, in which case it shall set it to the offset of local
    time from UTC. The value shall include the effect of Daylight Savings
    Time, if in effect. The /tm_leapsecs/ member shall be set to the
    number of leap seconds (the UTC-UT1 offset) applied in the result [*]
    if it can be determined, and to the value /_NO_LEAP_SECONDS/ if it
    cannot (and so none were applied).

    [*] If the /tm_sec/ member is set to 60, that leap second shall not
    be included in the value of /tm_leapsecs/.

In T.3.5 (the strftime function), change the bracket appended to the
descriptions of %z and %Z to:

    %z [tm_isdst]
    %Z [tm_isdst]

Add the following function to T.3 after strftime():

    T.3.Y The strfxtime function

    Synopsis

        #include <time.h>
        size_t strfxtime (char * restrict s,
            size_t maxsize,
            const char * restrict format,
            const struct tmx * restrict timeptr);

    Description

    The behavior and result of the /strfxtime/ is identical to that of
    the /strftime/ function, except that the /timeptr/ parameter has a
    different type, and the /%z/ and /%Z/ conversion specifiers depend
    on both the /tm_zone/ and /tm_isdst/ members.