Home > Articles

General Library Interfaces-Part 1

This chapter is from the book

We saw in Chapter 5, “Directories and File Metadata,” page 111, that directly reading a directory returns file names in the order in which they’re kept in the directory. We also saw that the struct stat contains all the information about a file, except its name. However, some components of that structure are not directly usable; they’re just numeric values.

This chapter presents the rest of the APIs needed to make full use of the struct stat component values. In order, we cover the following topics: time_t values for representing times and the time formatting functions; sorting and searching functions (for sorting file names, or any other data); the uid_t and gid_t types for representing users and groups and the functions that map them to and from the corresponding user and group names; and finally, a function to test whether a file descriptor represents a terminal.

POSIX systems have many more general-purpose APIs than just the ones in this chapter. We cover more of them later, in Chapter 13, “General Library Interfaces—Part 2,” page 419, and in Chapter 16, “Extended Interfaces,” page 557.

6.1 Times and Dates

Time values are kept in the type known as time_t. The ISO C standard guarantees that this is a numeric type but does not otherwise specify what it is (integer or floating-point), or the range or the precision of the values stored therein.

On GNU/Linux and Unix systems, time_t values represent “seconds since the Epoch.” The Epoch is the beginning of recorded time, which is midnight, January 1, 1970, UTC. On most systems, a time_t is a C long int. For 32-bit systems, this means that the time_t “overflows” sometime on January 19, 2038. By then, we hope, everyone will be using 64-bit systems (or bigger!), where the time_t type is at least 64 bits big.

Various functions exist to retrieve the current time, compute the difference between two time_t values, convert time_t values into a more usable representation, and format both representations as character strings. Additionally, a date and time representation can be converted back into a time_t, and limited time-zone information is available.

A separate set of functions provides access to the current time with a higher resolution than one second. The functions work by providing two discrete values: the time as seconds since the Epoch, and the number of microseconds or nanoseconds within the current second. These functions are described later in the book, in Section 16.3.1 “Microsecond Times: gettimeofday(),” page 567, and in Section 16.3.2, “Nanosecond Times: clock_gettime(),” page 569.

The data structures were introduced briefly in Section 5.6.4, “Changing Timestamps: utime() and Successors,” page 148.

6.1.1 Retrieving the Current Time: time() and difftime()

The time() system call retrieves the current date and time; difftime() computes the difference between two time_t values:

#include <time.h>                                     ISO C

time_t time(time_t *t);
double difftime(time_t time1, time_t time0);

time() returns the current time. If the t parameter is not NULL, then the value pointed to by t is also filled in with the current time. It returns (time_t) –1 if there was an error, and errno is set.

Although ISO C doesn’t specify what’s in a time_t value, POSIX does indicate that it represents time in seconds. Thus, it’s both common and portable to make this assumption. For example, to see if a time value represents something that is six months or more in the past, one might use code like this:

/* Error checking omitted for brevity */
time_t now, then, some_time;

time(& now);                                       Get current time
then = now - (6L * 31 * 24 * 60 * 60);             Approximately six months ago

… set some_time, for example, via stat() …
if (some_time < then)
   /* more than 6 months in the past */
else
   /* less than 6 months in the past */

However, since strictly portable code may need to run on non-POSIX systems, the difftime() function exists to produce the difference between two times. The same test, using difftime(), would be written this way:

time_t now, some_time;
const double six_months = 6.0 * 31 * 24 * 60 * 60;

time(& now);                                       Get current time
… set some_time, for example, via stat() …

if (difftime(now, some_time) >= six_months)
   /* more than 6 months in the past */
else
   /* less than 6 months in the past */

The return type of difftime() is a double because a time_t could possibly represent fractions of a second as well. On POSIX systems, it always represents whole seconds.

In each of the preceding examples, note the use of a typed constant to force the computation to be done with the right type of math: 6L in the first instance for long integers; 6.0 in the second, for floating point.

6.1.2 Breaking Down Times: gmtime() and localtime()

In practice, the “seconds since the Epoch” form of a date and time isn’t very useful except for simple comparisons. Computing the components of a time yourself, such as the month, day, year, and so on, is error prone, since the local time zone (possibly with daylight saving time) must be taken into account, leap years must be computed correctly, and so forth. Fortunately, two standard routines do this job for you:

