in Source/Shared/http_utils.cpp [512:718]
datetime datetime::from_string(const xsapi_internal_string& dateString, date_format format)
{
// avoid floating point math to preserve precision
uint64_t ufrac_second = 0;
#ifdef _WIN32
datetime result;
if (format == RFC_1123)
{
SYSTEMTIME sysTime = { 0 };
xsapi_internal_string month(3, '\0');
xsapi_internal_string unused(3, '\0');
const char* formatString = "%3c, %2d %3c %4d %2d:%2d:%2d %3c";
auto n = sscanf_s(dateString.c_str(), formatString,
unused.data(), unused.size(),
&sysTime.wDay,
month.data(), month.size(),
&sysTime.wYear,
&sysTime.wHour,
&sysTime.wMinute,
&sysTime.wSecond,
unused.data(), unused.size());
if (n == 8)
{
xsapi_internal_string monthnames[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
auto loc = std::find_if(monthnames, monthnames + 12, [&month](const xsapi_internal_string& m) { return m == month; });
if (loc != monthnames + 12)
{
sysTime.wMonth = (short)((loc - monthnames) + 1);
if (system_type_to_datetime(&sysTime, ufrac_second, &result))
{
return result;
}
}
}
}
else if (format == ISO_8601)
{
// Unlike FILETIME, SYSTEMTIME does not have enough precision to hold seconds in 100 nanosecond
// increments. Therefore, start with seconds and milliseconds set to 0, then add them separately
// Try to extract the fractional second from the timestamp
xsapi_internal_string input;
extract_fractional_second(dateString, input, ufrac_second);
{
SYSTEMTIME sysTime = { 0 };
const char* formatString = "%4d-%2d-%2dT%2d:%2d:%2dZ";
auto n = sscanf_s(input.c_str(), formatString,
&sysTime.wYear,
&sysTime.wMonth,
&sysTime.wDay,
&sysTime.wHour,
&sysTime.wMinute,
&sysTime.wSecond);
if (n == 3 || n == 6)
{
if (system_type_to_datetime(&sysTime, ufrac_second, &result))
{
return result;
}
}
}
{
SYSTEMTIME sysTime = { 0 };
DWORD date = 0;
const char* formatString = "%8dT%2d:%2d:%2dZ";
auto n = sscanf_s(input.c_str(), formatString,
&date,
&sysTime.wHour,
&sysTime.wMinute,
&sysTime.wSecond);
if (n == 1 || n == 4)
{
sysTime.wDay = date % 100;
date /= 100;
sysTime.wMonth = date % 100;
date /= 100;
sysTime.wYear = (WORD)date;
if (system_type_to_datetime(&sysTime, ufrac_second, &result))
{
return result;
}
}
}
{
SYSTEMTIME sysTime = { 0 };
GetSystemTime(&sysTime); // Fill date portion with today's information
sysTime.wSecond = 0;
sysTime.wMilliseconds = 0;
const char* formatString = "%2d:%2d:%2dZ";
auto n = sscanf_s(input.c_str(), formatString,
&sysTime.wHour,
&sysTime.wMinute,
&sysTime.wSecond);
if (n == 3)
{
if (system_type_to_datetime(&sysTime, ufrac_second, &result))
{
return result;
}
}
}
}
return datetime();
#else
xsapi_internal_string input(dateString);
struct tm output = tm();
if (format == RFC_1123)
{
strptime(input.data(), "%a, %d %b %Y %H:%M:%S GMT", &output);
}
else
{
// Try to extract the fractional second from the timestamp
xsapi_internal_string input;
extract_fractional_second(dateString, input, ufrac_second);
auto result = strptime(input.data(), "%Y-%m-%dT%H:%M:%SZ", &output);
if (result == nullptr)
{
result = strptime(input.data(), "%Y%m%dT%H:%M:%SZ", &output);
}
if (result == nullptr)
{
// Fill the date portion with the epoch,
// strptime will do the rest
memset(&output, 0, sizeof(struct tm));
output.tm_year = 70;
output.tm_mon = 1;
output.tm_mday = 1;
result = strptime(input.data(), "%H:%M:%SZ", &output);
}
if (result == nullptr)
{
result = strptime(input.data(), "%Y-%m-%d", &output);
}
if (result == nullptr)
{
result = strptime(input.data(), "%Y%m%d", &output);
}
if (result == nullptr)
{
return datetime();
}
}
#if (defined(ANDROID) || defined(__ANDROID__))
// HACK: The (nonportable?) POSIX function timegm is not available in
// bionic. As a workaround[1][2], we set the C library timezone to
// UTC, call mktime, then set the timezone back. However, the C
// environment is fundamentally a shared global resource and thread-
// unsafe. We can protect our usage here, however any other code might
// manipulate the environment at the same time.
//
// [1] http://linux.die.net/man/3/timegm
// [2] http://www.gnu.org/software/libc/manual/html_node/Broken_002ddown-Time.html
time_t time;
static std::mutex env_var_lock;
{
std::lock_guard<std::mutex> lock(env_var_lock);
xsapi_internal_string prev_env;
auto prev_env_cstr = getenv("TZ");
if (prev_env_cstr != nullptr)
{
prev_env = prev_env_cstr;
}
setenv("TZ", "UTC", 1);
time = mktime(&output);
if (prev_env_cstr)
{
setenv("TZ", prev_env.c_str(), 1);
}
else
{
unsetenv("TZ");
}
tzset();
}
#else
time_t time = timegm(&output);
#endif
struct timeval tv = timeval();
tv.tv_sec = time;
auto result = timeval_to_datetime(tv);
// fractional seconds are already in correct format so just add them.
result = result + ufrac_second;
return result;
#endif
}