Although I fully agree with the intent (of pushing to learn for themselves) and what Salem wrote above, I've seen so many incorrectly implemented time-related functions that "usually work" but fail in some corner cases that I really, really want code like this to be written correctly. To that end, I'll show you how to write this in a very defensive but robust manner. (Some would say these are "overkill"; I disagree.)
There is a slight "trick" in here: the nanoseconds part is a long, which per the C99 specs can handle at least the range -2147483647 to +2147483647. In other words, we know that we can always add or substract two nanosecond parts without overflowing. However, we do need to fold the result back to the valid nanosecond range 0 to 999999999 (inclusive), as some implementations cannot handle a nanosecond part outside that range. (Although most fold it correctly to seconds, some return an error, and some just produce incorrect results.)
Code:
#define _POSIX_C_SOURCE 200809L
#include <time.h>
struct timespec timespec_since(const struct timespec after, const struct timespec before)
{
struct timespec result;
result.tv_sec = after.tv_sec - before.tv_sec;
result.tv_nsec = after.tv_nsec - before.tv_nsec;
if (result.tv_nsec < -2000000000L) {
/* This should not occur. We handle it because we can, not because we must. */
result.tv_sec -= 3;
/* 3000000000L might not be a valid long, so we do it in two steps. */
result.tv_nsec += 2000000000L;
result.tv_nsec += 1000000000L;
} else
if (result.tv_nsec < -1000000000L) {
/* This should not occur. We handle it because we can, not because we must. */
result.tv_sec -= 2;
result.tv_nsec += 2000000000L;
} else
if (result.tv_nsec < 0) {
result.tv_sec -= 1;
result.tv_nsec += 1000000000L;
} else
if (result.tv_nsec >= 1000000000L) {
/* This should not occur. We handle it because we can, not because we must. */
result.tv_sec += 1;
result.tv_nsec -= 1000000000L;
} else
if (result.tv_nsec >= 2000000000L) {
/* This should not occur. We handle it because we can, not because we must. */
result.tv_sec += 2;
result.tv_nsec -= 2000000000L;
}
return result;
}
Now, the C99 standard does state that (a/b)*b + a%b = a if a/b is representable (C99:6.5.5p6). We could use that with b = 1000000000L in our calculations above, but I fear it would make it impossible to see the corner cases in the code it is supposed to handle. (Also, I'm not at all convinced that it would be better used here, since negative values of a would require three additions anyway, since (tv_nsec/1000000000L)*1000000000L + 1000000000L may not be representable by a long.)
In practice, I personally like to switch to the number of elapsed seconds as a double-precision number, since some past moment. This is much easier:
Code:
#define _POSIX_C_SOURCE 200809L
#include <time.h>
/* Return the number of seconds (since optional mark),
* using the specified clock clk.
* Returns 0.0 if the clock_gettime() call fails.
*/
double seconds_since(const clockid_t clk, const struct timespec *const mark)
{
struct timespec t;
if (clock_gettime(clk, &t))
return 0.0;
if (mark)
return (double)t.tv_sec - (double)mark->tv_sec + (double)(t.tv_nsec - mark->tv_nsec) / 1000000000.0;
else
return (double)t.tv_sec + (double)t.tv_nsec / 1000000000.0;
}
Since the double type has 53 significant bits, it will be accurate to within a nanosecond for durations up to 8388608 seconds (about 97 days). For longer durations, you just lose precision correspondingly (as the type has about 16 significant digits only).
On microcontrollers, you can use long to represent time in milliseconds (just multiply the seconds by a thousand, and divide the nanoseconds by a million) to avoid requiring floating-point support. This can cover positive and negative durations of at least 24 days or more (596 hours or more), and is just as easy to format for display purposes.