lib/core/CStrPTime_Windows.cc (101 lines of code) (raw):
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the following additional limitation. Functionality enabled by the
* files subject to the Elastic License 2.0 may only be used in production when
* invoked by an Elasticsearch process with a license key installed that permits
* use of machine learning features. You may not use this file except in
* compliance with the Elastic License 2.0 and the foregoing additional
* limitation.
*/
#include <core/CStrPTime.h>
#include <core/CStringUtils.h>
#include <core/CTimezone.h>
#include <string>
#include <ctype.h>
#include <string.h>
// We don't have a header for this on Windows, so declare it here
extern "C" char* strptime(const char* buf, const char* fmt, struct tm* tm);
namespace ml {
namespace core {
// Our Windows strptime() implementation supports the %z and %Z time formats.
// However, on Windows struct tm doesn't have any members for GMT offset that
// we could set, so strptime() doesn't make any changes to the output based
// on these flags.
//
// Therefore, this small extension to strptime() allows it to work with %z
// when the %z is the last non-whitespace in the format specifier.
// Realistically (or at least hopefully), the %z will always come at the end
// of a date/time.
//
// Also, since strptime() uses the C runtime globals _tzname[0] and _tzname[1],
// whereas we might want to use a different timezone, we replace %Z in the
// format string with a string obtained from the CTimezone singleton.
char* CStrPTime::strPTime(const char* buf, const char* format, struct tm* tm) {
// If any of the inputs are NULL then do nothing
if (buf == 0 || format == 0 || tm == 0) {
return 0;
}
std::string adjFormat(format);
// Replace %Z first if present
size_t tznamePos(adjFormat.find("%Z"));
if (tznamePos != std::string::npos) {
// Find the corresponding place in the buffer
char* excess(CStrPTime::strPTime(buf, adjFormat.substr(0, tznamePos).c_str(), tm));
if (excess == 0) {
return 0;
}
// Skip leading whitespace
while (::isspace(static_cast<unsigned char>(*excess))) {
++excess;
}
// Only GMT and the standard and daylight saving timezone names for the
// current timezone are supported, as per the strptime() man page
std::string possTzName(excess);
if (possTzName.find("GMT") == 0) {
adjFormat.replace(tznamePos, 2, "GMT");
} else {
CTimezone& tz = CTimezone::instance();
std::string stdAbbrev(tz.stdAbbrev());
if (possTzName.find(stdAbbrev) == 0) {
adjFormat.replace(tznamePos, 2, stdAbbrev);
} else {
std::string dstAbbrev(tz.dstAbbrev());
if (possTzName.find(dstAbbrev) == 0) {
adjFormat.replace(tznamePos, 2, dstAbbrev);
} else {
return 0;
}
}
}
}
// Check if the format specifier includes a %z
size_t zPos(adjFormat.find("%z"));
if (zPos != std::string::npos) {
// If there's anything except whitespace after the
// %z it's too complicated
if (adjFormat.find_first_not_of(CStringUtils::WHITESPACE_CHARS, zPos + 2) !=
std::string::npos) {
return 0;
}
adjFormat.erase(zPos);
}
char* excess(::strptime(buf, adjFormat.c_str(), tm));
// We only have more work to do if %z was in the string, and
// the basic strptime() call worked
if (excess != 0 && zPos != std::string::npos) {
// Skip leading whitespace
while (::isspace(static_cast<unsigned char>(*excess))) {
++excess;
}
// We expect something along the lines of +0000 or
// -0500, i.e. a plus or minus sign followed by 4 digits
core_t::TTime sign(0);
if (*excess == '+') {
sign = 1;
} else if (*excess == '-') {
sign = -1;
} else {
return 0;
}
++excess;
core_t::TTime hour(0);
if (*excess >= '0' && *excess <= '2') {
hour += (*excess - '0') * 10;
} else {
return 0;
}
++excess;
if (*excess >= '0' && *excess <= '9') {
hour += (*excess - '0');
} else {
return 0;
}
++excess;
core_t::TTime minute(0);
if (*excess >= '0' && *excess <= '5') {
minute += (*excess - '0') * 10;
} else {
return 0;
}
++excess;
if (*excess >= '0' && *excess <= '9') {
minute += (*excess - '0');
} else {
return 0;
}
++excess;
// Now we know how many minutes and hours ahead or behind GMT the
// time we just parsed was explicitly specified to be, so convert
// the struct tm on the basis that it's GMT and then subtract the
// explicit adjustment.
core_t::TTime utcTime(::_mkgmtime(tm));
utcTime -= sign * minute * 60;
utcTime -= sign * hour * 60 * 60;
CTimezone& tz = CTimezone::instance();
if (tz.utcToLocal(utcTime, *tm) == false) {
return 0;
}
}
return excess;
}
}
}