#include <time.h>                                      ISO C

struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);

gmtime() returns a pointer to a struct tm that represents UTC time. localtime() returns a pointer to a struct tm representing the local time; that is, it takes the current time zone and daylight saving time into account. In effect, this is “wall-clock time,” the date and time as it would be displayed on a wall clock or on a wristwatch. (How this works is discussed later; see Section 6.1.6, “Getting Time-Zone Information,” page 168.)

Both functions return a pointer to a struct tm, which looks like this:

struct tm {
    int     tm_sec;         /* seconds */
    int     tm_min;         /* minutes */
    int     tm_hour;        /* hours */
    int     tm_mday;        /* day of the month */
    int     tm_mon;         /* month */
    int     tm_year;        /* year */
    int     tm_wday;        /* day of the week */
    int     tm_yday;        /* day in the year */
    int     tm_isdst;       /* daylight saving time */
};

The struct tm is referred to as a broken-down time, since the time_t value is “broken down” into its component parts. The component parts, their ranges, and their meanings are shown in Table 6.1.

Table 6.1: Fields in the struct tm

Member

Range

Meaning

tm_sec

0–60

Second within a minute. Second 60 allows for leap seconds.

 

 

(C90 had the range as 0–61.)

tm_min

0–59

Minute within an hour.

tm_hour

0–23

Hour within the day.

tm_mday

1–31

Day of the month.

tm_mon

0–11

Month of the year.

tm_year

0–N

Year, in years since 1900.

tm_wday

0–6

Day of week, Sunday = 0.

tm_yday

0–365

Day of year, January 1 = 0.

tm_isdst

< 0, 0, > 0

Daylight saving time flag.

The ISO C standard presents most of these values as “x since y.” For example, tm_sec is “seconds since the minute,” tm_mon is “months since January,” tm_wday is “days since Sunday,” and so on. This helps to understand why all the values start at 0. (The single exception, logically enough, is tm_mday, the day of the month, which ranges from 1–31.) Of course, having them start at zero is also practical; since C arrays are zero-based, it makes using these values as indices trivial:

static const char *const days[] = {                        Array of day names
    "Sunday", "Monday", "Tuesday", "Wednesday",
    "Thursday", "Friday", "Saturday",
};
time_t now;
struct tm *curtime;

time(& now);                                               Get current time
curtime = gmtime(& now);                                   Break it down
printf("Day of the week: %s\n", days[curtime->tm_wday]);   Index and print

Both gmtime() and localtime() return a pointer to a struct tm. The pointer points to a static struct tm maintained by each routine, and it is likely that these struct tm structures are overwritten each time the routines are called. Thus, it’s a good idea to make a copy of the returned struct. Reusing the previous example:

static const char *const days[] = { /* As before */ };
time_t now;
struct tm curtime;                                         Structure, not pointer

time(& now);                                               Get current time
curtime = *gmtime(& now);                                  Break it down and copy data
printf("Day of the week: %s\n", days[curtime.tm_wday]);    Index and print, use . not ->

The tm_isdst field indicates whether or not daylight saving time (DST) is currently in effect. A value of 0 means DST is not in effect, a positive value means it is, and a negative value means that no DST information is available. (The C standard is purposely vague, indicating only zero, positive, or negative; this gives implementors the most freedom.)

6.1.3 Formatting Dates and Times

The examples in the previous section showed how the fields in a struct tm could be used to index arrays of character strings for printing informative date and time values. While you could write your own code to use such arrays for formatting dates and times, standard routines alleviate the work.

6.1.3.1 Simple Time Formatting: asctime() and ctime()

The first two standard routines, listed below, produce output in a fixed format:

#include <time.h>                                  ISO C

char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);

As with gmtime() and localtime(), asctime() and ctime() return pointers to static buffers that are likely to be overwritten upon each call. Furthermore, these two routines return strings in the same format. They differ only in the kind of argument they accept. asctime() and ctime() should be used when all you need is simple date and time information:

#include <stdio.h>
#include <time.h>

int
main(void)
{
    time_t now;

    time(& now);
    printf("%s", ctime(& now));
}

When run, this program produces output of the form: ‘Sun May 25 16:32:42 2025’. The terminating newline is included in the result. To be more precise, the return value points to an array of 26 characters, as shown in Figure 6.1.

