This document specifies a proposed extension to the standard C application programming interface (API) for time. It adds new primitives suitable for applications requiring higher-quality access to timestamps. Please see the Rationale in section 2 for a discussion about these extensions.
This document is drafted as an extension to the standard C99 library, standardized in ISO/IEC 9899:1999. It is suitable for use in other extensions to the C standard, notably POSIX.1, which is standardized in ISO/IEC 9945-1:1996.
To avoid repetition, this document does not repeat the existing <time.h> specification already present in C99. Instead, it specifies only the proposed extensions to the standard.
The macros defined are
CLOCK_LOCAL CLOCK_MONOTONIC CLOCK_TAI CLOCK_REALTIME
each of which expands to an integer constant expression with a clock type value, suitable as an argument to xtime_get;
CLOCK_SYSTEM
which expands to an integer expression with a clock type value that is supported by the implementation;
CLOCKOPT_MAXSTAMP CLOCKOPT_MINSTAMP CLOCKOPT_NOW CLOCKOPT_PRECISION CLOCKOPT_RESOLUTION CLOCKOPT_UTC_OFFSET
each of which expands to an integer constant expression with a clock option value, which can be added to a clock type like CLOCK_REALTIME; and
XTIME_MIN XTIME_MAX
which expand to integer constant expressions of type xtime_t, giving the minimum and maximum values for the type.
The types declared are
xtime_t
which is a signed integer type used for timestamps and other values; and
timezone_t
which is an object type that can hold all or part of the information needed to translate between a clock type's timestamp and a broken-down time.
An error number is a positive value E such that strerror(E) != NULL. An erroneous value is a negative value E such that xtime_errno(E) != 0.
A timestamp T is an integer that identifies the point in time that is T intervals after a clock type's reference point, called an epoch. The size of the intervals depends on the clock type. Negative timestamps identify points of time before the epoch. A clock type's timestamps form a contiguous subrange of the xtime_t values. XTIME_MIN and erroneous values are not timestamps.
When the true result falls between one representable value and the next, functions described in this section always return the lesser value.
xtime_t xtime_get(int flag);
The xtime_get function returns information according to flag, which shall be the sum of a clock type and a clock option.
The following clock types are defined by this standard:
The following clock options are defined by this standard:
If an error is detected, the xtime_get function returns an erroneous value. Otherwise the function returns the requested value.
xtime_t now = xtime_get(CLOCKOPT_NOW | CLOCK_REALTIME);
if (now < 0)
printf("UTC not supported: %s\n", strerror(xtime_errno(now)));
void xtime_delay(xtime_t xt);
The xtime_delay function waits for at least xt CLOCK_MONOTONIC intervals. The function returns immediately if xt is not positive.
A time zone in this context is the information necessary to convert between a timestamp and a struct tm broken-down or string-formatted local time. This information can include not only a fixed time offset between UTC and the local time, but also algorithms that determine how the UTC offset varies during the year due to daylight-saving times regulations and even tables that determine how the offset and the daylight-saving time regulations change over the years. The following functions convert a time zone description that is provided as a text string into an in-memory representation of the time zone. Since time zone information can include tables of variable length, the size of the in-memory representation is returned.
int tz_prep(timezone_t * restrict tz,
size_t *size,
int clock_type,
const char * restrict tzstring);
The tz_prep function stores into tz (which is of *size bytes) a handle for the time zone specified in tzstring. The handle can be used only with clock_type timestamps. Update *size to reflect the number of bytes actually needed to represent the time zone.
The syntax for tzstring shall include the TZ format defined in ISO/IEC 9945-1:1996 (POSIX.1) section 8.1.1 (e.g. "CET-1CEST,M3.5.0/2,M10.5.0/3" for Central Europe in 1999); it is a full algorithmic description of the time zone.
If tzstring == NULL, then some externally defined default time zone shall be used.
By convention, at program startup, getenv("TZ") returns a value suitable for use as tzstring.
If an error is detected, the tz_prep returns an error number; the contents of the buffer and of *size are indeterminate.
Otherwise, the function returns zero and updates *size to reflect the number of bytes needed to store the time zone handle. If tz != NULL, it also stores the time zone handle into the buffer identified by tz if the previous value of *size was no less than the new value with the contents of the buffer being indeterminate otherwise.
const char *tzstring = "PST8PDT,M4.1.0,M10.5.0";
size_t size = 0;
timezone_t tz = malloc((tz_prep(NULL, &size, CLOCK_SYSTEM, tzstring), size));
if (tz == NULL)
printf("out of memory\n");
else {
int e = tz_prep(tz, size, CLOCK_SYSTEM, tzstring);
if (e != 0)
printf("Invalid time zone spec `%s': %s\n", tzstring, strerror(e));
}
xtime_t tz_get(int option,
xtime_t xt,
const timezone_t * restrict tz);
The tz_get function reports information about the timestamp region around xt according to option, which shall be a clock option. In the timestamp region, the broken-down time representation defined by tz is handled uniformly: conversion is predictable by a uniform 24h-clock and the normal Gregorian calendar with no leap seconds, UTC offset changes, or clock rate adjustments. Outside a region boundary, conversion is no longer predictable by the same set of rules, either because of a discontinuity at the boundary due to a UTC offset change or a leap second, or because the clock rate is adjusted at the boundary, or because values outside the boundary are not timestamps, or because the implementation does not have the conversion rules available.
The discontinuity for a leap second occurs at 00:00:00 UTC.
The behavior is undefined if xt is not a timestamp, or if tz is not the address of a valid time zone handle of the same clock type as the timestamp.
If an error is detected, the tz_get function returns an erroneous value. Otherwise, the returned value depends on the option value, as follows:
xtime_t now = xtime_get(CLOCKOPT_NOW | CLOCK_SYSTEM);
xtime_t resolution = xtime_get(CLOCKOPT_RESOLUTION | CLOCK_SYSTEM);
xtime_t utc_offset = tz_get(CLOCKOPT_UTC_OFFSET, now, tz);
int e = xtime_errno(utc_offset);
if (e != 0)
printf("unknown UTC offset: %s\n", strerror(e));
else
printf("%g hours east of UTC\n", utc_offset / (3600.0 * resolution));
xtime_t xtime_make(xtime_t xt,
const struct tm *tmptr,
const timezone_t *tz);
This function adds to the timestamp xt the number of years, months, days, hours, minutes, and seconds (in that order) indicated by the members of *tmptr, according to the time zone handle addressed by tz, and returns the resulting timestamp. If tz == NULL then the *tmptr values are interpreted in UTC.
The behavior is undefined if xt is not a timestamp, or if tz is not the address of a valid time zone handle of the same clock type as the timestamp.
If an error is detected, the function returns an erroneous value. Otherwise, the function returns the requested timestamp.
int xtime_breakup(struct tm *tmptr,
xtime_t xt,
const timezone_t * restrict tz);
This function converts the timestamp xt to a corresponding broken-down local time in the time zone specified by tz into *tmptr. If tz == NULL then the written *tmptr values are in UTC.
The behavior is undefined if xt is not a timestamp, or if tz is not the address of a valid time zone handle of the same clock type as the timestamp.
If an error is detected, the function returns an error number. Otherwise, the function stores the requested time into *tmptr and returns zero.
xtime_t xtime_conv(xtime_t src, int src_clock_type, int dst_clock_type);
If the xtime_conv function detects an error, it returns an erroneous value. Otherwise, the function returns the requested timestamp.
time_t xtime_conv_time(xtime_t src, int src_clock_type);
If the xtime_conv function detects an error, it returns (time_t) -1. Otherwise, the function returns the requested time.
xtime_t time_conv_xtime(time_t src, int dst_clock_type);
If the time_conv_xtime function detects an error, it returns an erroneous value. Otherwise, the function returns the requested timestamp.
int xtime_format(char * restrict s,
size_t *size,
const char * restrict format,
xtime_t xt,
const timezone_t * restrict tz);
The xtime_format function places bytes into the array pointed to by s as controlled by the string pointed to by format. The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format string consists of zero or more conversion specifiers and ordinary multibyte characters. A conversion specifier consists of a % character, possibly followed by an E or O modifier character (described below), possibly followed by a sequence of digits that indicate the requested minimum width for the conversion, followed by a character that determines the behavior of the conversion specifier. All ordinary multibyte characters (including the terminating null character) are copied unchanged into the array. If copying takes place between objects that overlap, the behavior is undefined. No more than *size characters are placed into the array.
All conversion specifiers supported by strftime are also supported by xtime_format, with the following extensions:
The behavior is undefined if xt is not a timestamp, or if tz is not the address of a valid time zone handle of the same clock type as the timestamp.
If the xtime_format function detects an error, it returns an error number. Otherwise it returns zero and updates *size to reflect the number of bytes needed to store the formatted string, including the terminating null. If *size's new value is no greater than its old one, the function also stores the formatted string into s; otherwise, the contents of s are indeterminate.
int xtime_errno(xtime_t xt);
This API aims to fix many shortcomings in the ISO C <time.h> API. It is inspired by Markus Kuhn's struct xtime proposal, and attempts to improve on it in the following ways:
Like the struct xtime API, this API does not require complete support for leap seconds. An implementation can choose to not support leap seconds at all by reporting an error whenever CLOCK_TAI is requested; it can also provide only limited leap second support, based on the previous or next leap second, by having xtime_conv fail when converting TAI timestamps outside a local window of known leap seconds.
Several suggestions have been made to rename the identifiers in the struct xtime proposal, or to remove some features; this proposal sticks to the names and features of the earlier proposal for now, to ease comparison between the two proposals, except that it renames strfxtime to xtime_format to avoid collisions with draft C9x. The suggestions can be acted on after the proposals have been more thoroughly worked out.
Civil timekeeping is based on Coordinated Universal Time (UTC), which is roughly equivalent to the older Greenwich Mean Time standard. Implementations with access to UTC should support CLOCK_REALTIME.
Some implementations do not have access to UTC; instead, they have only a local-time clock. Such clocks are problematic during UTC offset changes, which typically occur twice per year during daylight-saving time changes; they also have problems when locales change time zones. Implementations with access to such clocks should support CLOCK_LOCAL.
The best current-time standard is International Atomic Time (TAI), whose clocks tick accurately with standard (SI) seconds. Implementations with access to TAI (e.g. via a GPS satellite receiver) should support CLOCK_TAI; when coupled with a leap second table, this allows correct support of leap seconds within the window of the table.
Since 1972, UTC clocks have ticked in lockstep with TAI clocks, except for leap seconds, which are periodically inserted at the end of a UTC month to keep UTC synchronized with the earth's rotation. (Leap seconds might also need to be deleted if the earth speeds up, but this has never happened and is unlikely.) Since the earth is slowing down irregularly, leap seconds need to be inserted at irregular intervals, and cannot be predicted far in advance. The decision to insert leap seconds is made by the International Earth Rotation Service (IERS); e.g. see the Relationship between TAI and UTC. Leap seconds are simultaneously inserted into all local times derived from UTC.
UTC has been the basis of civil time since 1961, but between 1961 and 1972 it was synchronized to atomic time using a different method, which did not involve leap seconds. UTC's predecessor Universal Time (UT) was introduced in 1925, replacing GMT. For hosts requiring high precision historical timestamps, these time scales form the basis of CLOCK_REALTIME and CLOCK_LOCAL. However, for most practical purposes, these time scales are all equivalent to GMT, and hosts not requiring high precision timestamps need not make any distinction between them and GMT.
Similarly, TAI has been in force since 1970; its predecessors date back to July 1955. There is no standard relationship between atomic time and universal time before July 1955; for earlier timestamps, implementations can either estimate the relationship, or refuse to convert between the two forms.
Some time protocols (e.g. NTP) provide TAI with an unknown epoch, and report only a single leap second, near the time when the leap second actually occurs. Implementations can model this information by supplying TAI conversions only within the window of known leap seconds.
CLOCK_MONOTONIC should be derived from an internal, monotonically nondecreasing clock. It differs from CLOCK_TAI in two ways. First, CLOCK_MONOTONIC is monotonically nondecreasing (hence its name), whereas CLOCK_TAI can go backwards when the clock is reset. Second, CLOCK_MONOTONIC's seconds can start right in the middle of a TAI second, whereas CLOCK_TAI's seconds are supposed to be synchronous with true TAI seconds.
The new type xtime_t is intended to allow implementations to use a wider integer than the old time_t type without introducing backward compatibility problems. Typically, xtime_t will be a 64-bit integer to allow enough range and precision for most applications' needs. Historically, time_t is typically 32 bits and has only 1-s precision.
The new type timezone_t is an opaque type that can hold part of a buffer describing a time zone rule set. An implementation can make it equivalent to char, but making it a unique type will promote better type-checking.
The functions of this section typically return error indications inline, as part of an xtime_t value. This simplifies the interface, and allows errors to propagate nicely instead of being lost due to sloppy programming; it is reminiscent of the NaNs of IEEE floating-point arithmetic. One possible implementation is to let XTIME_MIN+n represent error number n.
The implementation is required to provide only its best-effort estimate for the value of any of the above clocks. Precise UTC and TAI representation and correct leap-second treatment will usually only be possible with a connection to an external reference clock that provides for instance data on the current UTC and TAI-UTC values plus a leap second announcement. On systems without such a connection, the implementation should provide the best available estimates for TAI and UTC. Implementations may offer additional non-standard clock types (for instance CLOCK_UT1 for the current UTC+DUT1 time, which is broadcasted by some time services).
If flag is CLOCK_MONOTONIC, successful calls to xtime_get return monotonically nondecreasing values. This is not true for the other clocks, which can be adjusted by means not described in this standard.
If two or more clock options are added together, the result is undefined.
An implementation with a built-in leap-second table should provide access to this table via xtime_conv in the form of supported conversion between the clock types CLOCK_REALTIME and CLOCK_TAI. Implementations can also offer the application to convert CLOCK_MONOTONIC values into CLOCK_TAI or CLOCK_REALTIME values as soon as these clocks have been adjusted using an external reference. This way, CLOCK_MONOTONIC values that were measured at a time when the implementation had not yet been able to determine UTC or TAI can later be converted as soon as contact with the reference clock is established.
Implementations should report an error when asked to perform conversions that require unknown information. For example, it is an error to convert between TAI and UTC timestamps far in the future, since exact conversion requires knowledge of future leap seconds, which cannot be predicted more than a few months ahead. In locales where the future UTC offset is unpredictable due to political volatility, implementations should also refuse to convert between far-future UTC and local time.
This proposal emerged out of discussions on the tz mailing list, and I am especially thankful for valuable suggestions from Markus Kuhn, who wrote the spec that this one is based on. Other commenters include D. J. Bernstein, Clive D.W. Feather, Antoine Leca, Joseph Myers, and Chris Newman.
Please send comments and suggestions for improvement to:
Paul Eggert
$Id: timeapi.html,v 1.3 2000/01/22 02:20:44 eggert Exp eggert $ -- http://www.twinsun.com/tz/timeapi.html