02.24.09

Chart of time.h Functions

Posted in programming at 10:04 pm by danvk

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

unixtime

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!
  • struct tm: 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?

3 Comments

  1. Johannes said,

    February 24, 2009 at 11:32 pm

    Nice rant. To actually implement parsetime / formattime, you might need to split up the time_t value into its components, which you’d then interpret according to timezone etc. to make time_t. And in case your application (e.g. crond) needs to do hour/day/… arithmetic, your splittime implementation will need to undo that interpretation, so your proposal doesn’t let you write the fastest code possible (or perhaps I’m a bit confused). Maybe not critically important these days, but as you note, the original library has been around.

    It might be interesting (or amusing, depending on your perspective) to look at how Java ended up providing this functionality:
    Date – represents a specific instant in time, with millisecond precision.
    DateFormat – converts back and forth between Date and Strings, considering language, locale, and type of calendar used.
    Calendar – lets you do arithmetic with split-up dates, e.g. help answer the question “Is this the second Saturday of the month?”.
    Of course since it’s Java there are more classes beyond this, and in practice the JVM still picks up some environmental settings from the OS. As far as problems and bugs with go, it should be about even with Python etc. in this area, but it sure is a bit more complicated. Witness the amount of deprecation in java.util.Date and the comments at the top; perhaps this demonstrates that it’s actually somewhat nontrivial to make a nice API for dealing with time.

  2. danvk said,

    February 24, 2009 at 11:41 pm

    Thanks for the info, Johannes, I’d forgotten about the Java Date API. It looks like .NET also avoided the historical baggage of the C Standard Library.

    As you say, my alternative wouldn’t be quite as efficient. The existing time.h functions are all quite simple to implement (maybe with the exception of strptime). But I think we’re at the point where that complexity is tiny compared with the developer cost of a confusing API. If an application like crond needs the speed, they can work something out.

    time.h has certainly evolved too: witness the new critters like asctime_r, ctime_r, gmtime_r and localtime_r.

  3. Peteris Krumins said,

    February 25, 2009 at 7:10 am

    There is another nice graph of relationship of various time functions in Advanced Programming in the Uni Environment:

    http://imagepaste.com/img/dab9a2e71ee652988eacb49b986fe92c.png