First proposal for new ISO C 9x time API

Markus Kuhn Markus.Kuhn at
Fri Sep 4 19:28:47 UTC 1998

I think, I have come up with a nice C API that fulfills all the stated
requirements. At the moment, it is just a first design sketch, not
something that could already be pasted into ISO C, but please have a
look at it and think about whether you would like this <xtime.h> to
become the successor of the quite broken <time.h>. The API below have
fewer functions and yet provides much more functionality than <time.h>.

I think that the approach below is more practical that Dan Bernstein's
completely TAI-based API. My API does not require any leapsecond history
to be stored in the system, something which I would never expect to be
updated reliably in practice.


Markus G. Kuhn, Security Group, Computer Lab, Cambridge University, UK
email: mkuhn at,  home page: <>

// <xtime.h>
// Proposal for a modern time and calendar API for ISO C 9x - first sketch
// Markus Kuhn <mkuhn at> -- 1998-09-04
// Note: The names of objects are not yet final and have likely to be
//       changed for backwards compatibility reasons.
//       The <xtime.h> API is added to the existing <time.h>. The old
//       <time.h> is not modified, maintained for backwards compatibility,
//       and declared deprecated. The old <time.h> can be implemented
//       completely on top of the the <xtime.h> functions.

#include <stdint.h>

// arithmetic time (replacing both time_t and clock_t, on POSIX systems
//                  often bit compatible with struct timespec)

