libstddjb
skalibs
Software
www.skarnet.org
The following functions are declared in the tai.h header, and implemented in the libstddjb.a or libstddjb.so library.
tai is a set of data structures and primitives to represent and perform operations on time.
The point of tai is to handle time without ever having to deal with annoyances such as Y2K, Y2038, NTP limits, non-linear clocks, and the like. By using the tai interface, you ensure your program will behave properly no matter what.
The standard APIs for time management under Unix are broken in more or less subtle ways. The most obvious thing is that they do not pass year 2038. A less obvious problem is that they do not handle leap seconds correctly. Here are a few references you should read to understand what is going on:
The meat and potatoes of all this is that programmers cannot simply rely on standard Unix APIs such as gettimeofday() to measure time intervals or even to give precise absolute time, and in any case those APIs will become obsolete in 2038.
tai implements - among other things - the TAI64, TAI64N and TAI64NA formats, which are used in all of skalibs. This gives a programmer access to precise linear absolute time, which is suitable for both timestamping (wallclock usage) and time interval measurements (stopwatch usage). Additionally, TAI64 passes Y2038 (it can represent dates exceeding the estimated lifespan of the universe).
tai has been inspired by Dan J. Bernstein's libtai library, but does not borrow any code from it.
A struct tai structure holds an absolute date with a one-second precision. A struct taia structure holds an absolute date with a maximum of one-attosecond precision, as permitted by the underlying system call. If flag-usert is clear, the system clock will be read via gettimeofday() system call, which has a one-microsecond precision; if it is set, the system clock will be read via the clock_gettime() system call, which has a one-nanosecond precision. In either case, a current struct taia will be unable to be more precise than the underlying implementation.
A struct tai, as well as a struct taia, can also hold a (possibly negative) relative time, i.e. a difference of absolute dates. It is up to the programmer to make sure that a relative time is never interpreted as an absolute TAI64 date, and vice-versa.
skalibs provides a /package/prog/skalibs/etc/leapsecs.dat file, which is copied to /etc/leapsecs.dat at installation time. Make sure this file is always present and readable. This file contains the leap second table, which is needed for conversions between TAI and UTC. If you call a function that needs such a conversion (for instance, you call taia_sysclock() and your system clock is set to UTC) and the file cannot be read, the function call will fail.
The leap second table is read once in every process that needs it (the first time a TAI ↔ UTC conversion is made) and then is stored in memory. If the leapsecs.dat file changes, long-lived processes will need to be restarted to take the change into account.
int tai_now (struct tai *t)
Writes the current time as a TAI value to *t, with a
1-second precision. The current time is based on the system clock.
Make sure you have set or cleared the
flag-clockistai according
to your system clock synchronization method: skalibs supports a
system clock set to TAI-10 or to UTC.
The function returns 1 if it succeeds, or 0 (and sets errno) if
it fails.
int sysclock_get (struct taia *a)
Reads the current value of the system clock into *a, with
a 1-nanosecond (resp. 1-microsecond) if
flag-usert is set (resp. clear)
precision. Returns 1 if it succeeds or 0 (and sets errno) if it
fails. Note that despite being a struct taia, *a
does not contain a TAI value - it only contains
an internal, Y2038-safe representation of the value of the system
clock, which should be either TAI-10 or UTC. You should not use
this function directly unless you know exactly what you are doing.
int sysclock_set (struct taia const *a)
Sets the system clock to *a, provided *a has
the correct internal representation. You should not use this
function directly unless you know exactly what you are doing.
int taia_sysclock (struct taia *a)
Reads the current time into *a, as a TAI64NA value,
with a 1-nanosecond (resp. 1-microsecond) if
flag-usert is set (resp. clear)
precision. Returns 1 if it succeeds or 0 (and sets errno) if it
fails.
Here a contains a valid TAI stamp, no matter what the
system clock is set to: arithmetic operations can be performed
on it.
int taia_setnow (struct taia const *a)
Sets the current time to *a, with a 1-nanosecond
(resp. 1-microsecond) if
flag-usert is set (resp. clear)
precision. Returns 1 if it succeeds or 0 (and sets errno) if it
fails. a must contain a valid TAI stamp; proper
operations will be automatically run to convert that stamp into
the right format for the system clock.
The following 3 operations are only defined if your system provides the clock_gettime() primitive with the CLOCK_MONOTONIC option.
int taia_clockmon_init (struct taia *offset)
Initializes a stopwatch in *offset. The actual value of
*offset is meaningless to the user; offset's only
use is to be given as a second parameter to taia_clockmon().
The function returns 1 if it succeeds or 0 (and sets errno) if it fails.
What taia_clockmon_init() does is synchronize the "stopwatch clock" (CLOCK_MONOTONIC) to the system clock. Right after taia_clockmon_init() has been called, the absolute times given by taia_clockmon() and taia_sysclock() are similar. Then, depending on the accuracy of the system clock, a drift may appear; calling taia_clockmon_init() again resets that drift to zero.
int taia_clockmon (struct taia *a, struct taia const *offset)
Gives the absolute time, as a TAI64NA value, in *a. This
absolute time is computed as a linear increase (as measured with
CLOCK_MONOTONIC) since the last time taia_clockmon_init()
was called with parameter offset. taia_clockmon()
guarantees precise time interval measurements; however, the time it
gives can slightly differ from the result of taia_sysclock().
The function returns 1 if it succeeds or 0 (and sets errno) if it fails.
int taia_init (void)
If flag-usemon is set: this
function initializes a process-global stopwatch, that future
taia_now invocations will depend on.
If flag-usemon is clear: this
function does nothing.
The function returns 1 if it succeeds or 0 (and sets errno) if it fails.
int taia_now (struct taia *a)
Writes the current time, as a TAI value, to *a. This is the
function you should use to read time by default. It returns 1 if it succeeds or
0 (and sets errno) if it fails.
If flag-usemon is set: taia_now() is computed as a linear increase from the last time taia_init() was called. (If taia_init() has never been called before, the first invocation of taia_now() automatically calls taia_init().) If flag-usemon is clear: taia_now() is the same as taia_sysclock().
If all of the above is unclear to you: just use taia_now() everytime you need to read time, and you will always get a reasonable approximation of the current time, in a format suited for arithmetic computations.
int tai_from_timeval (struct tai *t, struct timeval const *tv)
int tai_from_timespec (struct tai *t, struct timespec const *ts)
int tai_relative_from_timeval (struct tai *t, struct timeval const *tv)
int tai_relative_from_timespec (struct tai *t, struct timespec const *ts)
Those functions convert an absolute (resp. relative) time in a
struct timeval (resp. struct timespec) to an absolute (resp. relative)
time in a struct tai, with a 1-second precision. They return 1.
int timeval_from_tai (struct timeval *tv, struct tai const *t)
int timespec_from_tai (struct timespec *ts, struct tai const *t)
int timeval_from_tai_relative (struct timeval *tv, struct tai const *t)
int timespec_from_tai_relative (struct timespec *ts, struct tai const *t)
Those functions do the opposite conversion. They normally return 1;
however, struct timeval and struct timespec cannot
represent an absolute date before the Epoch, or a negative relative time;
if *t cannot be converted, 0 EINVAL is returned.
int taia_from_timeval (struct taia *a, struct timeval const *tv)
int taia_from_timespec (struct taia *a, struct timespec const *ts)
int taia_relative_from_timeval (struct taia *a, struct timeval const *tv)
int taia_relative_from_timespec (struct taia *a, struct timespec const *ts)
int timeval_from_taia (struct timeval *tv, struct taia const *a)
int timespec_from_taia (struct timespec *ts, struct taia const *a)
int timeval_from_taia_relative (struct timeval *tv, struct taia const *a)
int timespec_from_taia_relative (struct timespec *ts, struct taia const *a)
Same conversion operations, but with a struct taia. The 1-microsecond
(for struct timeval) or 1-nanosecond (for struct timespec)
precision is preserved.
void taia_uint (struct taia *a, unsigned int c)
Stores a relative time of c seconds into a.
int taia_from_millisecs (struct taia *a, int ms)
This function makes a struct taia representing a relative
time of ms milliseconds. ms must be non-negative.
The function returns 1, unless ms is negative, in which case
it returns 0 EINVAL.
int taia_to_millisecs (struct taia const *a)
If *a contains a non-negative relative time that fits into
a 31-bit integer number of milliseconds, the function returns that
number. Else it returns -1 EINVAL.
void tai_add (struct tai *t, struct tai const *t1, struct tai const *t2)
Stores *t1 + *t2 into t. Of course, *t1
and *t2 must not both represent absolute times.
void tai_sub (struct tai *t, struct tai const *t1, struct tai const *t2)
Stores *t1 - *t2 into t. Of course, *t1
and *t2 must be of the same type (relative or absolute), and
*t will always be relative.
void taia_add (struct taia *a, struct taia const *a1, struct taia const *a2)
void taia_sub (struct taia *a, struct taia const *a1, struct taia const *a2)
Same thing with struct taia.
void taia_addsec (struct taia *a, struct taia const *a1, int c)
Adds c seconds to *a1 and stores the result into a.
c may be negative.
void taia_half (struct taia *a, struct taia const *b)
Stores *b/2 into a. *b must be relative.
int tai_less (struct tai const *t1, struct tai const *t2)
int taia_less (struct taia const *t1, struct taia const *t2)
Those functions return nonzero iff *t1 is lesser than *t2.
*t1 and *t2 must be both relative, or both absolute.
void tai_pack (char *s, struct tai const *t)
Marshalls *t into the buffer s points to, which
must be preallocated with at least TAI_PACK (8) characters. Afterwards,
the buffer contains the
external TAI64 format
representation of *t.
void tai_unpack (char const *s, struct tai *t)
Unmarshalls the
external TAI64 format
label pointed to by s (at least TAI_PACK characters) and stores
the result into t.
void tain_pack (char *s, struct taia const *a)
void tain_unpack (char const *s, struct taia *a)
void taia_pack (char *s, struct taia const *a)
void taia_unpack (char const *s, struct taia *a)
Same thing with
external TAI64N format,
using TAIN_PACK (12) characters, and
external TAI64NA format,
using TAIA_PACK (16) characters. Note that a struct taia is used in
both cases; there is no specific internal representation of TAI64N.
unsigned int tain_fmt (char *s, struct taia const *a)
Writes into s an ASCII representation of *a in external
TAI64N format. s must point to a preallocated buffer of at least
TAIN_PACK*2 (24) characters. The function returns the number of bytes that
have been written to s (24).
unsigned int tain_scan (char const *s, struct taia *a)
Reads 24 characters from s; if those characters are a valid ASCII
representation of the external TAI64N format of some time value, this value
is stored into a, and 24 is returned. Else 0 is returned.
unsigned int taia_fmt (char *s, struct taia const *a)
unsigned int taia_scan (char const *s, struct taia *a)
Same thing with 32 characters and the TAI64NA format.
A TAI64N timestamp is a string of 25 characters: a single '@' character followed by the ASCII representation of the TAI64N external format of an absolute date.
unsigned int timestamp_fmt (char *s, struct taia const *a)
Writes a TAI64N timestamp representing the absolute date *a
into the 25 characters pointed to by s. Returns 25.
unsigned int timestamp_scan (char const *s, struct taia *a)
Reads 25 characters at s. If those characters are a valid TAI64N
timestamp, stores the absolute date in a and returns 25. Else,
returns 0.
int timestamp (char *s)
Writes the current time (read from the system clock) as a TAI64N timestamp
into s. Returns 1 if it succeeds or 0 (and sets errno) if it fails.
TAI64N timestamps are an efficient, robust, and easy-to-use way of timestampping log lines. They're easy to recognize in automatic data parsers. Log files where every line starts with a TAI64N timestamp can be merged and alphanumerically sorted: the resulting file will be chronologically sorted.
The s6 package provides tools to convert TAI64N timestamps into human-readable dates. Please do not embed human-readable dates in your log files, thus making parsing tools unnecessarily hard to write; use TAI64N timestamps instead, design tools that can parse them, and translate them to human-readable form at human analysis time.