38

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); ... 
6
  • 2
    In your non-portable way, is there even a guarantee that there's a null-terminator within valid memory? Commented Sep 16, 2013 at 23:33
  • That isn't really the question. Do you know the answer to the question I asked? Commented Sep 16, 2013 at 23:35
  • No, but absent of any portable solution, your non-portable solution should still not invoke UB. Commented Sep 16, 2013 at 23:36
  • 1
    It is just an example to make my question about the printing of types clearer. Commented Sep 16, 2013 at 23:37
  • See also: stackoverflow.com/questions/10508236/… Commented Oct 14, 2013 at 0:39

4 Answers 4

30

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().

Sign up to request clarification or add additional context in comments.

3 Comments

What does this line mean --> "if ((time_t)-1 > 0) {" ? (thank you)
@Moytaba: That condition is true if and only if time_t is an unsigned integer type. Converting a negative value (-1) to an unsigned type yields a positive results.
I think you meant (time_t)1 / 2 == 0 or similar for (signed) integer versus floating discrimination
12

You can use difftime() to obtain a double:

time_t t = time(NULL); printf("seconds 1970->now: %.f\n", difftime(t, (time_t) 0)); 

It is simple and I think it is portable.

1 Comment

This is by far the best solution. It is also very simple and portable (Linux, FreeBSD).
11

Generally, the way to display the value of a time_t is to break down its components to a struct tm using gmtime or localtime and display those or convert them as desired with strftime, or ctime to go directly from time_t to a string showing local time.

If you want to see the raw value for some purpose, the C standard specifies that time_t is real, which means it is integer or floating-point (C 2011 (N1570) 6.2.5 17). Therefore, you should be able to convert it to double and print that. There is some possibility that time_t can represent values that double cannot, so you might have to guard against that if you want to take care regarding exotic implementations. Since difftime returns the difference of two time_t objects as a double, it seems C does not truly support time_t with more precision than a double.

1 Comment

I don't think anything in the standard forbids making time_t equivalent to long double. It would just mean that the result of difftime() might be imprecise. difftime() can lose precision anyway if time_t and double are both 64 bits.
3

The C standard says time_t will be a 'real type' (meaning an integer type or a floating point type, though in practice it is always an integer type).

With time_t, your best bet is to format it with strftime() after analyzing it with localtime() or gmtime() — this can be done portably.

Unportably, you have to determine by some mechanism what is the correct format specifier. You might use PRI_[Xxodi]_time and SCN_[Xxodi]_time or something similar as a non-standard but close-to-standard (without trampling on the reserved namespace — which is names starting PRI or SCN followed by a lower-case letter or X). You use some mechanism to specify that...encapsulating the unportable information in one place.

Comments