Why does `gmtime` take a pointer? - c

According to the documentation the struct tm *gmtime(const time_t *timer); is supposed to convert the time_t pointed to by timer to a broken down time.
Now is there a reason why they decided to make the function take a pointer to the time_t instead of passing the time_t directly?
As far as I can see time_t is of arithmetic type and should therefore have been possible to pass directly (also I find it reasonable that it would have fit into a long). Also there seem to be any specific handling of the NULL pointer (which could have motivated passing a pointer).
Is there something I'm missing? Something still relevant today?

From what I've seen, it's more a historical quirk. When time.h was first introduced, and with it functions like time, it used a value that could not be returned (ie: no long int etc). The standard defined an obscure time_t type that still leaves a lot of room for vendors to implement in weird ways (it has to be an arithmetic type, but no ranges or max values are defined for example) - C11 standard:
7.27.1 Components of time.
[...]
3 .The types declared are size_t (described in 7.19);
clock_t
and
time_t
which are real types capable of representing times;
4. The range and precision of times representable in clock_t and time_t are
implementation-defined.
size_t is described, in C11, as "is the unsigned integer type of the result of the sizeof operator;"
In light of this, your comment ("I find it reasonable that it would have fit into a long") is an understandable one, but it's incorrect, or at the very least inaccurate. POSIX, for example requires time_t to be an integer or real floating type. A long fits that description, but so would a long double which doesn't fit in a long. A more accurate assumption to make would be that the minimum size of time_tis a 32bit int (until 2038 at least), but that time_t preferably is a 64bit type.
Anyway, back in those days, if a value couldn't be returned, the only alternative was to pass the memory to the function (which is a sensible thing to do).
This is why we have functions like
time_t time(time_t *t);
It doesn't really make sense to set the same value twice: once by returning it, and once using indirection, but the argument is there because originally, the function was defined as
time(time_t *t)
Note the lack of a return type, if time were added today, it'd either be defined as void time( time_t * ), or if the committee hadn't been drinking, and realized the absurdity of passing a pointer here, they'd define it as time_t time ( void );
Looking at the C11 standard regarding the time function, it seems that the emphasis of the functions' behaviour is on the return value. The pointer argument is mentioned briefly but it certainly isn't given any significance:
7.27.2.4 The time function
1. Synopsis
#include <time.h>
time_t time(time_t *timer);
2. Description
The time function determines the current calendar time. The encoding of the value is
unspecified.
3. Returns
The time function returns the implementation’s best approximation to the current
calendar time. The value (time_t)(-1) is returned if the calendar time is not
available. If timer is not a null pointer, the return value is also assigned to the object it
points to.
The main thing we can take from this is that, as far as the standard goes, the pointer is just a secondary way of getting at the return value of the function. Given that the return value indicates something went wrong ((time_t)(-1)), I'd argue that we should treat this function as though it was meant to be time_t time( void ).
But because the old implementation is still kicking around, and we've all gotten used to it, it's one of those things that should've been marked for deprecation, but because it never really was, it probably will be part of the C language for ever...
The only other reason why functions use time_t like this (const) is either historical, or to maintain a consistent API across the time.h API. AFAIK that is
TL;DR
Most time.h function use pointers to the time_t type for historical, compatibility, and consistency reasons.
I knew I read that stuff about the early days of the time function before, here's a related SO answer

The reason is probably that in the old days a parameter could be no larger than an integer, so you couldn't pass a long and had to do that as a pointer. The definition of the function never changed.

Related

Under what circumstances can time in time.h fail?

