Search code examples
c++c++11time-t

write a std::string to comparable *time* parser which may fail


I know that size_t is unsigned and hence negative values are not allowed, time_t is to the best of my knowledge signed, so I am allowed to assign -1 to a size_t. However, for time_t I'm not entirely sure. If I follow the definition through the header files I end up here:

typedef __time_t time_t;

, then here:

__STD_TYPE __TIME_T_TYPE __time_t;  /* Seconds since the Epoch.  */

and finally

#define __TIME_T_TYPE       __SYSCALL_SLONG_TYPE

I'm not too sure what a __SYSCALL_SLONG_TYPE is, but I guess it is a signed long. Unfortunately, even after doing this trace, I can only hope that other c++11 platforms have the same implementation. I'm still not sure that this will be legal and well-defined for all of them:

size_t foo = -1;

Of course it makes sense to make a time_t signed, since one can have negative time offsets, for example to model time zones. But on the other hand it makes sense to have it unsigned, because there are a lot of seconds to count after 1970. So common sense goes both ways :) Googling on time_t returned this:

"For historical reasons, it is generally implemented as an integral value representing the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC (i.e., a unix timestamp). Although libraries may implement this type using alternative time representations."

Source: http://www.cplusplus.com/reference/ctime/time_t/

and on the same page: "Portable programs should not use values of this type directly, but always rely on calls to elements of the standard library to translate them to portable types."

So time_t is not at all well-defined on all systems, however, time() returns a time_t: so I wouldn't be the first to export it in an interface. What other type should I use?

I'm asking for these two reasons:

  • Speed: Using a struct, like struct tm is slow, as it has more bytes and hence copying it or comparing it with another struct tm will be slower than doing the same with a long.
  • Ordering: I want to have a fast data type for dates, which allows me to do a < b to figure out which of the dates a and b comes first.

Question: Since time_t is not a well-defined time representation on all platforms, what kind of data type that is (1) fast and (2) comparable can I use that is portable?

For example, regarding the ordering of time_t:

#include <iostream>
#include <ctime>
#include <cstring>

int main() {
    struct tm tm_a, tm_b;
    memset(&tm_a, 0, sizeof(struct tm));
    memset(&tm_b, 0, sizeof(struct tm));
    if(strptime("2014-01-01 12:00:00", "%Y-%m-%d %H:%M:%s", &tm_a) && strptime("2014-01-01 11:59:59", "%Y-%m-%d %H:%M:%s", &tm_b)) {
        if(mktime(&tm_a) > mktime(&tm_b)) std::cout << "time_t ordering OK." << std::endl;
        else std::cout << "time_t ordering tainted" << std::endl;
    } else std::cout << "failed to parse time" << std::endl;
    return 0;
}

Will time_t be untainted on all platforms?


Solution

  • (time_t)(-1) already means "this is not a valid time", regardless of what time_t actually is. The standard function mktime returns this value on error, so can you.

    The C and C++ standards say nothing about encoding of time in time_t. The standard function difftime computes time difference in seconds, as a double, between two times represented as time_t, and that's the only blessed way to order time_t values chronologically. However, the POSIX standard specifies that time_t is the time in seconds, so on a POSIX system it is OK to compare time_t directly with <.

    Note that both C and POSIX permit time_t to be any arithmetic type (not just integral).