FIGURE 6.1

Figure 6.1: Return string from ctime() and asctime()

Much older Unix code relies on the fact that the values have a fixed position in the returned string. When using these routines, remember that they include a trailing newline. Thus, the small example program uses a simple "%s" format string for printf(), and not "%s\n", as might be expected.

ctime() saves you the step of calling localtime(); it’s essentially equivalent to

time_t now;
char *curtime;

time(& now);
curtime = asctime(localtime(& now));

6.1.3.2 Complex Time Formatting: strftime()

While asctime() and ctime() are often adequate, they are also limited:

  • The output format is fixed. There’s no way to rearrange the order of the elements.

  • The output does not include time-zone information.

  • The output uses abbreviated month and day names.

  • The output assumes English names for the months and days.

For these reasons, C90 introduced the strftime() standard library routine:

#include <time.h>                                               ISO C

size_t strftime(char *s, size_t max, const char *format,
                const struct tm *tm);

strftime() is similar to sprintf(). The arguments are as follows:

  • char *s

    A buffer to hold the formatted string.

  • size_t max

    The size of the buffer.

  • const char *format

    The format string.

  • const struct tm *tm

    A struct tm pointer representing the broken-down time to be formatted.

The format string contains literal characters, intermixed with conversion specifiers that indicate what is to be placed into the string, such as the full weekday name, the hour according to a 24-hour or 12-hour clock, a.m. or p.m. designations, and so on. (Examples are coming shortly.)

If the entire string can be formatted within max characters, the return value is the number of characters placed in s, not including the terminating zero byte. Otherwise, the return value is 0. In the latter case, the contents of s are “indeterminate.” The following simple example gives the flavor of how strftime() is used:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int
main(void)
{
    char buf[100];
    time_t now;
    struct tm *curtime;

    time(& now);
    curtime = localtime(& now);
    (void) strftime(buf, sizeof buf,
            "It is now %A, %B %d, %Y, %I:%M %p", curtime);

    printf("%s\n", buf);
    exit(0);
}

When run, this program prints something like:

It is now Sunday, May 25, 2025, 04:34 PM

Table 6.2 provides the full list of conversion specifiers, their possible alternative representations, and their meanings.

Table 6.2: strftime() conversion format specifiers

Specifier(s)

Meaning

%a

The locale’s abbreviated weekday name.

%A

The locale’s full weekday name.

%b, %Ob

The locale’s abbreviated month name.

%B, %OB

The locale’s full month name.

%c, %Ec

The locale’s “appropriate” date and time representation.

%C, %EC

The century (0099).

%d, %Od

The day of the month (0131).

%D

Same as %m/%d/%y.

%e, %Oe

The day of the month. A single digit is preceded with a space (131).

%F

Same as %Y-%m-%d (ISO 8601 date format).

%g

The last two digits of week-based year (0099).

%G

The ISO 8601 week-based year.

%h

Same as %b.

%H, %OH

The hour in a 24-hour clock (0023).

%I, %OI

The hour in a 12-hour clock (0112).

%j

The day of the year (001366).

%m, %Om

The month as a number (0112).

%M, %OM

The minute as a number (0059).

%n

A newline character (’\n’).

%p

The locale’s AM/PM designation.

%r

The locale’s 12-hour clock time.

%R

Same as %H:%M.

%S, %OS

The second as a number (0060).

%t

A tab character (’\t’).

%T

Same as %H:%M:%S (ISO 8601 time format).

%u, %Ou

ISO 8601 weekday number, Monday = 1 (17).

%U, %OU

Week number, first Sunday is first day of week 1 (0053).

%V, %OV

ISO 8601 week number (0153).

%w, %Ow

The weekday as a number, Sunday = 0 (06).

%W, %OW

Week number, first Monday is first day of week 1 (0053).

%x, %Ex

The locale’s “appropriate” date representation.

%X, %EX

The locale’s “appropriate” time representation.

%y, %Ey, %Oy

The last two digits of the year (0099).

%Y, %EY

The year as a number.

%z

Time zone offset from UTC, in the form +hhmm or -hhmm.

%Z

The locale’s time zone, or no characters if no time-zone information is available.

%%

A single %.