struct xtime {
  int_fast64_t sec;        // seconds since the epoch
  int_fast32_t nsec;       // nanoseconds [0, 1_999_999_999]

// query the clock(s)

#define TIMEOPT_UTC          1
#define TIMEOPT_TIA          2
#define TIMEOPT_LOCAL        4
#define TIMEOPT_SYNC         8
#define TIMEOPT_PROCESS     32
#define TIMEOPT_THREAD      64

void get_xtime(struct xtime *time, int *options);

/* Function get_xtime(&time, &options) queries the clock and writes one
 * form of time into *time. The variable *options specifies which form of
 * time is requested and contains after the call some status bits that
 * indicate properties of the form of time that has been returned.
 * The possible properties are:
 * TIMEOPT_UTC       the epoch is 1970-01-01 00:00:00 UTC and time->sec
 *                   does not count inserted leap seconds and did count
 *                   removed leap seconds as if they hadn't been removed.
 *                   During a leap second (if the implementation is aware
 *                   of it), time->sec shows the value for the previous
 *                   second and time->nsec continues to increase in the
 *                   range 1e9 .. 2e9-1.
 * TIMEOPT_TAI       the epoch is 1970-01-01 00:00:00 TAI and time->sec
 *                   does count inserted leap seconds and does not count
 *                   removed leap seconds. time->nsec < 1_000_000_000.
 * TIMEOPT_LOCAL     the epoch is 1970-01-01 00:00:00 in the local time
 *                   zone. This is only a fallback option for systems
 *                   that are ignorant of UTC and know nothing but some
 *                   local time. TIMEOPT_LOCAL need not be supported on
 *                   systems that support TIMEOPT_UTC.
 * TIMEOPT_SYNC      Can appear only together with TIMEOPT_UTC and
 *                   TIMEOPT_TAI and indicates that the system has so
 *                   frequently been synchronized with an official time
 *                   source such that with high probability the deviation
 *                   from UTC resp. TAI is less than 1 s.
 * TIMEOPT_MONOTONIC The time comes from a clock with the following
 *                   property: If A and B are readings taken from this
 *                   clock (in seconds since the epoch) and TA and TB are
 *                   the corresponding TIMEOPT_TAI readings, then
 *                   1-1/2000 < (A-B)/(TA-TB) < 1+1/2000. In other words,
 *                   the frequency of a monotonic clock is never off
 *                   more than 500 ppm, a target that cheap clock oscillators
 *                   in office equipment can easily guarantee. This clock
 *                   cannot be reset while the process is running and
 *                   it will never show a leap second: time->nsec <
 *                   1_000_000_000. TIMEOPT_MONOTONIC is guaranteed to be
 *                   always available.
 * TIMEOPT_PROCESS   The epoch is the start of this process and the
 *                   measured time is the CPU time consumed by this process
 *                   so far. TIMEOPT_PROCESS is guaranteed to be always
 *                   available.
 * TIMEOPT_THREAD    The epoch is the start of this thread and the
 *                   measured time is the CPU time consumed by this thread
 *                   so far. TIMEOPT_PROCESS is guaranteed to be always
 *                   available. On systems without multithreading,
 *                   we always have TIMEOPT_THREAD == TIMEOPT_PROCESS.
 * are mutually exclusive. TIMEOPT_SYNC can only appear together with
 * The variable *options is used by the caller to select what type of
 * clock is requested, by specifying one of TIMEOPT_UTC, TIMEOPT_TAI,
 * provided to get_time(). The caller then has to check in the returned
 * options variable, whether the bit is still set to find out whether
 * the required time was known.
 * The TIMEOPT_MONOTONIC value can at boot time be equivalent with any
 * of the other clocks, but it can also have its epoch at boottime.
 * clocks guaranteed to be available on all implementations.
 * If a TIMEOPT_MONOTONIC value is given to mkxtm, the resulting
 * broken-down time might not be very meaningful. If TIMEOPT_LOCAL
 * is given to mkxtm, then zonespec should be TZ_UNDEFINED such that
 * the xtm value reflects that the precise epoch is not known.
 * Implementation example:
 * A typical desktop system will have only TIMEOPT_UTC,
 * After boot, we will have TIMEOPT_UTC == TIMEOPT_MONOTONIC until
 * someone adjusts the system clock. TIMEOPT_MONOTONIC will not be
 * affected by this adjustment, TIMEOPT_UTC will be a best effort
 * estimate of UTC. If the system has a leap seconds file that is less
 * than 6 months old or is connected to a GPS receiver, then
 * TIMEOPT_TAI might also be available. Note that systems have no need
 * to store the leap second history, because no function of this API
 * allows to convert between UTC and TAI.
 * TIMEOPT_LOCAL need not be available on systems that have
 * TIMEOPT_UTC. Local time should preferably be determined by
 * specifying TZ_DEFAULT or the respective local time zone to
 * mkxtm.
 * Systems without a battery clock will not have TIMEOPT_UTC available
 * after boot time, but TIMEOPT_MONOTONIC can already be used to make
 * time measurements and can also be used to backdate timestamps that
 * were made before UTC became known (e.g., by a network communication
 * or a radio clock lock-on).

// internal efficient representation of a time zone rule

typedef struct { ... } timezone_t;

// generate a timezone_t description from a string (e.g., POSIX.1 TZ format
// or Olsen database entry, or ...). Return *zone = NULL if there is
// some syntax error in the string.

void prepare_timezone(timezone_t **zone, char *zonespec);

// free the time zone descriptor zone and set *zone == TZ_UTC to remove
// dangling pointers

void free_timezone(timezone_t **zone);

// some predefined time zone descriptors

#define TZ_UTC       (timezone_t *)  0
#define TZ_UNDEFINED (timezone_t *) -1
#define TZ_DEFAULT   (timezone_t *) -2   // eg. from environment var TZ, etc.

/* When we use mkxtm() to split a xtime value up into a calendar date
 * and a hhmmss time (struct xtm), then we have to specify in which
 * time zone this time should be. If we are happy with UTC, we specify
 * TZ_UTC, and we will get offset==0. If the xtime value is not an
 * approximation of a UTC timestamp, then we specify TZ_UNDEFINED,
 * which indicates in struct tmx that offset is meaningless. If we
 * want to have whatever the system sees as its default or local time
 * zone, then we specify TZ_DEFAULT. We can select any other time zone
 * by parsing a time zone description with timezone() and specifying
 * the result. */

// brocken down date and time

struct xtm {
  long  nsec;   // nanoseconds after the second [0, 999_999_999]
  int   sec;    // seconds after the minute [0, 60]
  int   min;    // minutes after the hour [0, 59]
  int   hour;   // hours since midnight [0, 23]
  int   mday;   // day of the month [1, 31]
  int   mon;    // months since December [Jan=1, Dec=12]
  int   year;   // number of the year
  int   wday;   // days since Sunday [Mon=1, Sun=7]
  int   week;   // calendar week number (ISO 8601) [1, 53]
  int   wyear;  // year of the current week (ISO 8601)
  int   yday;   // days since December 31 [1, 366]
  int   offset; // minutes east off UTC
  int   repeat; // flag indicating first hour after DST
  timezone_t *zone;  // time zone descriptor

 * Note that in xtm, leap seconds are indicated by sec==60 and not by
 * and nsec overflow.

// display a brocken down time

size_t strfxtime(char * restrict s,
		 size_t maxsize,
		 const char * restrict format,
		 const struct xtm * restrict timeptr);

// construct a xtime value from a broken down date and time (return -1
// on error, 0 else)

int mkxtime(const struct xtm *src, struct xtime *dest);

// construct a brocken down date/time representation from an xtm value
// (return -1 on error, 0 else)

int mkxtm(const struct xtime *src, const timezone_t *zone, struct xtm *dest);

 * In time zones with summer time switches, the first hour of winter time
 * repeats the time values of the previous hour. To distinguish these values
 * the flag repeat is set to one. (May be, we can also keep the old DST
 * flag here instead)
 * In the tmx given to mkxtime, only the components nsec, sec, min,
 * hour, mday, mon, year, offset, repeat, and zone are examined. The
 * values of the other components are irrelevant. Values that are out
 * of range in nsec, sec, min, hour, mday, mon, year are adjusted
 * accordingly, for instance if sec is negative, then 60 is added to
 * sec while 1 is subtracted from min until the value is in the
 * correct interval before conversion takes place. If zone ==
 * TZ_UNDEFINED, then mkxtime behaves exactly as if TZ_UTC.

More information about the tz mailing list