Recently I was looking for a convenient way, to parse a string representation of DateTime to integral value representing epoch time. And I couldn't find an existing class to make that easily, for example giving the string as an input to constructor. After about a couple of hours of research I found a brief way to achieve that using a mix of C and C++ tools.
First of all, there is an std::tm struct defined in <ctime> header, which represents a calendar date and a time with all the relevant members, the date is reckoned from start of year 1900, thus, if we try to represent current year (2014) the year field of tm structure will be equal to 114.
Second, in the same <ctime> header there is an auxiliary function, mktime, which accepts a pointer to std::tm object and returns std::time_t. The latter is a typedef not defined by C++ standard specification. However, it is almost always an integral type, so we can store it in an std::int64_t to be on the safe side. std::time_t represents the number of seconds elapsed since epoch (Jan 1 1900, 00:00:00), the same as POSIX time.
Alright, now that we have found a good type to store date and time in, we need the last step, getting all this info from a string, and that is where the most difficult part comes. std::tm is a POD struct, so no parse methods for users. std::time_t is just a typedef of a built-in type, so again, not much use.
Fortunately, there is a great set of tools that help developers deal with streams, and one of them is i/o manipulators. The one that we are going to use here is std::get_time from <iomanip> header. If passed as an argument to input stream's operator>>, it formats the stream buffer with the provided format string and writes the resulting dateTime into an std::tm structure. And that solved my problem.
Enough text, let's look into the code:
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
// Converts UTC time string to a time_t
value.
std::time_t getEpochTime(const std::wstring& dateTime)
{
// Let's consider we
are getting all the input in
// this format:
'2014-07-25T20:17:22Z' (T denotes
// start of Time part,
Z denotes UTC zone).
// A better approach
would be to pass in the format as well.
static const std::wstring dateTimeFormat{ L"%Y-%m-%dT%H:%M:%SZ" };
// Create a stream
which we will use to parse the string,
// which we provide to
constructor of stream to fill the buffer.
std::wistringstream ss{ dateTime };
// Create a tm object
to store the parsed date and time.
std::tm dt;
// Now we read from
buffer using get_time manipulator
// and formatting the
input appropriately.
ss
>> std::get_time(&dt, dateTimeFormat.c_str());
// Convert the tm
structure to time_t value and return.
return std::mktime(&dt);
}
int main(void)
{
// Test to see if our
function works properly.
std::wstring dateTimeStr{ L"2014-07-25T20:17:22Z" };
std::wcout
<< L"The
epoch time value representing " << dateTimeStr
<<
L"
is "
<< getEpochTime(dateTimeStr) << std::endl;
return 0;
}
If you want to verify that get_time correctly populated the tm object, just add a couple of outputs after operator>> line, something like this:
std::wcout
<< dt.tm_year << std::endl;
std::wcout
<< dt.tm_mon << std::endl;
std::wcout
<< dt.tm_mday << std::endl;
std::wcout
<< dt.tm_hour << std::endl;
std::wcout
<< dt.tm_min << std::endl;
std::wcout
<< dt.tm_sec << std::endl;
To convert the epoch time value back to std::tm structure you can use either of std::localtime and std::gmtime functions from <ctime> header. And finally, if you want to convert std::tm to string, there is an analogous manipulator std::put_time which is used with output stream's operator<<.
Thanks for reading, I hope some people will use this approach when they encounter the same problem.