Modernized <time.h> for ISO C

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 and the
quoted references in section 3.

1 Specification

1.1 Components of time

The header <time.h> provides in addition to the
facilities specified in ISO C 89 the following new features:

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 <stdint.h>,
but these type names and the other type names from
<stdint.h> are not included automatically by
<time.h>.

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.

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

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.

1.4.2 The xtime_breakup function

Synopsis

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

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

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 <time.h> 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.)

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 <time.h> 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 <stdint.h> 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 page.

Dan Bernstein’s libtai
is a library for handling all internal timestamps in an application in
TAI.

David Tribble developed
several other time API draft proposals for ISO C 200X: