 
 JTC1/SC22/WG14 
N793
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.