20

I've always been curious, why does the time(time_t *) function both return a time_t, and set the time to the passed in pointer?

Example of returning the time:

time_t myTime = time(NULL);
printf("The time is now %s", ctime(&myTime));

Example of setting the value to the pointer:

time_t myTime;
time(&myTime);
printf("The time is now %s", ctime(&myTime));

I originally thought there would be a performance gain by writing to the memory instead of returning, but if it has to do both, doesn't that just make it slower?

wjl
  • 7,143
  • 1
  • 30
  • 49

2 Answers2

22

There's no real benefit in the way it's currently defined.

I suspect that when the time() function was first defined, it used a type that could not be returned from a function. Very early C implementations didn't have long int and were not able to return structures from functions. On a system with 16-bit ints, the only way to represent a time would be as a structure or as an array; 16 bits worth of seconds is less than a day.

UPDATE: My speculation is confirmed, see below.

So early implementations of time() might have been used something like this (speculation):

time_t now;
time(&now);    /* sets now.time_high, now.time_low */

or perhaps:

int now[2];
time_t(now);    /* sets now[0], now[1] */

When later C implementations added longer integers and the ability to return structures by value, the ability to return a time_t value from the time() function was added, but the old functionality was kept to avoid breaking existing code.

I think that if time() were being defined today, it would look more like this:

time_t time(void);

I haven't been able to confirm that old implementations of the time() function worked this way (try Googling "time"!), but it makes sense given the history of the language.

If you pass a null pointer to the time() function, it returns the current time without also storing it in a variable; this avoids some of the performance penalty:

time_t now = time(NULL);

UPDATE

Early UNIX sources are available in https://github.com/dspinellis/unix-history-repo

Checking out the Research-V6 git tag, the man page for the time() system call is in usr/doc/man/man2/time.2. It's written in an obsolete form of *roff, but here's my attempt at formatting it. (The implementation, written in PDP-11 assembly and callable from C, is in usr/source/s2/time.s.)

C didn't have void functions at that time. Functions without a declared return type would return int by default. It's not clear to me what the time function would return, but my guess is that it would be the high-order 16-bit word of the 32-bit value. As of the date on the man page, that would have been about 1730, in units of 216 seconds (18h12m16s). Correctly written C code would not have attempted to use the return value.

TIME 8/5/73

NAME

time - get date and time

SYNOPSIS

(time = 13.)
sys time

time(tvec)
int tvec[2]

DESCRIPTION

Time returns returns the time since 00:00:00 GMT, Jan. 1, 1970, measured in seconds. From as, the high order word is in the r0 register and the low order is in r1. From C, the user-supplied vector is filled in.

SEE ALSO

date (I), stime (II), ctime (III)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • That sounds plausible. I was expecting the performance penalty to go the other way. It's avoided if you pass a NULL, but if you call it with a pointer and ignore the return result, it still has to shove the time into the return register. Sometimes I forget that C is nearly twice as old as I am. :) – wjl Mar 30 '12 at 15:20
  • 3
    *Sometimes I forget that C is nearly twice as old as I am.* -- Thanks for making me feel old! 8-)} – Keith Thompson Mar 30 '12 at 15:23
  • Thank you for coming and updating this, years later! – wjl Dec 26 '19 at 11:33
7

It allows you to nest a call to time() within another expression, instead of doing it in a separate statement:

time_t x = time(&now) + more_time;

When the above statement finishes, now should contain the current time, and x should contain the current time plus some value.

strcpy falls in the same case because it returns the same char * pointer that has been passed as its destination, so nesting it is possible as well:

printf("Copied string is %s", strcpy(dst, src));
Blagovest Buyukliev
  • 42,498
  • 14
  • 94
  • 130
  • 5
    If `time()` took no arguments, that could just as easily be done by nesting an assignment: `time_t x = (now=time())` + more_time;` – Keith Thompson Mar 30 '12 at 14:57
  • 4
    While I agree with Keith, this answer is the first I've seen to provide a potential convenient usage for `time`'s odd signature... – R.. GitHub STOP HELPING ICE Mar 30 '12 at 16:49
  • 3
    I think in both cases it'd be better to use two lines for readability sake. It probably compiles down to the same thing either way. – wjl Apr 14 '12 at 20:21