5

Lets say we have a text file and read some timestamp from there into a local variable "sTime":

std::string sTime = "1440966379" // this value has been read from a file.
std::time_t tTime = ? // this instance of std::time_t shall be assigned the above value.

How do I convert this string properly into std::time assuming:

  1. We may use STL means only (no boost).
  2. We use the C++11 standard
  3. We don't know which CPU architecture/OS we're using (it should work cross plattform)
  4. We can not make any (static) assumptions on how time_t is internally defined. Of course we know that in most cases it will be an integral type, probably of 32 or 64 bit length, but according to cppreference.com the actual typedef of time_t is not specified. So atoi, atol, atoll, strtoul ... etc. are out of question at least until we have made sure by other means that we actually did pick the correct one out of those possible candidates.
norritt
  • 345
  • 4
  • 16
  • How has this string been created? / What does it represent? – dyp Aug 30 '15 at 20:55
  • It is a [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time) counting the seconds since Thursday, Jan. 1970. It has been created on a linux bash using the command "date +%s". It should be equal to the internal value std::time_t will have after proper conversion. – norritt Aug 30 '15 at 21:00
  • So we can assume that `std::time_t` actually contains a UNIX timestamp? If so, the question basically is *how do I parse an arithmetic value of the range of `std::time_t`*? – dyp Aug 30 '15 at 21:02
  • Not quite out input sTime is a string which is not an arithmetic value but a sequence of characters that represent an arithmetic value. So the question is how do I take a string value and convert it properly into a std::time_t. Edit: According the first question in your comment: yes we can assume it is a UNIX timestamp any conversion we might apply will probably provide some error handling/exception mechanism anyways. – norritt Aug 30 '15 at 21:06
  • Yes, sorry for the inaccurate wording. Since you know that the output of `date +%s` is always an integer (is it?), you could parse these characters (ASCII?) to an `uintmax_t` via the `scanf` family or using an `unsigned long long` and `istream::operator>>`, then cast from that to `time_t`. Getting more generic is difficult I think, e.g. on systems where `uintmax_t` is less than 64 bit but `time_t` is larger than `uintmax_t` (e.g. because `time_t` is a `double`); or if you need more than `uintmax_t` for the time stamps. – dyp Aug 30 '15 at 21:16
  • I'd probably just `istream::operator>>` into a `time_t` and let overload resolution sort things out. If there is no matching `operator>>`, you'll get a compile-time error. (Note however that `istream::operator>>` is not required to interpret the input as ASCII, so you should ensure that the file format fits to the `num_get` facility of the used locale). – dyp Aug 30 '15 at 21:19
  • I see no reason why this can't be solved with `std::time_t tTime = std::stoll(sTime);` – user4581301 Aug 30 '15 at 21:22
  • Yes you excatly understood the problem. And the difficulty you're describing is actually my motivation for asking this. One problem in my case is that reading the string data from the file and the conversion to the actual type are seperated so I can't convert on the fly using the istream operator. Also I feel like there should be some clean solution sth. like the non existent: "std::time_t::make_time(std::string)". Which actually does some checking on what time_t really is in the current environment and considering that when doing the conversion rather than assuming uint or ulong. – norritt Aug 30 '15 at 21:24
  • @user4581301: I'd be willing to accept that as a valid answer if there is some hard evidence that std::time_t tTime = std::stoll(sTime); is going to always work. But according to cppreference (see link in the question) time_t is not guarantueed to be equal or even big enough to store long long. – norritt Aug 30 '15 at 21:27
  • @norritt If `time_t` is *too small*, then there can't be a solution, assuming you want to support 64-bit time stamps. This can be `static_assert`ed, so that the program can't compile on such a system. – dyp Aug 30 '15 at 21:30
  • Agreed, you'll have a problem with a 32 bit time_t in another 20 years or so, but there is nothing that can save you from that. – user4581301 Aug 30 '15 at 21:30
  • Other than not using time_t, I suppose. Have you taken a look at the suitability of [std::chrono::time_point](http://en.cppreference.com/w/cpp/chrono/time_point)? – user4581301 Aug 30 '15 at 21:35
  • @user4581301, dyp: Yes the 32 Bit overflow will be inevitable. But what about the 32Bit architectures that might use long or ulong rather then long long as time_t, if we convert using stoll we're assigning a value to a variable which is not big enough to hold it. If we play it safe and convert to long the code will break in the aforementioned 20 years. So what I've been searching for is some official/trustworthy source stating for that case "Use stoll(sTime) to convert string to time_t" its safe even on 32Bit architectures. – norritt Aug 30 '15 at 21:48
  • @user4581301: I always considered chorono rather for hight precision purposes. I this case I'll have only to deal with a time resolution down to a second. I'll look into it but chrono might be overkill. – norritt Aug 30 '15 at 21:51

1 Answers1

7

This will keep your time in a standards-approved format:

Need #include <chrono>

std::string sTime = "1440966379"; // this value has been read from a file.

std::chrono::system_clock::time_point newtime(std::chrono::seconds(std::stoll(sTime)));
// this gets you out to a minimum of 35 bits. That leaves fixing the overflow in the 
// capable hands of Misters Spock and Scott. Trust me. They've had worse.

From there you can do arithmetic and compares on time_points.

Dumping it back out to a POSIX timestamp:

const std::chrono::system_clock::time_point epoch = std::chrono::system_clock::from_time_t(0);
// 0 is the same in both 32 and 64 bit time_t, so there is no possibility of overflow here
auto delta = newtime - epoch;
std::cout << std::chrono::duration_cast<std::chrono::seconds>(delta).count();

And another SO question deals with getting formatted strings back out: How to convert std::chrono::time_point to std::tm without using time_t?

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Very nice answer user, your proposal works nicely and according to the documentation on cppreference it clears out all assumptions according the acutal type of std::time_t. Since the documentation states that chrono::seconds takes an argument of _at least_ 35 Bits it seems to be safe to work with long long values obtained by stoll, even on 32 Bit systems as long as chrono is available. – norritt Aug 30 '15 at 23:20