3

std::gmtime is failing for me when I pass it the max possible std::time_t. See the following test:

std::cout   << "sizeof(std::time_t): " << sizeof(std::time_t) << '\n'
            << "sizeof(long long): " << sizeof(long long) << '\n'
            << "sizeof(unsigned int): " << sizeof(unsigned int) << "\n\n";

auto testgmtime = [](std::time_t time)
{
    std::tm* ptm = std::gmtime(&time);
    std::cout << (ptm ? "succeeded\n" : "failed\n");
};

testgmtime(std::numeric_limits<std::time_t>::max());
testgmtime(std::numeric_limits<long long>::max());
testgmtime(std::numeric_limits<long long>::max()-1);
testgmtime(static_cast<std::time_t>(std::numeric_limits<unsigned int>::max())*2);
testgmtime(std::numeric_limits<int>::max());

Output:

sizeof(std::time_t): 8
sizeof(long long): 8
sizeof(unsigned int): 4

failed
failed
failed
succeeded
succeeded

I'm using VS2012. Is there a standard limit I'm not seeing or is this VS being crappy? What alternative can I use that works?

David
  • 27,652
  • 18
  • 89
  • 138

2 Answers2

2

VS2012 is not being crappy. Do the math.

If time_t is a 64-bit integer, then the maximum number of seconds in it is 264-1 if it's an unsigned integer or 263-1 if it's a signed integer.

Roughly, 263 seconds corresponds to 263/(60*60*24*365) ≈ 2.9*1011 years.

time_t's tm_year is a 32-bit int, whose maximum value is 231-1 ≈ 2.1*109.

Clearly, you can't store ~2.9*1011 in a 32-bit signed integer. And gmtime() rightfully fails when you ask it to.

You may be able to modify my implementation of gmtime() in this answer to handle very large values. You'll need to workaround the overflow in SecondsSinceEpoch + 11644473600ULL and change some ints and uints to uint64s.

Community
  • 1
  • 1
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • Thanks, that does explain the very high numbers. But `(std::numeric_limits::max()-1)/10000000` fails too, which would only be a range of ~29247years, so does a range of 2924 years, and finally a range of 292 works. I don't know where it stops exactly but this thing fails at between ~300 and ~3000 years. I guess I'll have to write my own like you suggest – David Jul 15 '12 at 13:08
  • You can do a binary search to determine the cut-off date/time. I'm curious to know what it is. It might reveal the logic. – Alexey Frunze Jul 15 '12 at 19:44
  • ~300 to ~3000 years will take 33 to 37 bits. I wonder if the implementation is basically 32-bit despite `time_t` being 64-bit (note that you can choose `time_t` size). – Alexey Frunze Jul 16 '12 at 00:53
  • The limit is 31 Dec 3000, according to http://msdn.microsoft.com/en-us/library/0z9czt0w(v=vs.80).aspx – user9876 Jan 08 '13 at 14:36
1

C++ only says that gmtime may fail. The standard that specifies the behavior of gmtime on large input is POSIX:

If an error is detected, gmtime() shall return a null pointer and set errno to indicate the error.

ERRORS

The gmtime() and gmtime_r() functions shall fail if:

[EOVERFLOW] The result cannot be represented.

It doesn't directly say what the limit is, although it does say that if the seconds since the Epoch are negative, their relationship to the actual time is undefined.

As an alternative, try boost

auto testgmtime = [](std::time_t time)
{
    std::cout << boost::posix_time::from_time_t(time) << '\n';
};

(although on my linux setup, from_time_t() appears to hit the ceiling earlier than gmtime)

Cubbi
  • 46,567
  • 13
  • 103
  • 169