Format specifiers for implementation-defined types like time_t

effervescent-phantom picture effervescent-phantom · Sep 17, 2013 · Viewed 25.7k times · Source

I want to make my code more platform-/implementation-independent. I don't know what a time_t will be implemented as on the platform when the code is being compiled. How do I know the type of t to determine what format specifier to use?

...
time_t t = time(NULL);
printf("%s", t);
...

Answer

Keith Thompson picture Keith Thompson · Sep 17, 2013

Usually you can use a cast to convert the operand to some type for which you know the right format.

Your proposed solution:

time_t t = time(NULL);
printf("%s", t);

clearly will not work, since time_t is a numeric type, not char*.

We know, in general, that time_t is an arithmetic type. Something like this:

 printf("%ld\n", (long)t);

is likely to work on most systems. It can fail (a) if time_t is an unsigned type no wider than unsigned long and the current value of t exceeds LONG_MAX, or (b) if time_t is a floating-point type.

If you have C99 support, you can use long long, which is a little better:

printf("%lld\n", (long long)t);

If you really want to go overboard with portability, you can detect what kind of type time_t is:

if ((time_t)-1 > 0) {
    // time_t is an unsigned type
    printf("%ju\n", (uintmax_t)t);
}
else if ((time_t)1 / 2 > 0) {
    // time_t is a signed integer type
    printf("%jd\n", (intmax_t)t);
}
else {
    // time_t is a floating-point type (I've never seen this)
    printf("%Lg\n", (long double)t);
}

You might want to tweak the %Lg format to something like %Lf or %.10Lf, depending on what output format you want.

Again, this assumes C99 support -- and you'll need #include <stdint.h> to make uintmax_t and intmax_t visible.

time_t and clock_t are a bit unusual, in that the standard says only that they're arithmetic type capable of representing times. (In principle they could be complex types, but I'd say ignoring that possibility is worth the risk.)

In most other cases, you'll probably know whether a given type is signed, unsigned, or floating-point, and you can just convert to the widest type of that kind.

Note that if you don't know how time_t is represented, you probably won't understand the output of the printf (such as 1379375215) either -- unless your goal is to figure that out.

(If you were programming in C++ rather than C, std::cout << t << "\n"; would automatically use the correct overloaded operator<<.)

If you want human-readable output (like Mon 2013-09-16 16:46:55 PDT), you'll want to use one of the conversion functions declared in <time.h>, such as asctime() or strftime().