Let’s take the occasion of the change back from DST here in Europe, not in the US, yet, to look how times are handled in C.
The C standard proposes a large variety of types for representing times:
double and textual representations as
char. It is a bit complicated to find out what the proper type for a particular purpose is, so let me try to explain this.
The first class of “times” can be classified as calendar times, times with a granularity and range as it would typically appear in a human calendar, as for appointments, birthdays and so on. Some of the functions that manipulate these in C99 are a bit dangerous, they operate on global state. Let us have a look how these interact:
char ^ o strftime| \_ ctime _ |time asctime| | | | mktime | v difftime struct tm --------> time_t ----------> double ^ | | localtime | |-------------| gmtime
struct tm structures a calendar time mainly as you would expect. It has hierarchical date fields such as
tm_year for the year,
tm_mon for the month and so on, down to the granularity of a second. They have one pitfall, though, how the different fields are counted. All but one start with
tm_mon set to
0 stands for January and
0 stands for Sunday.
Unfortunately, there are exceptions:
tm_mdaystarts counting days in the month at 1.
tm_yearmust add 1900 to get the year in the Gregorian calendar. Years represent in that way should lie between Gregorian years 0 and 9999.
tm_secis in the range from
60, including. The later is for the rare occasion of leap seconds.
There are three supplemental date fields that are used to supply additional information to a time value in a
tm_wdayfor the week day,
tm_ydayfor the day in the year, and
tm_isdsta flag that informs if a date is considered being in DST of the local time zone or not.
The consistency of all these fields can be enforced with the function
mktime. It can be seen to operate in three steps
- The hierarchical date fields are normalized to their respective ranges.
tm_ydayare set to the corresponding values.
tm_isdayhas a negative value, this value is modified to 1 if the date falls into DST for the local platform, and to 0 otherwise.
mktime also serves an extra purpose. It returns the time as a
time_t is thought to represent the same calendar times as
struct tm, but is defined to be an arithmetic type, more suited to compute with them. It operates on a linear time scale. A
time_t value of
0. the beginning of
time_t is called epoch in the C jargon. Often this corresponds to the beginning of Jan 1, 1970,
The granularity of
time_t is usually to the second, but nothing guarantees that. Sometimes processor hardware has special registers for clocks that obey a different granularity.
difftime translates the difference between two
time_t values into seconds that are represented as a
Two functions for the inverse operation from
struct tm come into view:
localtimestores the broken down local time
gmtimestores the broken time, expressed as UTC.
As indicated, they differ in the time zone that they assume for the conversion. Under normal circumstances
mktime should be inverse to each other,
gmtime has no direct counterpart for the inverse direction.
Textual representations of calendar times are also available.
asctime stores the date in a fixed format, independent of any locale, language (it uses English abbreviations 🙂 or platform dependency. The format is a string of the form
"Www Mmm DD HH:MM:SS YYYY\n"
strftime is more flexible and allows to compose a textual representation with format specifiers similar to the
One additional warning is in order.
asctime use internal state and return pointers to static data. They are not thread safe, not reentrant, and calling one of them erases the return value of any previous call to one of them, be careful.
The new optional annexe K of C11, has functions that check for validity of their arguments and are reentrant:
asctime_s. Use them when you may. P99 now implements them in its C11 emulation layer, for those of you that don’t have a complying C library, yet. Give it some testing, if you like.