Code Capsules

Time and Date Processing in C

Chuck Allison


Chuck Allison is a software architect for the Family History Department of the Church of Jesus Christ of Latter Day Saints Church Headquarters in Salt Lake City. He has a B.S. and M.S. in mathematics, has been programming since 1975, and has been teaching and developing in C since 1984. His current interest is object-oriented technology and education. He is a member of X3J16, the ANSI C++ Standards Committee. Chuck can be reached on the Internet at allison@decus.org, or at (801)240-4510.

Most operating systems have some way of keeping track of the current date and time. ANSI C makes this information available in various formats through the library functions defined in time.h. The time function returns a value of type time_t (usually a long), which is an implementation-dependent encoding of the current date and time. You in turn pass this value to other functions which decode and format it.

The program in Listing 1 uses the functions time, localtime and strftime to print the current date and time in various formats. The localtime function breaks the encoded time down into

struct tm
{
   int tm_sec;     /* (0 - 61) */
   int tm_min;     /* (0 - 59) */
   int tm_hour;    /* (0 - 23) */
   int tm_mday;    /* (1 - 31) */
   int tm_mon;     /* (0 - 11) */
   int tm_year;    /* past 1900 */
   int tm_wday;    /* (0 - 6) */
   int tm_yday;    /* (0 - 365) */
   int tm_isdst;   /* daylight savings flag */
};
local time overwrites a static structure each time you call it, and returns its address (therefore only one such structure is available at a time in a program without making an explicit copy). The ctime function returns a pointer to a static string which contains the full time and date in a standard format (including a terminating newline). strftime formats a string according to user specifications (e.g., %A represents the name of the day of the week). See Table 1 for the complete list of format descriptors.

Time/Date Arithmetic

You can do time/date arithmetic by changing the values in a tm structure. The program in Listing 2 shows how to compute a date a given number of days in the future, as well as the elapsed execution time in seconds. Note the optional alternate syntax for the time function (the time_t parameter is passed by reference instead of returned as a value). The mktime function alters a tm structure so that the date and time values are within the proper ranges, after which the day-of-week (tm_wday) and day-of-year (tm_yday) fields are updated accordingly. mktime brings the date and time values in the tm structure into their proper ranges, and updates the day of week (tm-wday) and day of year (tm-yday) values accordingly. This occurs when a date falls outside the range that your implementation supports. My MS-DOS-based compiler, for example, cannot encode dates before January 1, 1970, but VAXC can process dates as early as the mid-1800s. The asctime function returns the standard string for the time represented in tm parameter (so ctime (&tval) is equivalent to asctime (localtime(&tval)). The function difftime returns the difference in seconds between two time_t encodings as a double.

If you need to process dates outside your system's range or calculate the interval between two dates in units other than seconds, you need to roll your own date encoding. The application in Listing 3, Listing 4, and Listing 5 shows a technique for determining the number of years, months and days between two dates, using a simple month-day-year structure. It subtracts one date from another, much as you might have done in elementary school (i.e., it subtracts the days first, borrowing from the month's place if necessary, and so on). Note that leap years are taken into account. For brevity, the date_interval function assumes that the dates are valid and that the first date entered precedes the second. Following the lead of the functions in time.h, It returns a pointer to a static Date structure which holds the answer.

File Time/Date Stamps

Most operating systems maintain a time/date stamp for files. At the very least, you can find out when a file was last modified. (The common make facility uses this information to determine if a file needs to be recompiled, or if an application needs to be relinked). Since file systems vary across platforms, there can be no universal function to retrieve a file's time/date stamp, so the ANSI standard doesn't define one. However, most popular operating systems (including MS-DOS and VAX/VMS) provide the UNIX function stat, which returns pertinent file information, including the time last modified expressed as a time_t. The program in Listing 6 uses stat and difftime to see if the file time1.c is newer than (i.e., was modified more recently than) time2.c.

If you need to update the time/date stamp of a file to the current time, simply overwrite the first byte of a file with itself. Although the contents haven't changed, your file system will think it has, and will update the time/date stamp accordingly. (Know your file system! Under VAX/VMS, you get a newer version of the file, while the older version is retained). This is sometimes called "touching" a file. The implementation of touch in Listing 7 creates the file if it doesn't already exist. Note that the file is opened in "binary" mode (indicated by the character b in the open mode string — I'll discuss file processing in detail in a future capsule).