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 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 , 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_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 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 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.