Figure 10-1 shows the functions used to convert between time_t values and other time formats, including printable representations. These functions shield us from the complexity brought to such conversions by timezones, daylight saving time (DST) regimes, and localization issues. (We describe timezones in Timezones and locales in Section 10.4.)
The ctime() function provides a simple method of converting a time_t value into printable form.
#include <time.h>
char *ctime
(const time_t *timep);
Returns pointer to statically allocated string terminated by newline and \0
on success, or NULL
on error
Given a pointer to a time_t value in timep, ctime() returns a 26-byte string containing the date and time in a standard format, as illustrated by the following example:
Wed Jun 8 14:22:34 2011
The string includes a terminating newline character and a terminating null byte. The ctime() function automatically accounts for local timezone and DST settings when performing the conversion. (We explain how these settings are determined in Section 10.3.) The returned string is statically allocated; future calls to ctime() will overwrite it.
SUSv3 states that calls to any of the functions ctime(), gmtime(), localtime(), or asctime() may overwrite the statically allocated structure that is returned by any of the other functions. In other words, these functions may share single copies of the returned character array and tm structure, and this is done in some versions of glibc. If we need to maintain the returned information across multiple calls to these functions, we must save local copies.
A reentrant version of ctime() is provided in the form of ctime_r(). (We explain reentrancy in Reentrant and Async-Signal-Safe Functions.) This function permits the caller to specify an additional argument that is a pointer to a (caller-supplied) buffer that is used to return the time string. Other reentrant versions of functions mentioned in this chapter operate similarly.
The gmtime() and localtime() functions convert a time_t value into a so-called broken-down time. The broken-down time is placed in a statically allocated structure whose address is returned as the function result.
#include <time.h> struct tm *gmtime
(const time_t *timep); struct tm *localtime
(const time_t *timep);
Both return a pointer to a statically allocated broken-down time structure on success, or NULL
on error
The gmtime() function converts a calendar time into a broken-down time corresponding to UTC. (The letters gm derive from Greenwich Mean Time.) By contrast, localtime() takes into account timezone and DST settings to return a broken-down time corresponding to the system’s local time.
The tm structure returned by these functions contains the date and time fields broken into individual parts. This structure has the following form:
struct tm { int tm_sec; /* Seconds (0-60) */ int tm_min; /* Minutes (0-59) */ int tm_hour; /* Hours (0-23) */ int tm_mday; /* Day of the month (1-31) */ int tm_mon; /* Month (0-11) */ int tm_year; /* Year since 1900 */ int tm_wday; /* Day of the week (Sunday = 0)*/ int tm_yday; /* Day in the year (0-365; 1 Jan = 0)*/ int tm_isdst; /* Daylight saving time flag > 0: DST is in effect; = 0: DST is not effect; < 0: DST information not available */ };
The tm_sec field can be up to 60 (rather than 59) to account for the leap seconds that are occasionally applied to adjust human calendars to the astronomically exact (the so-called tropical) year.
If the _BSD_SOURCE
feature test macro is defined, then the glibc definition of the tm structure also includes two additional fields containing further information about the represented time. The first of these, long int tm_gmtoff, contains the number of seconds that the represented time falls east of UTC. The second field, const char *tm_zone, is the abbreviated timezone name (e.g., CEST for Central European Summer Time). SUSv3 doesn’t specify either of these fields, and they appear on only a few other UNIX implementations (mainly BSD derivatives).
The mktime() function translates a broken-down time, expressed as local time, into a time_t value, which is returned as the function result. The caller supplies the broken-down time in a tm structure pointed to by timeptr. During this translation, the tm_wday and tm_yday fields of the input tm structure are ignored.
#include <time.h>
time_t mktime
(struct tm *timeptr);
Returns seconds since the Epoch corresponding to timeptr on success, or (time_t) -1 on error
The mktime() function may modify the structure pointed to by timeptr. At a minimum, it ensures that the tm_wday and tm_yday fields are set to values that correspond appropriately to the values of the other input fields.
In addition, mktime() doesn’t require the other fields of the tm structure to be restricted to the ranges described earlier. For each field whose value is out of range, mktime() adjusts that field’s value so that it is in range and makes suitable adjustments to the other fields. All of these adjustments are performed before mktime() updates the tm_wday and tm_yday fields and calculates the returned time_t value.
For example, if the input tm_sec field were 123, then on return, the value of this field would be 3, and the value of the tm_min field would have 2 added to whatever value it previously had. (And if that addition caused tm_min to overflow, then the tm_min value would be adjusted and the tm_hour field would be incremented, and so on.) These adjustments even apply for negative field values. For example, specifying -1 for tm_sec means the 59th second of the previous minute. This feature is useful since it allows us to perform date and time arithmetic on a broken-down time value.
The timezone setting is used by mktime() when performing the translation. In addition, the DST setting may or may not be used, depending on the value of the input tm_isdst field:
If tm_isdst is 0, treat this time as standard time (i.e., ignore DST, even if it would be in effect at this time of year).
If tm_isdst is greater than 0, treat this time as DST (i.e., behave as though DST is in effect, even if it would not normally be so at this time of year).
If tm_isdst is less than 0, attempt to determine if DST would be in effect at this time of the year. This is typically the setting we want.
On completion (and regardless of the initial setting of tm_isdst), mktime() sets the tm_isdst field to a positive value if DST is in effect at the given date, or to 0 if DST is not in effect.
In this section, we describe functions that convert a broken-down time to printable form, and vice versa.
Given a pointer to a broken-down time structure in the argument tm, asctime() returns a pointer to a statically allocated string containing the time in the same form as ctime().
#include <time.h>
char *asctime
(const struct tm *timeptr);
Returns pointer to statically allocated string terminated by newline and \0
on success, or NULL
on error
By contrast with ctime(), local timezone settings have no effect on asctime(), since it is converting a broken-down time that is typically either already localized via localtime() or in UTC as returned by gmtime().
As with ctime(), we have no control over the format of the string produced by asctime().
Example 10-1 demonstrates the use of asctime(), as well as all of the time-conversion functions described so far in this chapter. This program retrieves the current calendar time, and then uses various time-conversion functions and displays their results. Here is an example of what we see when running this program in Munich, Germany, which (in winter) is on Central European Time, one hour ahead of UTC:
$date
Tue Dec 28 16:01:51 CET 2010 $./calendar_time
Seconds since the Epoch (1 Jan 1970): 1293548517 (about 40.991 years) gettimeofday() returned 1293548517 secs, 715616 microsecs Broken down by gmtime(): year=110 mon=11 mday=28 hour=15 min=1 sec=57 wday=2 yday=361 isdst=0 Broken down by localtime(): year=110 mon=11 mday=28 hour=16 min=1 sec=57 wday=2 yday=361 isdst=0 asctime() formats the gmtime() value as: Tue Dec 28 15:01:57 2010 ctime() formats the time() value as: Tue Dec 28 16:01:57 2010 mktime() of gmtime() value: 1293544917 secs mktime() of localtime() value: 1293548517 secs 3600 secs ahead of UTC
Example 10-1. Retrieving and converting calendar times
time/calendar_time.c
#include <locale.h> #include <time.h> #include <sys/time.h> #include "tlpi_hdr.h" #define SECONDS_IN_TROPICAL_YEAR (365.24219 * 24 * 60 * 60) int main(int argc, char *argv[]) { time_t t; struct tm *gmp, *locp; struct tm gm, loc; struct timeval tv; t = time(NULL); printf("Seconds since the Epoch (1 Jan 1970): %ld", (long) t); printf(" (about %6.3f years)\n", t / SECONDS_IN_TROPICAL_YEAR); if (gettimeofday(&tv, NULL) == -1) errExit("gettimeofday"); printf(" gettimeofday() returned %ld secs, %ld microsecs\n", (long) tv.tv_sec, (long) tv.tv_usec); gmp = gmtime(&t); if (gmp == NULL) errExit("gmtime"); gm = *gmp; /* Save local copy, since *gmp may be modified by asctime() or gmtime() */ printf("Broken down by gmtime():\n"); printf(" year=%d mon=%d mday=%d hour=%d min=%d sec=%d ", gm.tm_year, gm.tm_mon, gm.tm_mday, gm.tm_hour, gm.tm_min, gm.tm_sec); printf("wday=%d yday=%d isdst=%d\n", gm.tm_wday, gm.tm_yday, gm.tm_isdst); locp = localtime(&t); if (locp == NULL) errExit("localtime"); loc = *locp; /* Save local copy */ printf("Broken down by localtime():\n"); printf(" year=%d mon=%d mday=%d hour=%d min=%d sec=%d ", loc.tm_year, loc.tm_mon, loc.tm_mday, loc.tm_hour, loc.tm_min, loc.tm_sec); printf("wday=%d yday=%d isdst=%d\n\n", loc.tm_wday, loc.tm_yday, loc.tm_isdst); printf("asctime() formats the gmtime() value as: %s", asctime(&gm)); printf("ctime() formats the time() value as: %s", ctime(&t)); printf("mktime() of gmtime() value: %ld secs\n", (long) mktime(&gm)); printf("mktime() of localtime() value: %ld secs\n", (long) mktime(&loc)); exit(EXIT_SUCCESS); }time/calendar_time.c
The strftime() function provides us with more precise control when converting a broken-down time into printable form. Given a broken-down time pointed to by timeptr, strftime() places a corresponding null-terminated, date-plus-time string in the buffer pointed to by outstr.
#include <time.h>
size_t strftime
(char *outstr, size_t maxsize, const char *format,
const struct tm *timeptr);
Returns number of bytes placed in outstr (excluding terminating null byte) on success, or 0 on error
The string returned in outstr is formatted according to the specification in format. The maxsize argument specifies the maximum space available in outstr. Unlike ctime() and asctime(), strftime() doesn’t include a newline character at the end of the string (unless one is included in format).
On success, strftime() returns the number of bytes placed in outstr, excluding the terminating null byte. If the total length of the resulting string, including the terminating null byte, would exceed maxsize bytes, then strftime() returns 0 to indicate an error, and the contents of outstr are indeterminate.
The format argument to strftime() is a string akin to that given to printf(). Sequences beginning with a percent character (%
) are conversion specifications, which are replaced by various components of the date and time according to the specifier character following the percent character. A rich set of conversion specifiers is provided, a subset of which is listed in Table 10-1. (For a complete list, see the strftime(3) manual page.) Except as otherwise noted, all of these conversion specifiers are standardized in SUSv3.
The %U
and %W
specifiers both produce a week number in the year. The %U
week numbers are calculated such that the first week containing a Sunday is numbered 1, and the partial week preceding that is numbered 0. If Sunday happens to fall as the first day of the year, then there is no week 0, and the last day of the year falls in week 53. The %W
week numbers work in the same way, but with Monday rather than Sunday.
Often, we want to display the current time in various demonstration programs in this book. For this purpose we provide the function currTime(), which returns a string containing the current time as formatted by strftime() when given the argument format.
#include "curr_time.h"
char *currTime
(const char *format);
Returns pointer to statically allocated string, or NULL
on error
The currTime() function implementation is shown in Example 10-2.
Table 10-1. Selected conversion specifiers for strftime()
Specifier | Description | Example |
---|---|---|
| A |
|
| Abbreviated weekday name |
|
| Full weekday name |
|
| Abbreviated month name |
|
| Full month name |
|
| Date and time |
|
| Day of month (2 digits, 01 to 31) |
|
| American date (same as |
|
| Day of month (2 characters) |
|
| ISO date (same as |
|
| Hour (24-hour clock, 2 digits) |
|
| Hour (12-hour clock, 2 digits) |
|
| Day of year (3 digits, 001 to 366) |
|
| Decimal month (2 digits, 01 to 12) |
|
| Minute (2 digits) |
|
| AM/PM |
|
| am/pm (GNU extension) |
|
| 24-hour time (same as |
|
| Second (00 to 60) |
|
| Time (same as |
|
| Weekday number (1 to 7, Monday = 1) |
|
| Sunday week number (00 to 53) |
|
| Weekday number (0 to 6, Sunday = 0) |
|
| Monday week number (00 to 53) |
|
| Date (localized) |
|
| Time (localized) |
|
| 2-digit year |
|
| 4-digit year |
|
| Timezone name |
|
Example 10-2. A function that returns a string containing the current time
time/curr_time.c
#include <time.h> #include "curr_time.h" /* Declares function defined here */ #define BUF_SIZE 1000 /* Return a string containing the current time formatted according to the specification in 'format' (see strftime(3) for specifiers). If 'format' is NULL, we use "%c" as a specifier (which gives the date and time as for ctime(3), but without the trailing newline). Returns NULL on error. */ char * currTime(const char *format) { static char buf[BUF_SIZE]; /* Nonreentrant */ time_t t; size_t s; struct tm *tm; t = time(NULL); tm = localtime(&t); if (tm == NULL) return NULL; s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm); return (s == 0) ? NULL : buf; }time/curr_time.c
The strptime() function is the converse of strftime(). It converts a date-plus-time string to a broken-down time.
#define _XOPEN_SOURCE
#include <time.h>
char *strptime
(const char *str, const char *format, struct tm *timeptr);
Returns pointer to next unprocessed character in str on success, or NULL
on error
The strptime() function uses the specification given in format to parse the date-plus-time string given in str, and places the converted broken-down time in the structure pointed to by timeptr.
On success, strptime() returns a pointer to the next unprocessed character in str. (This is useful if the string contains further information to be processed by the calling program.) If the complete format string could not be matched, strptime() returns NULL
to indicate the error.
The format specification given to strptime() is akin to that given to scanf(3). It contains the following types of characters:
conversion specifications beginning with a percent character (%
);
white-space characters, which match zero or more white spaces in the input string; and
non-white-space characters (other than %
), which must match exactly the same characters in the input string.
The conversion specifications are similar to those given to strftime() (Table 10-1). The major difference is that the specifiers are more general. For example, both %a
and %A
can accept a weekday name in either full or abbreviated form, and %d
or %e
can be used to read a day of the month with or without a leading 0 in the case of single-digit days. In addition, case is ignored; for example, May and MAY are equivalent month names. The string %%
is used to match a percent character in the input string. The strptime(3) manual page provides more details.
The glibc implementation of strptime() doesn’t modify those fields of the tm structure that are not initialized by specifiers in format. This means that we can employ a series of strptime() calls to construct a single tm structure from information in multiple strings, such as a date string and a time string. While SUSv3 permits this behavior, it doesn’t require it, and so we can’t rely on it on other UNIX implementations. In a portable application, we must ensure that str and format contain input that will set all fields of the resulting tm structure, or make sure that the tm structure is suitably initialized before calling strptime(). In most cases, it would be sufficient to zero out the entire structure using memset(), but be aware that a value of 0 in the tm_mday field corresponds to the last day of the previous month in glibc and many other implementations of the time-conversion functions. Finally, note that strptime() never sets the value of the tm_isdst field of the tm structure.
The GNU C library also provides two other functions that serve a similar purpose to strptime(): getdate() (specified in SUSv3 and widely available) and its reentrant analog getdate_r() (not specified in SUSv3 and available on only a few other UNIX implementations). We don’t describe these functions here, because they employ an external file (identified by the environment variable DATEMSK
) to specify the format used for scanning the date, which makes them somewhat awkward to use and also creates security vulnerabilities in set-user-ID programs.
Example 10-3 demonstrates the use of strptime() and strftime(). This program takes a command-line argument containing a date and time, converts this to a broken-down time using strptime(), and then displays the result of performing the reverse conversion using strftime(). The program takes up to three arguments, of which the first two are required. The first argument is the string containing a date and time. The second argument is the format specification to be used by strptime() to parse the first argument. The optional third argument is the format string to be used by strftime() for the reverse conversion. If this argument is omitted, a default format string is used. (We describe the setlocale() function used in this program in Section 10.4.) The following shell session log shows some examples of the use of this program:
$ ./strtime "9:39:46pm 1 Feb 2011" "%I:%M:%S%p %d %b %Y"
calendar time (seconds since Epoch): 1296592786
strftime() yields: 21:39:46 Tuesday, 01 February 2011 CET
The following usage is similar, but this time we explicitly specify a format for strftime():
$ ./strtime "9:39:46pm 1 Feb 2011" "%I:%M:%S%p %d %b %Y" "%F %T"
calendar time (seconds since Epoch): 1296592786
strftime() yields: 2011-02-01 21:39:46
Example 10-3. Retrieving and converting calendar times
time/strtime.c
#define _XOPEN_SOURCE #include <time.h> #include <locale.h> #include "tlpi_hdr.h" #define SBUF_SIZE 1000 int main(int argc, char *argv[]) { struct tm tm; char sbuf[SBUF_SIZE]; char *ofmt; if (argc < 3 || strcmp(argv[1], "--help") == 0) usageErr("%s input-date-time in-format [out-format]\n", argv[0]); if (setlocale(LC_ALL, "") == NULL) errExit("setlocale"); /* Use locale settings in conversions */ memset(&tm, 0, sizeof(struct tm)); /* Initialize 'tm' */ if (strptime(argv[1], argv[2], &tm) == NULL) fatal("strptime"); tm.tm_isdst = -1; /* Not set by strptime(); tells mktime() to determine if DST is in effect */ printf("calendar time (seconds since Epoch): %ld\n", (long) mktime(&tm)); ofmt = (argc > 3) ? argv[3] : "%H:%M:%S %A, %d %B %Y %Z"; if (strftime(sbuf, SBUF_SIZE, ofmt, &tm) == 0) fatal("strftime returned 0"); printf("strftime() yields: %s\n", sbuf); exit(EXIT_SUCCESS); }time/strtime.c