The time function in the header time.h is defined by POSIX to return a time_t which can, evidently, be a signed int or some kind of floating point number.
http://en.cppreference.com/w/c/chrono/time
The function, however, returns (time_t)(-1) on error.
Under what circumstances can time fail?
Based on the signature, time_t time( time_t *arg ) it seems like the function shouldn't allocate, so that strikes one potential cause of failure.
The time() function is actually defined by ISO, to which POSIX mostly defers except it may place further restrictions on behaviour and/or properties (like an eight-bit byte, for example).
And, since the ISO C standard doesn't specify how time() may fail(a), the list of possibilities is not limited in any way:
One way in which it may fail is in the embedded arena. It's quite possible that your C program may be running on a device with no real-time clock or other clock hardware (even a counter), in which case no time would be available.
Or maybe the function detects bad clock hardware that's constantly jumping all over the place and is therefore unreliable.
Or maybe you're running in a real-time environment where accesses to the clock hardware are time-expensive so, if it detects you're doing it too often, it decides to start failing so your code can do what it's meant to be doing :-)
The possibilities are literally infinite and, of course, I mean 'literally' in a figurative sense rather than a literal one :-)
POSIX itself calls out explicitly that it will fail if it detects the value won't fit into a time_t variable:
The time() function may fail if: [EOVERFLOW] The number of seconds since the Epoch will not fit in an object of type time_t.
And, just on your comment:
Based on the signature, time_t time( time_t *arg ), it seems like the function shouldn't allocate.
You need to be circumspect about this. Anything not mandated by the standards is totally open to interpretation. For example, I can envisage a bizarre implementation that allocates space for an NTP request packet to go out to time.nist.somewhere.org so as to ensure all times are up to date even without an NTP client :-)
(a) In fact, it doesn't even specify what the definition of time_t is so it's unwise to limit it to an integer or floating point value, it could be the string representation of the number of fortnights since the big bang :-) All it requires is that it's usable by the other time.h functions and that it can be cast to -1 in the event of failure.
POSIX does state that it represents number of seconds (which ISO doesn't) but places no other restrictions on it.
I can imagine several causes:
the hardware timer isn't available, because the hardware doesn't support it.
the hardware timer just failed (hardware error, timer registers cannot be accessed for some reason)
arg is not null, but points to some illegal location. Instead of crashing, some implementations could detect an illegal pointer (or catch the resulting SEGV) and return an error instead.
in the provided link "Implementations in which time_t is a 32-bit signed integer (many historical implementations) fail in the year 2038.". So after 1<<31 seconds since the epoch (1/1/1970), time return value overflows (well, that is, if the hardware doesn't mask the problem by silently overflowing as well).

How does the C function time() treat fractional seconds?

The time() function will return the seconds since 1970. I want to know how it does the rounding for the second returned.
For example, for 100.4s, will it return 100 or 101?
Is there a explicit definition?
The ISO C standard doesn't say much. It says only that time() returns
the implementation’s best approximation to the current calendar time
The result is of type time_t, which is a real type (integer or floating-point) "capable of representing times".
A lot of systems implement time_t as a signed integer type representing the whole number of seconds since 1970-01-01 00:00:00 GMT.
A quick experiment on my Ubuntu system (comparing the values returned by time() and gettimeofday()) indicates that the value returned by time() is truncated, so for example if the high-precision time is 1514866171.750058, the time() function will return 1514866171. Neither ISO C nor POSIX guarantees this, but I'd expect it to behave consistently on any UNIX-like systems.
7.27.2.4p3
The time function returns the implementation's best approximation to
the current calendar time. The value (time_t)(-1) is returned if the
calendar time is not available. If timer is not a null pointer, the
return value is also assigned to the object it points to.
It's implementation defined, so unless you specify your compiler and operating system, the answer is "best approximation".
Is there a explicit definition?
No
http://en.cppreference.com/w/c/chrono/time
The encoding of calendar time in time_t is unspecified, but most systems conform to POSIX specification and return a value of integral type holding the number of seconds since the Epoch. Implementations in which time_t is a 32-bit signed integer (many historical implementations) fail in the year 2038.

Can time(NULL) ever return failure?

Can the time_t time(time_t *t) function ever return failure if the argument passed is always NULL?
If the call is time(NULL), do we still need to check for the return value?
The only documented error code is EFAULT, which relates to the pointer being invalid.
Yes. time has a documented may fail case:
The time() function may fail if:
[EOVERFLOW] The number of seconds since the Epoch will not fit in an object of type time_t.
Source: http://pubs.opengroup.org/onlinepubs/9699919799/functions/time.html
Expect this to happen in practice in about 22 years, no sooner, and not on 64-bit systems or 32-bit ones that utilize a 64-bit time_t.
Also, the presence of any shall fail or may fail cases also allows for implementation-defined errors, though their existence would be a serious quality-of-implementation flaw.
EFAULT is a non-issue/non-existent because it only happens when your program has undefined behavior.
So despite all this, in the real world, time is not actually going to fail.
Can time(NULL) ever return failure?
No. C standard says that
C11: 7.27.2.4:
The time function returns the implementation’s best approximation to the current calendar time. The value (time_t)(-1) is returned if the calendar time is not available.
I've checked on RHEL, SLES and UBTU; the man 2 page yields the same (relevant) thing:
time() returns the time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
If t is non-NULL, the return value is also stored in the memory pointed to by t.
Anyway, going back to the original questions
Q0: Can the time_t time(time_t *t) function ever return failure if the argument passed is always NULL?
A/R0: Yes, if some very special events occurred (memory full, and so on, ...)
Q1: If the call is time(NULL), do we still need to check for the return value?
A/R1: The actual answer is "NO", you don't have to; the fact that the func could return something relevant, is a different story. After all, why calling a func ,if there's no need of doing so?
Q2: The only documented error code is EFAULT, which relates to the pointer being invalid.
You don't have anything to do with the invalid codes; as you said you're passing NULL, so there's no problem.
In the C standard, time() can return (time_t)(-1) if "the calendar time is not available". In the 1999 standard, for example, that is in Section 7.23.2.4, para 3.
Although that wording is less than specific, I would suggest it represents an error condition. Presumably an implementation can return (time_t)(-1) if it can't access the system clock, can't sensibly interpret the data it gets, etc.
R's answer describes what is the case for the posix spec.
(Not consider POSIX degraded mode functionality)
If the underlying real-time clock sub-system had a hardware fault like loss of clock integrity (battery) when the unit was off on an independent system, the return value of time() could certainly be (time_t) -1. In that case, it make no difference what the passed in time_t* was.

Why C has so many different types? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
I write a simple timer function to calculate the time elapsed between start and end
double mytimer(struct timeval *start, struct timeval *end)
{
return (end->tv_sec - start->tv_sec) + (end->tv_usec - start->tv_usec)*1e-6;
}
gcc gives the following warnings:
warning: conversion to ‘double’ from ‘__suseconds_t’ may alter its value
warning: conversion to ‘double’ from ‘__time_t’ may alter its value
Here is the definition of timeval:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
So my question is why does C define so many incompatible types instead of simply using primitive types such as int short ...? It's not user-friendly at all.
And how can I do arithmetic operations on these types?
update
Most of you seemed to have neglected my second question. What is the standard way to add two different types such as time_t and suseconds_t?
Because what time_t etc contains are implementation-defined, there is nothing saying that they should contain a number of seconds as an integer, like the comment in your code implies. The reason is that they want these types to be portable between different systems.
In practice, time.h is indeed rather cumbersome, so most of the time programs end up calling system-specific functions instead.
As buttiful butterfly correctly points out, the aim of the c++ language and standard library design is to enforce logical correctness at compile time.
What this means is that we aim for a situation where a program that would do the wrong thing under certain obscure circumstances simply will not compile (or will compile, but will warn you).
This means you can fix these logical errors before your software ever even runs in your test harness, let alone in front of your customers.
The result of this is that correctly written c++ code can be mostly proven to be 'correct' before run-time, meaning you spend a great deal less time tracking down obscure bugs that you otherwise would.
The magic of c++ is that is performs this incredible feat while offering a fantastic of efficiency and code optimisation.
note: by 'correct' I mean that it will reliably do what you think you're telling it to do. It's still up to you to write the correct logic!
as to the questions:
So my question is why does C define so many incompatible types instead of simply using primitive types such as int short ...? It's not user-friendly at all.
They are made incompatible in order to deliberately prevent you from converting them to each other. They represent different concepts, in the same way that velocity and distance are different concepts. There is no direct conversion between them.
And how can I do arithmetic operations on these types?
make the intermediate results of your arithmetic something that these types can safely convert to without losing precision. In this case, tv_sec and tv_usec are integral types, so even though they are incompatible with each other, they are both convertible to double individually.
for example:
double mytimer(struct timeval *start, struct timeval *end)
{
return double(end->tv_sec - start->tv_sec) +
double(end->tv_usec - start->tv_usec) * 1e-6;
}
To avoid the warnings
double mytimer(struct timeval * start, struct timeval * end) {
long usec = end->tv_usec - start->tv_usec;
long sec = end->tv_sec - start->tv_sec;
return 1.0 * sec + 1e-6 * usec;
}
If you see the datatypes defined in <sys/types.h>, inherently you'll find
typedef long time_t;
...
typedef long suseconds_t;
There are no specific format specifiers for struct timeval but the structure members are of type long. Hope this solves your problem.
The reason is that the built-in types like int are platform-dependent. So on one computer an int might be good enough to store a time value, while on another you would need a long. To allow people to write programs that run on all platforms, types like time_t were introduces, which are often just alias definitions for some primitive type that is suitable on that particular platform. This in fact takes a little more learning effort in the beginning, but this effort will pay large dividends in the long run.
Makes sense, doesn't it?
[EDIT]: As for the strange warnings: The compiler is warning that the conversion of a time_t and suseconds_t to double might lose some information. That is because both types are integer types with more bits than the mantissa part of double. In your case that would apply only to very large time values, so you can simply ignore these warnings. (But how should the compiler know that a time_t value normally fits into a double? So he emits this warning.) There is in fact not much you can do about that without making the code platform-dependent.
[N.B. I have almost completely rewritten this answer since I first posted it.]
The answer to your first question is, C has so many types in an attempt to balance the needs of supporting machines of all different word sizes, with reasonable portability. Things get more complicated due to the desire to also support specialized quantities like "sizes of data structures", "offsets in files", and "times in the real world" reasonably portably, and in spite of the fact that sometimes those specialized quantities end up being determined not by the language spec or the compiler but rather by the underlying operating system.
In general, there are two concerns when converting from a large integral type to a floating-point type:
the floating-point type might not be able to represent all the significant digits of the integral type accurately
the floating-point type might not even be able to handle the range of the integral type
In the case of time_t, there is an additional concern:
Type time_t might not be an integer number of seconds that you can meaningfully subtract
(These days, though, the "helpful" compiler warnings in response to these concerns do sometimes seem to border on the nannyish, and I share your concern. It can be hard to understand what obscure situation the compiler is actually worried about, and it can be hard to see how to rewrite the code without warnings, and it can be hard to be sure that any casts you end up having to insert don't end up making the code even less safe.)
If you're not worried about concern #3 (if you're willing to assume that time_t is an integer number of seconds), you can minimize the chances for data loss by doing the subtraction first (and in the integral type), and then converting:
return (sometype)(end->tv_sec - start->tv_sec) +
(sometype)(end->tv_usec - start->tv_usec) / 1e6;
But of course the big question is, what should sometype be?
I believe your best bet is to cast to double in each of those places. Both the guaranteed range and precision of type double in C are quite large. So unless you're manipulating time differences greater than 1e50 years (and unless someone has implemented type subsec_t to be a 266-bit type or something), your code should be safe even with the warning-suppressing casts, and you can insert a comment to that effect.
If you want to understand how these concerns can actually manifest in practice, it's easy to demonstrate them. Try this:
float f = (float)2000000123L - (float)2000000000L;
printf("%f\n", f);
If you have a 64-bit compiler, you can observe precision loss even with double precision:
double d = (double)9000000000000001234LL - (double)9000000000000000000LL;
printf("%f\n", d);
On my machine, these two fragments print 128 and 1024, respectively.
I'm not sure exactly which of the three concerns your compiler was trying to warn you about. #1 is the likeliest possibility. And you can see how the precision loss disappears if you convert after subtracting, instead of before:
f = 2000000123L - 2000000000L;
d = 9000000000000001234LL - 9000000000000000000LL;
or
f = (float)(2000000123L - 2000000000L);
d = (double)(9000000000000001234LL - 9000000000000000000LL);
Back when all we had was 32-bit longs and 64-bit doubles, this wasn't much of a concern in practice (because IEEE 754 doubles have something like 52 bits of precision). Now that 64-bit types are becoming commonplace, these kinds of warnings are becoming much more widespread. If you're satisfied that type double has enough precision for all your time-subtracting needs, you can use appropriate casts to double to silence the warnings. (And, again, here by "appropriate" we mean "after the subtraction".) If you want to be even safer, you could move to type long double instead. If your compiler supports it, and if it is indeed "longer" than regular double, it can truly reduce the loss-of-precision problem. (Here's the previous example using long double:
long double ld = (long double)9000000000000001234LL - (long double)9000000000000000000LL;
printf("%Lf\n", ld);
On my system this one prints 1234.)
But with all of that said, in this case, if you want to really make your life easier -- and, incidentally, address concern #3 at the same time -- you could and arguably should use a standard function for computing the differences. The standard function for subtracting two time_t values is difftime. (It's difftime's job to worry about all of these things, including the possibility that time_t doesn't represent seconds directly.) So you could write
return difftime(end->tv_sec - start->tv_sec) +
(double)(end->tv_usec - start->tv_usec) / 1e6;
although of course there's still the problem of the subseconds.
The very best solution would be a prewritten library function to subtract two timeval's, and you might want to spend some time looking for one of those.
The reason is called type safety. Not all types of expressions make sense in a working program. And type safety means that the compiler will refuse to permit unsafe, invalid, or inappropriate operations.
In the long run, a compiler that rejects such bad code saves the programmer effort, since it takes more time for a programmer to detect a problem without help than it does for a compiler.
C and C++ have a number of type safety features. Other programming languages have more, others less. Which simply means that some styles of programming are more effective in different programming languages.
Warnings are a little less prescriptive form of type safety - they cause the compilers to warn about suspicious things, rather than reject them entirely.
While these type are generally implemented with standard integral types, you can't assume this for portability. The standard library offers some convenient functions for conversions, etc. For example, if you want to compute the delay in between times you can use difftime which returns a double.

What is the biggest useful value of time_t?

It seems remarkably difficult to find out what a valid range of values is for time_t.
It is on some platforms 32 bit, on most 64 bit, and so can easily enough be set to LONG_MAX. However trying to then use that value doesn't really work properly. For instance you can't pass it to localtime and change it into a struct tm.
A quick test program to binary search the value tells me it is 67768036191676799. That corresponds to the end of year 2147483647, so that makes sense as a value. But is it this specified anywhere, and is there any reasonable, platform independent value for a maximum usable time_t?
Indeed, the specification for time_t and clock_t are implementation defined (C99 7.23.1).
This is one of those things where I would recommend not generating these values yourself, but rely on the implementation to create them for you such as with mktime(), and use the struct tm for directly manipulating the time. -1 is the only value of time_t that is a "good" value you can use on your own.
I would specifically suggest that you not treat it as a 32 bit value of any type, as jgm suggests. You just never know if some strange embedded compiler will want to use a 16 bit time or 18 or who knows.
The safest way to use it is as a 32-bit signed, as long as you're happy with it not working 25 years from now.
Otherwise you're going to have to test the type yourself on whichever platforms you run on and act accordingly.
tm_year has type int, so if you will be converting to struct tm, the largest meaningful time_t is the value corresponding to year INT_MAX.

Resources