A locale is a way of describing the current location, taking into account such things as language, character set, and defaults for formatting dates, times, and monetary amounts. We deal with them in Chapter 15, “Internationalization and Localization,” page 507. For now, it’s enough to understand that the results from strftime() for the same format string can vary according to the current locale.

The versions starting with %E and %O are for “alternative representations.” Some locales have multiple ways of representing the same thing; these specifiers provide access to the additional representations. If a particular locale does not support alternative representations, then strftime() uses the regular version.

Many Unix versions of date allow you to provide, on the command line, a format string that begins with a + character. date then formats the current date and time and prints it according to the format string:

$ date +'It is now %A, %B %d, %Y, %I:%M %p'
It is now Sunday, May 25, 2025, 04:35 PM

The output depends on the current locale (where in the world the system thinks you are; locales and the environment variables that control them are discussed later, in Section 15.2, “Locales and the C Library,” page 508.) For example, by setting the environment variable LC_ALL to it_IT.utf8 (Italy), we might get something like this:

$ LC_ALL=it_IT.utf8 date +'It is now %A, %B %d, %Y, %I:%M %p'
It is now domenica, maggio 25, 2025, 04:36

Note that despite the %p in the format string, there is no a.m./p.m. indication. By setting LC_ALL to C, we can get the traditional Unix output:

$ LC_ALL=C date +'It is now %A, %B %d, %Y, %I:%M %p'
It is now Sunday, May 25, 2025, 04:35 PM

Many of strftime()’s specifiers come from such existing Unix date implementations. The %n and %t formats are not strictly necessary in C, since the tab and newline characters can be directly embedded in the string. However, in the context of a date format string on the command line, they make more sense. Thus they’re included in the specification for strftime() as well.1

The ISO 8601 standard defines (among other things) how weeks are numbered within a year. According to this standard, weeks run Monday through Sunday, and Monday is day 1 of the week, not day 0. If the week in which January 1 comes out contains at least four days in the new year, then it is considered to be week 1. Otherwise, that week is the last week of the previous year, numbered 52 or 53. These rules are used for the computation of the %g, %G, and %V format specifiers. (While parochial Americans such as the author may find these rules strange, they are commonly used throughout Europe.)

Many of the format specifiers produce results that are specific to the current locale. In addition, several indicate that they produce the “appropriate” representation for the locale (for example, %x). The C standard defines the values for the "C" locale. These values are listed in Table 6.3.

Table 6.3: "C" locale values for certain strftime() formats

Specifier

Meaning

%a

The first three characters of %A.

%A

One of Sunday, Monday, …, Saturday.

%b

The first three characters of %B.

%B

One of January, February, …, December.

%c

Same as %a %b %e %T %Y.

%p

One of AM or PM.

%r

Same as %I:%M:%S %p.

%x

Same as %m/%d/%y.

%X

Same as %T.

%Z

Implementation-defined.

In addition to the strftime() features mandated by the C standard, POSIX extends the format specifiers in a manner similar to what printf() allows. Between the % and the format letter, you may also provide:

  • An optional flag. There are two possible values:

    • 0 Specify that the values should be padded with ’0’ characters.

    • + Print a leading + for positive numeric values, or a - for negative ones.

    In our testing, GLIBC does not support either of these flags, but things may change in the future.

  • A positive numeric field width. If the formatted value is smaller than the field’s width, it is padded with the default padding character. On GLIBC systems, this is ’0’.

  • The optional E and O modifiers shown earlier.

It should be obvious that strftime() provides considerable flexibility and control over date- and time-related output, in much the same way as printf() and sprintf() do. Furthermore, strftime() cannot overflow its buffer, since it checks against the passed-in size parameter, making it a safer routine than is sprintf().

As a simple example, consider the creation of program log files, when a new file is created every hour. The file name should embed the date and time of its creation in its name:

/* Error checking omitted for brevity */
char fname[PATH_MAX];       /* PATH_MAX is in <limits.h> */
time_t now;
struct tm *tm;
int fd;

time(& now);
tm = localtime(& now);
strftime(fname, sizeof fname, "/var/log/myapp.%Y-%m-%d-%H:%M", tm);
fd = creat(name, 0600);
...

The year-month-day-hour-minute format causes the file names to sort in the order they were created.

