P.J. Plauger is senior editor of The C Users Journal. He is secretary of the ANSI C standards committee, X3J11, and convenor of the ISO C standards committee, WG14. His latest books are The Standard C Library, published by Prentice-Hall, and ANSI and ISO Standard C (with Jim Brodie), published by Microsoft Press. You can reach him at pjp@plauger.com.
Background
Time and date calculations achieved a new level of sophistication under the UNIX operating system. Several of the developers of that system were amateur astronomers. They were sensitive to the need for representing times over a span of decades, not just years. They automatically reckoned time as Greenwich Mean Time (once GMT, now UTC), not just by the clock on the wall. They were, in short, more finicky than most about measuring and representing time on a computer.That same attention to detail has spilled over into the Standard C library. Its scope is basically whatever was available in C under UNIX that didn't depend on the peculiarities of UNIX. As a consequence, you can do a lot with times and dates in Standard C. The functions declared in <time. h> provide the relevant services.
It stretches the truth a bit to say that these functions don't depend on the peculiarities of UNIX. Not all operating systems distinguish between local time and UTC. Even fewer allow different users to display times relative to different time zones. Some of the smallest systems can't even give you the time of day. Yet all implementations of C must take a stab at telling time wisely if they want to claim conformance to the C Standard.
The C Standard contains enough weasel words to let nearly everybody off the hook. A system need only provide its "best approximation" to the current time and date, or to processor time consumed, to conform to the C Standard. A vendor could argue that 1 January 1980 is always the best available approximation to any time and date. A customer can rightly quarrel about the low quality of such an approximation, but not whether it satisfies the C Standard.
What this means in practice is that a program should never take times too seriously. It can enquire about the current time (by calling time) and display what it gets in a variety of attractive formats. But it can't know for sure that the time and date are meaningful. If you have an application that depends critically upon accurate time stamps, check each implementation of Standard C closely.
There are too many functions declared in <time.h> to cover in one installment. So I've divided the presentation into three parts:
The topic for this month is the basic functions.
- the basic time functions those that yield results of type clock_t, time_t, or double
- the conversion functions those that convert times between scalar and structured forms
- the formatting functions those that yield text representations of encoded times
What the C Standard Says
7.12 Date and time <time.h>
7.12.1 Components of time
The header <time.h> defines two macros, and declares four types and severalufunctions for manipulating time. Many functions deal with a calendar time that represents the current date (according to the Gregorian calendar) and time. Some functions deal with local time, which is the calendar time expressed for some specific time zone, and with Daylight Saving Time, which is a temporary change in the algorithm for determining local time. The local time zone and Daylight Saving Time are implementation-defined.The macros defined are NULL (described in 7.1.6); and
CLOCKS_PER_SECwhich is the number per second of the value returned by the clock function.The types declared are size_t (described in 7.1.6);
clock tand
time_twhich are arithmetic types capable of representing times; and
struct tmwhich holds the components of a calendar time, called the broken-down time. The structure shall contain at least the following members, in any order. The semantics of the members and their normal ranges are expressed in the comments.137
int tm_sec; /* seconds after the minute- [0, 61] */ int tm_min; /* minutes after the hour- [0, 59] */ int tm_hour; /* hours since midnight- [0, 23] */ int tm_mday; /* day of the month- [1, 31] */ int tm_mon; /* months since January- [0, 11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday- [0, 6] */ int tm_yday; /* days since January 1- [0, 365] */ int tm_isdst; /* Daylight Saving Time flag */The value of tm_isdst is positive if Daylight Saving Time is in effect, zero if Daylight Saving Time is not in effect, and negative if the information is not available.
7.12.2 Time manipulation functions
7.12.2.1 The clock function
Synopsis
#include <time.h> clock_t clock(void);Description
The clock function determines the processor time used.
Returns
The clock function returns the implementation's best approximation to the processor time used by the program since the beginning of an implementation-defined era related only to the program invocation. To determine the time in seconds, the value returned by the clock function should be divided by the value of the macro CLOCKS_PER_SEC. If the processor time used is not available or its value cannot be represented, the function returns the value (clock_t) -1.138
7.12.2.2 The difftime function
Synopsis
#include <time.h> double difftime(time_t time1, time_t time0);Description
The difftime function computes the difference between two calendar times: time1 - time0.
Returns
The difftime function returns the difference expressed in seconds as a double......
7.12.2.4 The time function
Synopsis
#include <time.h> time_t time(time_t *timer);Description
The time function determines the current calendar time. The encoding of the value is unspecified.
Returns
The time function returns the implementation's best approximation to the current calendar time. The value (time_t)-1 is returned if the calendar time is not available. If timer is not a null pointer, the return value is also assigned to the object it points to.Footnotes
137. The range [0, 61] for tm_see allows for as many as two leap seconds.
138. In order to measure the time spent in a program, the clock function should be called at the start of the program and its return value subtracted from the value returned by subsequent calls.
Using the Basic Time Functions
The functions declared in <time.h> determine elapsed processor time and calendar time. They also convert among different data representations. You can represent a time as:
You have a rich assortment of choices. The hard part is often identifying just which data represention, and which functions, you want to use for a particular application. For this installment, I ignore functions that produce a struct tm or a text string.
- type clock_t for elapsed processor time, as returned by the primitive function clock
- type time_t for calendar time, as returned by the primitive function time or the function mktime
- type double for calendar time in seconds, as returned by the function difftime
- type struct tm for calendar time broken down into separate components, as returned by the functions gmtime and localtime
- a text string for calendar time, as returned by the functions asctime, ctime, and strftime
Here is a brief description of the individual types and macros defined in <time.h>. It is followed by brief notes on how to use the basic time functions declared in <time.h>.
NULL See "The Header <stddef. h>," CUJ December 1991.
CLOCKS_PER_SEC The expression clock() / CLOCKS_PER_SEC measures elapsed processor time in seconds. The macro can have any arithmetic type, either integer or floating point. Type cast it to double to ensure that you can represent fractions of a second as well as a wide range of values.
clock_t This is the arithmetic type returned by clock, described below. It represents elapsed processor time. It can have any integer or floating-point type, which need not be the same type as the macro CLOCKS_PER_SECOND, above.
size_t See "The Header <stddef.h >," CUJ December 1991.
time_t This is the arithmetic type returned by time, described below. Several other functions declared in <time.h> also manipulate values of this type. It represents calendar times that span years, presumably to the nearest second (although not necessarily). Don't attempt to perform arithmetic on a value of this type.
tm A structure of type struct tm represents a "broken-down time." Several functions declared in <time.h> manipulate values of this type. You can access certain members of struct tm. Its definition looks something like:
struct tm { int tm_sec; /* seconds after the minute (from 0) */ int tm_min; /* minutes after the hour (from 0) */ int tm_hour; /* hour of the day (from 0) */ int tm_mday; /* day of the month (from 1) */ int tm_mon; /* month of the year (from 0) */ int tm_year; /* years since 1900 (from 0) */ int tm_wday; /* days since Sunday (from 0) */ int tm_yday; /* day of the year (from 0) */ int tm_isdst; /* DST flag */The members may occur in a different order, and other members may also be present. The DST flag is greater than zero if Daylight Savings Time (DST) is in effect, zero if it is not in effect, and less than zero if its state is unknown. The unknown state encourages the functions that read this structure to determine for themselves whether DST is in effect.clock This function measures elapsed processor time instead of calendar time. It returns -1 if that is not possible. Otherwise, each call should return a value equal to or greater than an earlier call during the same program execution. It is the best measure you can get of the time your program actually consumes. See the macro CLOCKS_PER_SEC, above.
difftime The only safe way compute the difference between two times t1 and t0 is by calling difftime(t1, t0). The result, measured in seconds, is positive if t1 is a later time than t0.
time This function determines the current calendar time. It returns -1 if that is not possible. Otherwise, each call should return a value at the same time or later than an earlier call during the same program execution. It is the best estimate you can get of the current time and date.
Implementing the Basic Time Functions
The functions declared in <time.h> are quite diverse. Many wrestle with the bizarre irregularities involved in measuring and expressing times and dates. Be prepared for an assortment of coding techniques (at least in future installments).Listing 1 shows the file time.h. As usual, it inherits from the internal header <yvals.h> definitions that are repeated in several standard headers. I discuss the implementation of both the macro NULL and the type definition size_t in "The Header <stddef. h>," CUJ December 1991.
<yvals.h> also defines two macros that describe properties of the primitive functions clock and time:
The values of these macros depend strongly on how you implement clock and time. This implementation represents elapsed processor time as an unsigned int (type clock_t). It represents calendar time as an unsigned long (type time_t) that counts UTC seconds since the start of 1 January 1900. That represents dates from 1900 until at least 2036. You have to adjust whatever the system supplies to match these conventions.
- The macro _CPS specifies the value of the macro CLOCKS_PER_SECOND.
- The macro_TBIAS gives the difference, in seconds, between values returned by time and the time measured from 1 January 1900. (This macro name does not appear in <time.h>.)
The macro _TBIAS is a kludge. Normally, you want to set it to zero. The version of time you supply should deliver calendar times with the appropriate starting point. UNIX, however, measures time in seconds since 1 January 1970. Many implementations of C offer a function time that matches this convention. If you find it convenient to use such a time function directly, then <yvals.h> should contain the definition:
#define _TBIAS ((70 * 365LU + 17) * 86400That counts the 70 years, including 17 leap days, that elapsed between the two starting points. In several places, the functions declared in <time.h> adjust a value of type time_t by adding or subtracting _TBIAS.Listing 2 shows the file time.c. It defines the function time for a UNIX system. As usual, I assume the existence of a C-callable function with a reserved name that peforms the UNIX system service. For this version of time, the header <yvals.h> can define the macro _TBIAS to be zero.
UNIX also provides an exact replacement for the function clock. So do many implementations of C modeled after UNIX. Thus, you may not have to do any additional work. Just define the macro _CPS appropriately. For a PC-compatible computer, for example, the value is approximately 18.2.
Listing 3 shows the file clock.c. It defines a version of clock you can use if the operating system doesn't provide a separate measure of elapsed processor time. The function simply returns a truncated version of the calendar time. In this case, the header <yvals.h> defines the macro _CPS to be 1.
Listing 4 shows the file difftime.c. It is careful to correct the biases of both times before comparing them. It is also careful to develop a signed difference between two unsigned integer quantities. Note how the function negates the difference t1 - t0 only after converting it to double.
The remaining functions all include the internal header "xtime.h". Listing 5 shows the file xtime.h. It includes the standard header <time.h> and the internal header "xtinfo.h". That internal header defines the type _Tinfo. It also declares the data object _Times, defined in the file asctime.c. _Times specifies locale-specific information on the category LC_TIME. Listing 6 shows the file xtinfo.h.
The header "xtime.h" defines the macro WDAY that specifies the weekday for 1 January 1900 (Monday). It defines the type Dstrule that specifies the components of an encoded rule for determining Daylight Savings Time. And it declares the various internal functions that implement this version of <time.h>.
Conclusion
The basic time functions I have shown so far meet a number of needs. You can measure execution times, obtain time and date stamps, and compare those stamps. That's more time support than many programming languages offer. For Standard C, however, it's only the beginning.
References
W.M. O'Neil. 1975. Time and the Calendars. Sydney, N.S.W.: Sydney University Press. Calendars are notoriously idiosyncratic. This book tells you more than you probably want to know about the history of measuring calendar time. It also explains why days and dates are named and determined the way they are today.This article is excerpted in part from P.J. Plauger, The Standard C Library, (Englewood Cliffs, N.J.: Prentice-Hall, 1992).