--- created_at: '2014-10-04T18:58:24.000Z' title: Modernized time.h for ISO C (1998) url: http://www.cl.cam.ac.uk/~mgk25/time/c/ author: mr_tyzic points: 61 story_text: '' comment_text: num_comments: 18 story_id: story_title: story_url: parent_id: created_at_i: 1412449104 _tags: - story - author_mr_tyzic - story_8410220 objectID: '8410220' year: 1998 --- [Source](http://www.cl.cam.ac.uk/~mgk25/time/c/ "Permalink to Modernized <time.h> API for ISO C") # Modernized <time.h> API for ISO C # Modernized for ISO C [Markus Kuhn][1] _Warning: Section 1 attempts to mimic the style of a chapter of the ISO C standard and thus is a bit dry on first reading without background information. For more tutorial information, start with the [rationale in section 2][2] and the quoted references in section 3._ # 1 Specification ## 1.1 Components of time The header provides in addition to the facilities specified in ISO C 89 the following new features: The macros defined are ** TIME_UTC TIME_TAI TIME_MONOTONIC TIME_PROCESS TIME_THREAD TIME_LOCAL TIME_SYNC TIME_RESOLUTION ** all of which expand to an integral constant expression of type **int** and all of which should have disjoint bits set to one. The types declared are ** struct xtime ** which represents a point on some time scale or a duration in time and ** timezone_t ** which holds information related to a timezone. This information can for instance include data related to a geographical region regarding its historic or planned timezone and daylight-saving time changes or an algorithmic description of rules for such changes, all of which can be used to convert between a **struct xtime** numeric time and some local-time and calendar-date representation of this time. The **xtime** structure shall contain at least the following members: ** int_fast64_t sec; int_fast32_t nsec; ** The types are those defined in ****, but these type names and the other type names from **** are not included automatically by ****. **** The **xtime** structure describes a point in time relative to some reference point, which is referred to as the _epoch_. The member **sec** identifies the one second long time interval that starts **sec** seconds after the epoch. Negative **sec** values refer to second intervals before the epoch. The member **nsec** contains the number of completed nanoseconds from the start of the second interval identified by **sec** to the described point in time. The **nsec** value is always in the range 0 to 999_999_999, except during an inserted leap second, which extends the second interval identified by **sec** to two seconds, and where **nsec** is in the range 1_000_000_000 to 1_999_999_999 if the described point in time falls within an inserted leap second. **Note:** Coordinated Universal Time (UTC), the modern version of Greenwich Mean Time (GMT), is the international atomic-clock time scale on which almost all time services and official local-time definitions are based. Leap seconds are occasionally inserted into UTC as an additional second 23:59:60 at the end of a UTC month in order to keep UTC and UT1 (another timescale defined by the rotation of the earth) from drifting apart by more than 900 ms. Leap seconds are simultaneously inserted into all local times derived from UTC. UTC and leap seconds are defined in [ITU-R Recommendation TF.460-4][3]. The types **time_t**, **clock_t**, and all related functions and macros could be declared deprecated, but are still required in the form defined in ISO C 89/99 for backward compatibility with existing applications. ## 1.2 Time manipulation functions ### 1.2.1 The xtime_get function #### Synopsis ** int xtime_get(struct xtime *xtp, int clock_type); ** #### Description The **xtime_get** function determines the current time and writes it into ***xtp**. Several types of time are provided, and the value of **clock_type** selects among them. The following **clock_type** values are defined by this standard: **TIME_UTC** : The epoch for this clock is 1970-01-01 00:00:00 in Coordinated Universal Time (UTC). Since UTC was not defined in its current form before 1972, we define this epoch such that on precisely 1972-01-01 00:00:00 UTC the **xtp->sec** value jumps to 2*365*86400 and **xtp->nsec** jumps to 0. From then on, at the start of every normal UTC second, **xtp->sec** increases by one and **xtp->nsec** starts to count from 0 to 999_999_999 during the following second. UTC leap seconds are handled in a special way: At the start of an inserted leap second (23:59:60), **xtp->sec** does not increase and remains at the same value as for the previous second (23:59:59), while **xtp->nsec** continues to count from 1_000_000_000 to 1_999_999_999 during the leap second. After a deleted leap second (at the jump from 23:59:58.999... to 00:00:00), **xtp->sec** increases by two and **xtp->nsec** starts to count normally from 0. In other words, **xtp->sec** always contains the number of non-leap seconds since the start of 1970, as if the insertion or deletion of leap seconds in UTC never had happened. **TIME_TAI** : The epoch for this clock is 1970-01-01 00:00:00 in International Atomic Time (TAI), which means that on precisely 1972-01-01 00:00:00 UTC the **xtp->sec** value increases to 2*365*86400+10 and **xtp->nsec** jumps to 0. From then on, at the start of every TAI/UTC second, including UTC leap seconds, **xtp->sec** increases by one and **xtp->nsec** starts to count from 0 to 999_999_999 during the following second. In other words, **xtp->sec** always contains the number of completed SI seconds since the start of 1970 in TAI, that is it counts all inserted and does not count any deleted UTC leap seconds. **TIME_MONOTONIC** : The epoch for this clock is at some time before the first C program is started on this computer following a system reset. From then on, at the start of every further second, **xtp->sec** increases by one and **xtp->nsec** counts from 0 to 999_999_999 during the following second. The implementation shall make every effort to ensure that the time between a number of **xtp->sec** increments is as close as possible to the duration of the same number of SI seconds. This means that leap seconds should not affect this clock, nor should manual resets or adjustments of a system-wide UTC, TAI, or local-time clock affect it. The implementation is allowed to adjust the duration of the second of this clock to match the duration of an SI second more closely once it has learnt the frequency error of its local oscillator by comparing it with an external reference time signal. With this clock type, **xtp->sec** shall never be negative and **xtp->nsec** shall always be in the range 0 to 999_999_999. **TIME_PROCESS** : The epoch for this clock is at some time during the generation of the current process. From then on, **xtp->sec** and **xtp->nsec** provide a best effort indication of how many seconds plus how many nanoseconds all threads of the current process have been utilizing the processor so far. With this clock type, **xtp->sec** shall never be negative and **xtp->nsec** shall always be in the range 0 to 999_999_999. **TIME_THREAD** : This clock is like **TIME_PROCESS**, but processor utilization is determined per thread for the current thread of execution, and not per process. If any of the above **clock_type** values is combined with a bit-wise or operator with **TIME_RESOLUTION** before being passed to the function, then the value written into ***xtp** will be a time interval that describes the resolution of this clock and not the current value of this clock. **Note:** The implementation is only required to provide 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 estimate for UTC. The implementation of **TIME_TAI** is optional and the majority of systems is not expected to implement **TIME_TAI**, because TAI data is not part of many time signal services and stored [TAI-UTC tables][4] that allow to convert the widely available UTC signals into TAI would have to be updated roughly every six months with every new [IERS Bulletin C mailing][5]. Implementations are allowed to offer additional non-standard clock types (for instance **TIME_UT1** for the current UTC+DUT1 time, which is broadcast by some time services), and implementations are allowed to offer additional clock property macros to request more information about the clock than just the resolution. #### Returns On success, if the requested clock type was available, the function shall return a value **r** with **(r & clock_type) == clock_type**. If **xtp != NULL** then the value of the requested clock shall be stored in ***xtp**. If the requested clock type was not available or the requested clock type was not known, the function shall return a value **r** with **(r & clock_type) == 0** and ***xtp** will be undefined. If the requested clock type was available and was **TIME_UTC** or **TIME_TAI**, and if the system was recently in contact with an external reference clock and can assure with high certainty that the provided time is still correct within one second, then a value **r** with **(r & (clock_type | TIME_SYNC)) == (clock_type | TIME_SYNC)** should be returned. If the requested clock type was not available and the implementation knows only some local time in some unspecified time zone, then the function shall return a value **r** with **(r & (clock_type | TIME_LOCAL)) == TIME_LOCAL** and if **xtp != NULL** then this local-time value shall be written into ***xtp** in a way such that the epoch is 1970-01-01 00:00:00 in this local time, not counting during any inserted and counting for all deleted time intervals for timezone adjustments or leap seconds in this local time. ### 1.2.2 The xtime_delay function #### Synopsis ** void xtime_delay(long sec, long nsec); ** #### Description The function shall wait for at least **sec** \+ **nsec** / 109 seconds on the **TIME_MONOTONIC** clock. The function shall return immediately if this value is not positive. **Note:** Signal handlers can be called while this function is waiting, but it will not return prematurely due to the arrival of a signal. The POSIX.1 function **nanosleep** provides a similar service that is interruptible by signals. ## 1.3 Timezone manipulation functions A timezone in this context is the information necessary to convert between a **struct xtime** UTC value and a **struct tm** broken-down or string-formatted local time. This information can include not only a fixed time offset in minutes between UTC and the local time, but also algorithms that determine how this 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 timezone description that is provided as a text string into an in-memory representation of the timezone. Since timezone information can include tables of variable length, dynamic memory allocation and deallocation functions are provided. ### 1.3.1 The tz_prep function #### Synopsis ** int tz_prep(timezone_t **tz, const char * restrict tzstring); ** #### Description Allocate memory for and create the in-memory representation of the timezone specified in **tzstring**. This standard does not specify the syntax of the timezone description in **tzstring**. If **tzstring == NULL**, then some externally defined default timezone shall be used. #### Returns On success, the function shall set ***tz** to the address of the allocated **timezone_t** data structure and shall return 0. If there was a failure during memory allocation, the function shall set ***tz == NULL** and shall return -1. If there was a problem with interpreting **tzstring**, the function shall set ***tz** to the address of the allocated **timezone_t** data structure, shall then write into ****tz** further information about the cause of the problem for evaluation by **tz_error**, and shall return 1. **Implementation Advice:** The supported **tzstring** values can be full algorithmic descriptions of the timezone, for instance in the TZ format defined in ISO/IEC 9945-1:1996 (POSIX.1) section 8.1.1, such as **"CET-1CEST,M3.5.0/2,M10.5.0/3"** for Central Europe in 1999. They can also be names of geographic locations or timezones, which are then translated by a configuration database lookup into a detailed description. A possible convention is for example to name a region with common timezone rules after the most populated area in this region, such as **"Europe/Paris"**. If **tzstring == NULL** is specified, the default timezone can be determined, for instance, using environment variable **TZ** or, failing that, using a system-wide configuration file. ### 1.3.2 The tz_error function #### Synopsis ** char *tz_error(timezone_t *tz); ** #### Description After **tz_prep** has signalled an error by returning another value than 0, this function can be used to generate a readable error message about the cause of the problem by looking at the **timezone_t** value allocated by **tz_prep**. If **tz == NULL** or if no error has occurred, then this shall also be indicated by an appropriate message. The language used in the message should depend on the locale. The implementation is free to arbitrarily select between the locale that was active at the time of the call to **tz_prep** or that active when this function is called. #### Returns The function returns a pointer to a zero-terminated text string that contains a message. This text string is usually not accessible any more for the application after the next call to **tz_error** or **tz_free** with the same **tz** value. Calls to any of these functions with other **tz** values generated by **tz_prep** shall not affect this text string (multi-threading safety). **Implementation Advice:** There are several ways in which this function can be implemented in a multi-threading safe way. One is to let **tz_prep** generate the text message and store it somewhere in ****tz**, such that **tz_error** just has to return a pointer into this **timezone_t** value. Another is that **tz_error** determines the error cause by examining data found in the **timezone_t** value, and allocating space for the string on the heap. The pointer to this string would then not only be returned by **tz_error** but would also have to be stored in the **timezone_t** value such that **tz_free** can deallocate it again. A conforming portable application should not change the locale between calling **tz_prep** and **tz_error**. ### 1.3.3 The tz_free function #### Synopsis ** void tz_free(timezone_t *tz); ** #### Description This function deallocates the **timezone_t** data structure that was allocated before by a **tz_prep** call which returned with a non-negative value. The function shall perform no action if **tz == NULL**. ### 1.3.4 The tz_jump function #### Synopsis ** long tz_jump(timezone_t *tz, struct xtime *xtp, int forward); ** #### Description This function allows an application to find all non-continuities in a broken-down time representation defined by ***tz** about which the implementation is aware. Such non-continuities are all inserted or deleted time intervals that are not predictable by a continuously running 24h-clock and the normal Gregorian calendar. This includes leap seconds and daylight-saving time start and end, perhaps even calendar exceptions. If **tz == NULL**, then only UTC leap seconds shall be indicated. The value ***xtp** shall be interpreted on the **TIME_UTC** clock and defines the point in time where the search for the next non-continuity starts. A non-continuity on this start point shall be ignored in the search. After the function returns, ***xtp** shall contain the point in time where the non-continuity begins if one had been found. If **forward != 0** then the function shall search for the next non-continuity after ***xtp**. If **forward == 0**, then the first non-continuity before ***xtp** shall be searched. #### Returns A return value of zero indicates that the implementation is not aware of any non-continuity in the requested search direction and ***xtp** will remain unmodified. A positive return value **r** indicates that a time interval of **r** seconds is inserted into the time scale starting at the time ***xtp** in the timezone ***tz**. Unless the non-continuity is just a leap second, this means that the clocks in this timezone are turned back **r** seconds at ***xtp** and that the broken-down time representations during the **r** seconds that follow ***xtp** are a repetition of the **r** seconds that preceded ***xtp**. For example in the case of an inserted leap second, the function will return **1** at the point in time that corresponds to the start of the leap second (23:59:60 UTC) and **xtp->nsec == 1_000_000_000**. In the case of a switch from summer time back to winter time, the function will return **3600** if the difference is one hour, and ***xtp** will be set to the start of the repeated hour, that is to the start of winter time. A negative return value **r** indicates that a time interval of **r** seconds is deleted from the time scale at the time ***xtp** in the timezone ***tz**. This means that the clocks in this timezone are turned forward **r** seconds when the time ***xtp** is reached. For example in the case of a deleted leap second, the function will return **-1** and write into ***xtp** the point in time that corresponds to the start of the second after the deleted leap second (00:00:00 UTC). In the case of a switch from winter time to summer time, the function will return **-3600** if the difference is one hour, and ***xtp** will be set to the start of summer time. ## 1.4 Time conversion functions ### 1.4.1 The xtime_make function #### Synopsis ** int xtime_make(struct xtime *xtp, const struct tm *tmptr, const timezone_t *tz); ** #### Description This function interprets the broken-down time in ***tmptr** as a local time in the timezone specified in ***tz** and writes the corresponding value of the **TIME_UTC** clock type into ***xtp**. If **tz == NULL** then the ***tmptr** values are interpreted in UTC on the Gregorian calendar. Since **struct tm** provides only second resolution, the function sets **xtp->nsec = 0** or in case **tmptr->tm_sec == 60** then it sets **xtp->nsec = 1_000_000_000**. #### Returns On success the function shall write the requested time into ***xtp** if **xtp != NULL** and shall return 0. If the conversion is not possible because the values in ***tmptr** did not correspond to a valid broken-down time in the specified timezone, then the function shall return -1 and ***xtp** will be undefined. **Note:** [Example implementation][6] (incomplete) ### 1.4.2 The xtime_breakup function #### Synopsis ** int xtime_breakup(struct tm *tmptr, const struct xtime *xtp, const timezone_t *tz); ** #### Description This function interprets the value in ***xtp** as a **TIME_UTC** clock type value and writes the corresponding broken-down local time in the timezone specified in ***tz** into ***tmptr**. If **tz == NULL** then the written ***tmptr** values are in UTC. #### Returns On success the function shall write the requested time into ***tmptr** if **tmptr != NULL** and shall return 0. If the conversion is not possible then the function shall return -1 and ***tmptr** will be undefined. ### 1.4.3 The xtime_conv function #### Synopsis ** int xtime_conv( struct xtime *dst, int dst_clock_type, const struct xtime *src, int src_clock_type); ** #### Description This function converts **struct xtime** values between different clock types and gives this way the application access to the information that the implementation has available about the relationship between the various clock types. The value ***src** as it would have been returned by clock type **src_clock_type** is converted into the value that would at the same time have been returned by clock type **dst_clock_type** and stored in ***dst**. The implementation of the conversions between all clock types is optional. #### Returns On success the function shall write the result into ***dst** if ***dst != NULL** and shall return 0. If the conversion is not possible because the implementation lacks the necessary information, then the function shall return -1 and ***dst** will be undefined. **Implementation Advice:** Implementations which have 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 **TIME_UTC** and **TIME_TAI**. Implementations can also offer the application to convert **TIME_MONOTONIC** values into **TIME_TAI** or **TIME_UTC** values as soon as these clocks have been adjusted using an external reference. This way, **TIME_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. ### 1.4.4 The strfxtime function #### Synopsis ** size_t strfxtime(char * restrict s, size_t maxsize, const char * restrict format, const struct xtime *xtp, const timezone_t *tz); ** #### Description The **strfxtime** function places characters 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 **maxsize** characters are placed into the array. All conversion specifiers supported by **strftime** are also supported by **strfxtime**, with the following extensions and modifications: **%k** : If the current broken-down time appears multiple times in the specified timezone (for instance as the last hour of summer time and as the first hour of winter time), then this conversion specifier is substituted by **"A"** during the first appearance, by **"B"** during the second appearance, and so on. Otherwise it is replaced by no character, unless the conversion specifier was **%1k**, in which case it is replaced by a space. (This A/B convention is part of the official local-time notation in some countries (e.g., Germany).) **%l** : is replaced by the locale's abbreviated timezone name or no character if none is determinable. **%L** : is replaced by the locale's unabbreviated timezone name or no character if none is determinable. **%z** : is replaced by the offset from UTC in the ISO 8601 basic format "**-0430**" (meaning 4 hours 30 minutes behind UTC, west of Greenwich), or by no characters if no timezone offset is determinable. If the format specifier is **%:z**, then it is replaced by the ISO 8601 extended format "**-04:30**" with a colon separating the hour and minute field. If the offset to UTC is an integral number of hours, then the minute field and the colon are omitted. **%Z** : is identical to **%z**, except that the minute field is always present when the hour field is present, even if it is zero. The format specifiers **%H**, **%M**, and **%S** can also be used to generate fractional parts of hours, minutes, or seconds, as required for some ISO 8601 notations. Fractional output is obtained by following the **%** with either a full stop or a comma, followed by a decimal number indicating the requested number of fractional digits. The choice of either a full stop or a comma indicates whether a decimal dot or a decimal comma shall be used in the output. The output is always rounded downwards such that a shorter output is guaranteed to be the prefix of an output with a larger number of fractional digits. For instance if "**%H:%M:%.1S**" results in "**04:40:00.0**", then "**%,2H**" will result in "**04,66**". #### Returns If the total number of resulting characters including the terminating null character is not more than **maxsize**, the **strftime** function returns the number of characters placed into the array pointed to by **s** not including the terminating null character. Otherwise, zero is returned and the contents of the array are indeterminate. # 2 Rationale The ISO C 89 **** functions have a number of serious shortcomings: * **Lack of resolution.** The **time_t** type has traditionally been (and still is for backwards compatibility) a second counter since some epoch, providing only a resolution of one second. The standard theoretically allows a floating point type for **time_t**, but even a 64-bit FP type cannot represent the resolution of modern CPU bus-cycle counters. In addition, the absolute error of floating point types grows with distance from the epoch, which can be problematic in many applications. * **Lack of arithmetic support for the time_t type.** Since the encoding of **time_t** was not defined in the standard, it was not practical to even add or subtract a specified time interval in a portable way. * **Lack of a UTC equivalent of mktime.** It was not possible to convert a broken-down UTC time in a portable way into **time_t**. * **Lack of reentrant functions.** Some ISO C 89 time functions return pointers to static buffers and are therefore not efficiently implementable in a multi-threading safe way. POSIX.1c had to replace all these functions. * **Lack of control over the timezone.** The old API knows only about some unspecified local time and with restrictions it also supports UTC, which leaves something to be desired in an age of global networking. * **Lack of a reliable interval time scale.** The old API does not provide a separate clock for time-interval measurements that is not affected by clock adjustments (including leap seconds). It also did not provide access to TAI for those systems that had access to this form of time (e.g. via GPS or PTP). * **Lack of leap-second representation.** The ISO C 89 standard did not provide practical provisions for dealing with UTC leap seconds. On POSIX systems, **time_t** deliberately does not count inserted leap seconds, in the interest of simple and robust date arithmetic. Integer and floating point encodings of **time_t** do not provide a representation of leap seconds that would allow calculation of the correct length of time intervals without the use of a leap-second table that requires periodic updating. (On the other hand, this may not be a real problem. A good case can be made that leap seconds should not actually be represented as any special value for 23:59:60 in computer applications: while they could be displayed as such, what would their arithmetic properties be? A more robust way to deal with leap seconds would be a standardized way to stear around them, such as [UTC-SLS][7].) Considering the large number of problems, a clean-slate redesign of the time API seemed preferable to incremental improvements. Several previous attempts to improve C's time API have remained unsatisfactory: * The POSIX.1 standard (ISO/IEC 9945-1:1996) fixed the multi-threading problems. It defined additional versions of some functions, which write the result into a user-provided buffer instead of returning a pointer to a static buffer. It also tried to fix the problem of undefined arithmetic in **time_t**, by defining an integer encoding with a 1970 epoch for this type. However, this encoding lacks a value for leap seconds and allows 32-bit implementations that will overflow rather soon (2038-01-19 03:14:08). POSIX.1 also does not provide a reentrant interface for working in multiple timezones. The POSIX.2 (ISO/IEC 9945-2:1993) date function specifies a number of additional format specifiers that should also be in C's time formatting function, but these extensions do not yet cover all requirements of ISO 8601. * The new **** revision by Clive Feather, which was proposed in an earlier ISO C 9X draft, does not address multi-threading safety. It continues to use **time_t** as its primary time representation and therefore cannot introduce a mandatory encoding for backwards compatibility reasons (POSIX uses UTC seconds since 1970, Microsoft uses some float type, some people use TAI seconds, etc.). Support for **time_t** arithmetic is provided by extending the range of **mktime** such that it becomes for instance possible to add an hour to a **time_t** value by converting it into a **struct tm**, then adding 3600 to **tm_sec** or 1 to **tm_hour** and then converting back via **localtime**. The problem that **strftime** has to output values that are not part of **struct tm** is attacked by providing a **struct tmx** version of the broken-down time with a variable-length extension field. All these are rather complicated, non-straight-forward, and not very robust extensions. The portable **time_t** arithmetic via **mkxtime** is relatively inefficient and the semantic is dubious in the context of leap seconds. All these problem can easily be solved by simply giving up **time_t** and introducing a new time type with a carefully defined encoding. The definition of **struct xtime** used here is inspired by the POSIX.1 **struct timespec**, which again was inspired by the **struct timeval** interface of the BSD Unix **gettimeofday** system call, so there exists plenty of practical experience with this encoding. The improvements that **struct xtime** brings compared to its POSIX and BSD predecessors are: * Instead of leaving the type of the seconds counter implementation defined, we require it to be at least 64-bits long using the new ISO C 99 **** facilities. This prevents all the overflow problems that 32-bit POSIX implementations will face on January 19, 2038. A 64-bit second counter can represent a time interval of over 0.5 * 1012 years, which generously exceeds the lifetime of the solar system. * Nanoseconds are a reasonable lower bound for the resolution, because a nanosecond is shorter than the precision of the best available clocks, and relativistic limits will make it unlikely that subnanosecond clock precisions will become available in commonly accessible timescales. It has been argued that CPU bus cycles could eventually get as short as 0.1 ns (3 cm clock-signal wavelength) and that therefore higher precision timestamps might be desirable one day. However, this API is primarily designed to provide access to time references. For access to a high-resolution CPU-cycle counter – as required for benchmarking and code optimization – it is much more prudent to give applications direct access to the integer value of a hardware counter and also to an estimate of the current frequency of this counter. This can be done in a much simpler function, preferably one with constant and precisely documentable execution time. Such a function or macro, while no doubt useful, is outside the scope of this proposal. A function like xtime_get() has to perform much more complex algorithms than one would like to have in a simple cycle-counter access function for low-level profiling. * The ugly structure member prefix **tv_** that POSIX.1 uses was removed, because the original reason for them – some ancient pre-ISO-C compilers that put members of all structs into the same namespace – no longer applies, and the **tv_** prefix in the **struct timespec** was an unfortunate typo anyway (should have been **ts_**). * The idea of representing leap seconds as 32-bit nanosecond overflows is a very elegant solution to the leap second problem. This allows combining easy UTC arithmetic with a proper representation of leap seconds. The same functions that print and convert UTC timestamps with this encoding can also be used to handle TAI timestamps, as both UTC and TAI now have the same mapping between second-counter and broken-down representations of time. In addition, the correct encoding of UTC timestamps does not depend on the presence of a leap-second table. The POSIX.1 approach of not specifying the behaviour of time_t in the vicinity of a UTC leap second can lead to incompatible behaviour in network-synchronized distributed systems. Defining an encoding of a time scale that is completely based on TAI is also not feasible, because TAI is not widely publicized. End users usually expect UTC-based external representations of time, and forcing implementations to represent everything in a TAI-based time scale without leap seconds would cause in practice numerous wrong timestamps caused by out-of-date UTC-TAI tables in systems (as demonstrated by the experience with complaints from owners of early GPS receivers). This API does not specify whether the system's internal clock runs on UTC, TAI, or on a completely independent (monotonic) time base. It provides applications equally with access to all three types of time scales and fully supports implementations that are unaware of TAI. Implementations of this API are not required to store an up-to-date UTC-TAI table or to keep track of how the internal monotonic time is related to UTC and TAI, but if the information is available to the implementation, then the user can access it in a portable way via the **xtime_conv** function. This way, this API does not put extraordinary burdens on the implementor, but at the same time allows sophisticated implementations to offer their full UTC/TAI functionality to the user. The **TIME_MONOTONIC** clock type is offered for two reasons. The first one is that a reliable leap-second free time-scale is required for algorithms like "Wait 3000 ms before opening the valve", even on systems that do not have access to TAI, and access to TAI is also not required otherwise for such applications. The second reason is that many applications require a time-scale that is guaranteed to be available right from system start, but devices without a reliable battery-backed clock have to acquire UTC first after startup from an external source, and even devices with a battery clock can drift so far away that a crude adjustment is necessary, which would affect wait loops if UTC were the only timebase available. The problem that **struct tm** does not provide all information that an efficient and extensible time-string formatting function might need is solved elegantly by just defining **strfxtime** such that the **struct xtime** and timezone values are provided directly to this function, avoiding the inefficient and lossy detour via **mktime** that was unfortunately necessary in ISO C 89\. This approach avoids that we need significant extensions of **mktime** and clumsy mechanisms for future extensions in a second **struct tmx** as one of the ISO C 9X draft proposals suggested. None of the previous drafts provided a clean interface to handle timezone information as an object of its own right and in a multi-threading safe way. Timezone information can be a comprehensive data structure that needs a compilation step in order to be transformed into an efficient in-memory representation. The textual description format of a time zone can be complex, therefore an error message facility is necessary to help users in getting a correct configuration. An API with three functions similar to the POSIX.2 regular expression functions has been provided for this. The old **mktime**, **gmtime**, and **localtime** functions have been replaced by the two new functions **xtime_make** and **xtime_breakup**, which provide a simpler and yet much more functional interface. They accept a universal timezone specification as a parameter and are not bound to a single timezone. The old functions allowed breaking up a time only for local time and UTC, and a time could only be composed in local time and not in UTC. The new functions do not return pointers to internal static buffers like their original equivalents and are therefore multi-threading safe. They also allow the user to switch between arbitrarily many timezones and provide two-way conversion for all of these. The supported timezones can have very complex relationships with UTC, for instance it is no problem for an application to offer via this API the local time for some Mars robot. The role of the **xtime_make** and **xtime_breakup** functions in this new API is also much less important: While in ISO C 89 **mktime**, **gmtime**, and **localtime** were the only way of performing portable arithmetic on **time_t**, with the new API, arithmetic can be performed directly on **struct xtime**, since the encoding is fully defined there. These functions are also not relevant any more for printing a time since, in contrast to **strftime**, **strfxtime** operates directly on the second-counter time and the timezone descriptor. The list of conversion specifiers of **strftime** was much shorter than needed, and a number of new ones had to be added in order to support the POSIX.2 **date** functionality, all ISO 8601 date and time formats, the European Union summer time directive regulations for denoting the last hour of summer time and the first hour of winter time, and customary locale based abbreviated and full names of timezones. Most of the new conversion specifiers are already provided in ISO C 99, so we have to add only a few additional ones. # 3 Related information Readers not very familiar with concepts such as leap seconds, TAI, UTC, UT1, NTP, and modern PLL-based kernel clock implementations, are invited to have a look at the references listed on the author's [Computer time resouces][8] page. * Dan Bernstein's [libtai][9] is a library for handling all internal timestamps in an application in TAI. * [David Tribble][10] developed several other time API draft proposals for ISO C 200X: * [Additional Constraints on time_t][11] * [Long Time Type][12] * [Calendar Date Functions][13] * [Timezone Functions][14] * Paul Eggert also wrote an [alternative proposal][15], which is derived from this proposal but replaces struct xtime with an integer type. * [Jonathan Lennox][16] has also written a [proposal for thread-safe time zone information][17], in part inspired by this proposal, and implemented it as a [patch][18] to the [Olson TZ library][19]. * A proposal for handling leap seconds with fewer hazards for information systems is the [UTC with Smoothed Leap Seconds (UTC-SLS)][20]. * If you want to submit a formal comment on the draft ISO C standard to the [ISO/IEC JTC1/SC22/WG14][21] committee, then please have a look at the [NCITS Announcement of the Public Review Comment Period for ISO/IEC FCD 9899][22] for instructions. * If you are interested in the time APIs of other languages, the Ada95 Reference Manual (ISO/IEC 8652:1995) defines a common purpose package [Ada.Calendar in section 9.6][23], and a more sophisticated high-resolution, monotonic clock package [Ada.Real_Time in section D.8][24]. * There are also the CORBA [ Time Service][25] and [Enhanced View of Time Service][26] specifications by [OMG][27]. This proposal emerged out of discussions on the [tz mailing list][28], and I am especially thankful for valuable suggestions from [Paul Eggert][29], [Joseph Myers][30], [Antoine Leca ][31], [Clive Feather][32], [Chris Newman][33], [Nelson Beebe][34], and others. Suggestions and comments on this text are very welcome! created 1998-09-09 – last modified 2004-08-03 – http://www.cl.cam.ac.uk/~mgk25/c-time/ [1]: http://www.cl.cam.ac.uk/~mgk25/ [2]: http://www.cl.cam.ac.uk#rationale [3]: http://www.cl.cam.ac.uk/volatile/ITU-R-TF.460-4.pdf [4]: http://hpiers.obspm.fr/eop-pc/earthor/utc/TAI-UTC_tab.html [5]: http://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat [6]: http://www.cl.cam.ac.uk/xtime_make.c [7]: http://www.cl.cam.ac.uk/utc-sls/ [8]: http://www.cl.cam.ac.uk/~mgk25/time/ [9]: http://cr.yp.to/libtai.html [10]: http://david.tribble.com/ [11]: http://david.tribble.com/text/c0xtimet.htm [12]: http://david.tribble.com/text/c0xlongtime.html [13]: http://david.tribble.com/text/c0xcalendar.html [14]: http://david.tribble.com/text/c0xtimezone.html [15]: http://www.twinsun.com/tz/timeapi.html [16]: mailto:lennox%40cs.columbia.edu [17]: http://www.cl.cam.ac.uk/proposal-lennox.txt [18]: http://www.cl.cam.ac.uk/proposal-lennox.patch [19]: http://www.twinsun.com/tz/tz-link.htm [20]: http://www.cl.cam.ac.uk/~mgk25/time/utc-sls/ [21]: http://anubis.dkuug.dk/JTC1/SC22/WG14/ [22]: http://www.cl.cam.ac.uk/review-period.txt [23]: http://www.adapower.com/rm95/RM-9-6.html [24]: http://www.adapower.com/rm95/RM-D-8.html [25]: http://www.omg.org/technology/documents/formal/time_service.htm [26]: http://www.omg.org/technology/documents/formal/enhanced_time_service.htm [27]: http://www.omg.org/ [28]: http://www.cl.cam.ac.uk/iso-time.html#tz [29]: mailto:eggert%40twinsun.com [30]: mailto:jsm28%40cam.ac.uk [31]: mailto:Antoine.Leca%40renault.fr [32]: http://www.davros.org/c/ [33]: mailto:Chris.Newman%40innosoft.com [34]: http://www.math.utah.edu/~beebe/