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.