Here’s a handy chart of the C Standard Library functions in time.h :

The ovals are data types and the rectangles are functions. The three basic types are:

time_t : number of seconds since the start of the UNIX epoch. This is always UTC!

: number of seconds since the start of the UNIX epoch. This is always UTC! struct tm : A broken-down date, split into years, months, seconds, etc. In Python, it’s a tuple.

: A broken-down date, split into years, months, seconds, etc. In Python, it’s a tuple. string: Any string representation of a time, e.g. “Wed Jun 30 21:49:08 1993″.

Generally you either want a time_t (because it’s easy to do arithmetic with) or a string (because it’s pretty to look at). So to get from a time_t to a string , you should use something like strftime("%Y-%m-%d", localtime(time())) . To go the other way, you’d use mktime(strptime(str, "%Y-%m-%d")) .

This library has been around since at least 1982. It’s been replicated in many other languages (Python, Perl, Ruby). We seem to be stuck with it.

Read on for my rant about why this is all idiotic.



Let me just say that I think this is a horrible system. You almost never want to use struct tm . Most of the time, you want to go between strings and time_t . But lonely ctime is the only function that makes this jump, and it doesn’t let you set the output format or time zone.

The names are not exactly descriptive, either. They all end in “time”, which makes some sense. strptime and strftime are even OK, if a bit cryptic. The p stands for “parse” and the f stands for “format”, ala printf . The parameter order is hard to remember, though. Don’t use gmtime unless you have a good reason. ctime and asctime are non-sensical, but I don’t use them much, either. My greatest loathing is reserved for localtime and mktime . I can never remember which of these does which. Only mnemonic I can think of: mktime makes a time_t from a struct tm.

For another exercise in head-scratching, follow the role of time zones through this chart. time_t knows no time zones — it’s always UTC. To get to struct tm , you need to specify a time zone. This is not made explicit in the struct, however, so you need to do your own bookkeeping. The time zone for conversion isn’t a parameter or anything sensible like that, either. You just get two choices: GM (UTC) time or local time. And if you choose gmtime, you’ll never be able to get back to time_t because that function doesn’t exist. (Some systems supply a mkgmtime or timegm function.)

How would I design it? struct tm would lose its place at the center of everything. There would be sensibly-named functions to go between time_t and string :

time_t parsetime(format, string[, timezone])

string formattime(format, time_t[, timezone])

And if you really need them:

splittime(time_t, struct tm*)

time_t packtime(struct tm*)

Was that really so hard?

Permalink