6.1.4 Converting a Broken-Down Time to a time_t

Obtaining seconds-since-the-Epoch values from the system is easy; that’s how date and times are stored in inodes and returned from time() and stat(). These values are also easy to compare for equality or by < and > for simple earlier-than/later-than tests.

However, dates entered by humans are not so easy to work with. For example, many versions of the touch command allow you to provide a date and time to which touch should set a file’s modification or access time (with utimensat(), as described in Section 5.6.4, “Changing Timestamps: utime() and Successors,” page 148).

Converting a date as entered by a person into a time_t value is difficult: leap years must be taken into account, time zones must be compensated for, and so on. Therefore, the C90 standard introduced the mktime() function:

#include <time.h>                                    ISO C

time_t mktime(struct tm *tm);

To use mktime(), fill in a struct tm with appropriate values: year, month, day, and so on. If you know whether daylight saving time was in effect for the given date, set the tm_isdst field appropriately: 0 for “no,” and positive for “yes.” Otherwise, use a negative value for “don’t know.” The tm_wday and tm_yday fields are ignored.

mktime() assumes that the struct tm represents a local time, not UTC. It returns a time_t value representing the passed-in date and time, or it returns (time_t) –1 if the given date/time cannot be represented correctly. Upon a successful return, all the values in the struct tm are adjusted to be within the correct ranges, and tm_wday and tm_yday are set correctly as well. Here is a simple example:

 1  /* ch-general1-echodate.c --- demonstrate mktime(). */
 2
 3  #include <stdio.h>
 4  #include <stdlib.h>
 5  #include <time.h>
 6
 7  int
 8  main(void)
 9  {
10       struct tm tm;
11       time_t then;
12
13       printf("Enter a Date/time as YYYY/MM/DD HH:MM:SS : ");
14       scanf("%d/%d/%d %d:%d:%d",
15           & tm.tm_year, & tm.tm_mon, & tm.tm_mday,
16           & tm.tm_hour, & tm.tm_min, & tm.tm_sec);
17
18       /* Error checking on values omitted for brevity. */
19       tm.tm_year -= 1900;
20       tm.tm_mon--;
21
22       tm.tm_isdst = -1;       /* Don't know about DST */
23
24       then = mktime(& tm);
25
26       printf("Got: %s", ctime(& then));
27       exit(EXIT_SUCCESS);
28  }

Line 13 prompts for a date and time, and lines 14–16 read it in. (Production code should check the return value from scanf(). mktime() checks that the values it receives are within sane ranges.) Lines 19 and 20 compensate for the different basing of years and months, respectively. Line 24 indicates that we don’t know whether the given date and time represent daylight saving time. Line 24 calls mktime(), and line 26 prints the result of the conversion. When compiled and run, we see that it works:

$ ch-general1-echodate
Enter a Date/time as YYYY/MM/DD HH:MM:SS : 2025/07/10 12:44:55
Got: Thu Jul 10 12:44:55 2025

6.1.5 Parsing a Date and Time into a struct tm

With strftime(), we turn a struct tm into a human-readable string. POSIX provides an additional routine to go in the opposite direction: taking a string and turning it back into a struct tm. This lets you avoid the pain of manually parsing a date to get a struct tm. The routine is strptime():

#define _XOPEN_SOURCE                                   GLIBC
#include <time.h>                                       POSIX XSI

char *strptime(const char *buf, const char *format, struct tm *tm);

The parameters are:

  • const char *buf

    The text of the date string to be parsed.

  • const char *format

    A format string describing the contents of buf.

  • struct tm *tm

    A pointer to a struct tm in which to place the parsed values.

Upon error the return value is NULL. Otherwise, the return value points to the first character that was not parsed. Here is a simple example program:

/* ch-general1-parsetime.c --- Adapted from POSIX strptime() description. */

#define _XOPEN_SOURCE        /* Needed on GLIBC, must be before all includes! */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>

