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 -- Markus G. Kuhn, Security Group, Computer Lab, Cambridge University, UK email: mkuhn at acm.org, home page: <http://www.cl.cam.ac.uk/~mgk25/> // // <xtime.h> // // Proposal for a modern time and calendar API for ISO C 9x - first sketch // // Markus Kuhn <mkuhn@acm.org> -- 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_MONOTONIC 16 #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. * * The status bits TIMEOPT_UTC, TIMEOPT_TAI, and TIMEOPT_LOCAL * are mutually exclusive. TIMEOPT_SYNC can only appear together with * TIMEOPT_UTC and TIMEOPT_TAI. * * The variable *options is used by the caller to select what type of * clock is requested, by specifying one of TIMEOPT_UTC, TIMEOPT_TAI, * TIMEOPT_MONOTONIC, TIMEOPT_PROCESS, or TIMEOPT_THREAD in the value * 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. * TIMEOPT_MONOTONIC, TIMEOPT_PROCESS, and TIMEOPT_THREAD are the only * 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, * TIMEOPT_MONOTONIC, TIMEOPT_PROCESS, and TIMEOPT_THREAD available. * 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. */