int
main(int argc, char **argv)
{
    struct tm tm;
    time_t t;

    if (strptime("15 Dec 2025 18:07:53", "%d %b %Y %H:%M:%S", & tm) == NULL) {
        fprintf(stderr, "%s: strptime failed: %s\n", argv[0], strerror(errno));
        exit(EXIT_FAILURE);
    }

    printf("year: %d; month: %d; day: %d;\n",
        tm.tm_year + 1900, tm.tm_mon, tm.tm_mday);
    printf("hour: %d; minute: %d; second: %d\n",
        tm.tm_hour, tm.tm_min, tm.tm_sec);
    printf("week day: %d; year day: %d\n", tm.tm_wday, tm.tm_yday);

    /* Tell mktime() to determine if daylight saving time is in effect */
    tm.tm_isdst = -1;
    t = mktime(&tm);
    if (t == -1) {
        fprintf(stderr, "%s: mktime failed: %s\n", argv[0], strerror(errno));
        exit(EXIT_FAILURE);
    }
    printf("Seconds since the Epoch: %ld\n", (long) t);
    return EXIT_SUCCESS;
}

When run, it produces this output:

$ ch-general1-parsetime
year: 2025; month: 11; day: 15;
hour: 18; minute: 7; second: 53
week day: 1; year day: 348
Seconds since the Epoch: 1765814873

On GLIBC systems, in order to access the declaration of strptime(), you must define _XOPEN_SOURCE before including any standard header files.

The conversion specifiers available for use in format are purposely the same as those for strftime(); see Table 6.2. GLIBC provides an additional value, %s, which scans a seconds-since-the-Epoch value.

Additionally, %a and %A are equivalent to each other, as are %b and %B; %n and %t match any whitespace characters.

POSIX allows optional flags of 0 or + that are to be ignored, and an optional field width, neither of which are mentioned by the Linux strptime(3) manpage. The optional E and O modifiers are allowed.

6.1.6 Getting Time-Zone Information

Early Unix systems embedded time-zone information into the kernel when it was compiled. The rules for daylight saving time conversions were generally hard-coded, which was painful for users outside the United States or in places within the United States that didn’t observe DST.

Modern systems have abstracted that information into binary files read by the C library when time-related functions are invoked. This technique avoids the need to recompile libraries and system executables when the rules in a particular location change and makes it much easier to update the rules.

The C language interface to time-zone information evolved across different Unix versions, both System V and Berkeley, until finally it was standardized by POSIX as follows:

#include <time.h>                                 POSIX

extern char *tzname[2];
extern long timezone;                             POSIX XSI
extern int daylight;                              POSIX XSI

void tzset(void);

The tzset() function examines the TZ environment variable to find time-zone and daylight saving time information.3 If that variable isn’t set, then tzset() uses an “implementation-defined default time zone,” which is most likely the time zone of the machine you’re running on.

After tzset() has been called, the local time-zone information is available in several variables:

  • extern char *tzname[2]

    The standard and daylight saving time names for the time zone. For example, for U.S. locations in the Eastern time zone, the time-zone names are "EST" (Eastern Standard Time) and "EDT" (Eastern Daylight Time).

  • extern long timezone

    The difference, in seconds, between the current time zone and UTC. The standard does not explain how this difference works. In practice, negative values represent time zones east of (ahead of, or later than) UTC, and positive values represent time zones west of (behind, or earlier than) UTC. If you look at this value as “how much to change the local time to make it be the same as UTC,” then the sign of the value makes sense.

  • extern int daylight

    This variable is zero if daylight saving time conversions should never be applied in the current time zone, and nonzero otherwise.

The POSIX standard indicates that ctime(), localtime(), mktime(), and strftime() all act “as if” they call tzset(). This means that they need not actually call tzset(), but they must behave as if it had been called. (The wording is intended to provide a certain amount of flexibility for implementors while guaranteeing correct behavior for user-level code.)

In practice, this means that you will almost never have to call tzset() yourself. However, it’s there if you need it.

6.1.6.1 BSD Systems Gotcha: timezone(), Not timezone

Instead of the POSIX timezone variable, several systems derived from 4.4 BSD provide a timezone() function:

#include <time.h>                                                   BSD

char *timezone(int zone, int dst);

The zone argument is the number of minutes west of GMT, and dst is true if daylight saving time is in effect. The return value is a string giving the name of the indicated zone, or a value expressed relative to GMT. This function provides compatibility with the V7 function of the same name and behavior.

This function’s existence makes portable use of the POSIX timezone variable difficult. Fortunately, we don’t see a huge need for it: strftime() should be sufficient for all but the most unusual needs.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.