/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include #include #include #include #include static const char* CLASS_TAG = "DateTime"; static const char* RFC822_DATE_FORMAT_STR_MINUS_Z = "%a, %d %b %Y %H:%M:%S"; static const char* RFC822_DATE_FORMAT_STR_WITH_Z = "%a, %d %b %Y %H:%M:%S %Z"; static const char* ISO_8601_LONG_DATE_FORMAT_STR = "%Y-%m-%dT%H:%M:%SZ"; static const char* ISO_8601_LONG_BASIC_DATE_FORMAT_STR = "%Y%m%dT%H%M%SZ"; using namespace Aws::Utils; std::tm CreateZeroedTm() { std::tm timeStruct; timeStruct.tm_hour = 0; timeStruct.tm_isdst = -1; timeStruct.tm_mday = 0; timeStruct.tm_min = 0; timeStruct.tm_mon = 0; timeStruct.tm_sec = 0; timeStruct.tm_wday = 0; timeStruct.tm_yday = 0; timeStruct.tm_year = 0; return timeStruct; } //Get the 0-6 week day number from a string representing WeekDay. Case insensitive and will stop on abbreviation static int GetWeekDayNumberFromStr(const char* timeString, size_t startIndex, size_t stopIndex) { if(stopIndex - startIndex < 3) { return -1; } size_t index = startIndex; char c = timeString[index]; char next = 0; //it's ugly but this should compile down to EXACTLY 3 comparisons and no memory allocations switch(c) { case 'S': case 's': next = timeString[++index]; switch(next) { case 'A': case 'a': next = timeString[++index]; switch (next) { case 'T': case 't': return 6; default: return -1; } case 'U': case 'u': next = timeString[++index]; switch (next) { case 'N': case 'n': return 0; default: return -1; } default: return -1; } case 'T': case 't': next = timeString[++index]; switch (next) { case 'H': case 'h': next = timeString[++index]; switch(next) { case 'U': case 'u': return 4; default: return -1; } case 'U': case 'u': next = timeString[++index]; switch(next) { case 'E': case 'e': return 2; default: return -1; } default: return -1; } case 'M': case 'm': next = timeString[++index]; switch(next) { case 'O': case 'o': next = timeString[++index]; switch (next) { case 'N': case 'n': return 1; default: return -1; } default: return -1; } case 'W': case 'w': next = timeString[++index]; switch (next) { case 'E': case 'e': next = timeString[++index]; switch (next) { case 'D': case 'd': return 3; default: return -1; } default: return -1; } case 'F': case 'f': next = timeString[++index]; switch (next) { case 'R': case 'r': next = timeString[++index]; switch (next) { case 'I': case 'i': return 5; default: return -1; } default: return -1; } default: return -1; } } //Get the 0-11 monthly number from a string representing Month. Case insensitive and will stop on abbreviation static int GetMonthNumberFromStr(const char* timeString, size_t startIndex, size_t stopIndex) { if (stopIndex - startIndex < 3) { return -1; } size_t index = startIndex; char c = timeString[index]; char next = 0; //it's ugly but this should compile down to EXACTLY 3 comparisons and no memory allocations switch (c) { case 'M': case 'm': next = timeString[++index]; switch (next) { case 'A': case 'a': next = timeString[++index]; switch (next) { case 'Y': case 'y': return 4; case 'R': case 'r': return 2; default: return -1; } default: return -1; } case 'A': case 'a': next = timeString[++index]; switch (next) { case 'P': case 'p': next = timeString[++index]; switch (next) { case 'R': case 'r': return 3; default: return -1; } case 'U': case 'u': next = timeString[++index]; switch (next) { case 'G': case 'g': return 7; default: return -1; } default: return -1; } case 'J': case 'j': next = timeString[++index]; switch (next) { case 'A': case 'a': next = timeString[++index]; switch (next) { case 'N': case 'n': return 0; default: return -1; } case 'U': case 'u': next = timeString[++index]; switch (next) { case 'N': case 'n': return 5; case 'L': case 'l': return 6; default: return -1; } default: return -1; } case 'F': case 'f': next = timeString[++index]; switch (next) { case 'E': case 'e': next = timeString[++index]; switch (next) { case 'B': case 'b': return 1; default: return -1; } default: return -1; } case 'S': case 's': next = timeString[++index]; switch (next) { case 'E': case 'e': next = timeString[++index]; switch (next) { case 'P': case 'p': return 8; default: return -1; } default: return -1; } case 'O': case 'o': next = timeString[++index]; switch (next) { case 'C': case 'c': next = timeString[++index]; switch (next) { case 'T': case 't': return 9; default: return -1; } default: return -1; } case 'N': case 'n': next = timeString[++index]; switch (next) { case 'O': case 'o': next = timeString[++index]; switch (next) { case 'V': case 'v': return 10; default: return -1; } default: return -1; } case 'D': case 'd': next = timeString[++index]; switch (next) { case 'E': case 'e': next = timeString[++index]; switch (next) { case 'C': case 'c': return 11; default: return -1; } default: return -1; } default: return -1; } } // Ensure local classes with generic names have internal linkage namespace { class DateParser { public: DateParser(const char* toParse) : m_error(false), m_toParse(toParse), m_utcAssumed(true) { m_parsedTimestamp = CreateZeroedTm(); memset(m_tz, 0, 7); } virtual ~DateParser() = default; virtual void Parse() = 0; bool WasParseSuccessful() const { return !m_error; } std::tm& GetParsedTimestamp() { return m_parsedTimestamp; } bool ShouldIAssumeThisIsUTC() const { return m_utcAssumed; } const char* GetParsedTimezone() const { return m_tz; } protected: bool m_error; const char* m_toParse; std::tm m_parsedTimestamp; bool m_utcAssumed; // The size should be at least one byte greater than the maximum possible size so that we could use the last char to indicate the end of the string. char m_tz[7]; }; static const int MAX_LEN = 100; //Before you send me hate mail because I'm doing this manually, I encourage you to try using std::get_time on all platforms and getting //uniform results. Timezone information doesn't parse on Windows and it hardly even works on GCC 4.9.x. This is the only way to make sure //the standard is parsed correctly. strptime isn't available one Windows. This code gets hit pretty hard during http serialization/deserialization //as a result I'm going for no dynamic allocations and linear complexity class RFC822DateParser : public DateParser { public: RFC822DateParser(const char* toParse) : DateParser(toParse), m_state(0) { } /** * Really simple state machine for the format %a, %d %b %Y %H:%M:%S %Z */ void Parse() override { size_t len = strlen(m_toParse); //DOS check if (len > MAX_LEN) { AWS_LOGSTREAM_WARN(CLASS_TAG, "Incoming String to parse too long with length: " << len) m_error = true; return; } size_t index = 0; size_t stateStartIndex = 0; int finalState = 8; while(m_state <= finalState && !m_error && index < len) { char c = m_toParse[index]; switch (m_state) { case 0: if(c == ',') { int weekNumber = GetWeekDayNumberFromStr(m_toParse, stateStartIndex, index + 1); if (weekNumber > -1) { m_state = 1; stateStartIndex = index + 1; m_parsedTimestamp.tm_wday = weekNumber; } else { m_error = true; } } else if(!isalpha(c)) { m_error = true; } break; case 1: if (isspace(c)) { m_state = 2; stateStartIndex = index + 1; } else { m_error = true; } break; case 2: if (isdigit(c)) { m_parsedTimestamp.tm_mday = m_parsedTimestamp.tm_mday * 10 + (c - '0'); } else if(isspace(c)) { m_state = 3; stateStartIndex = index + 1; } else { m_error = true; } break; case 3: if (isspace(c)) { int monthNumber = GetMonthNumberFromStr(m_toParse, stateStartIndex, index + 1); if (monthNumber > -1) { m_state = 4; stateStartIndex = index + 1; m_parsedTimestamp.tm_mon = monthNumber; } else { m_error = true; } } else if (!isalpha(c)) { m_error = true; } break; case 4: if (isspace(c) && index - stateStartIndex == 4) { m_state = 5; stateStartIndex = index + 1; m_parsedTimestamp.tm_year -= 1900; } else if (isspace(c) && index - stateStartIndex == 2) { m_state = 5; stateStartIndex = index + 1; m_parsedTimestamp.tm_year += 2000 - 1900; } else if (isdigit(c)) { m_parsedTimestamp.tm_year = m_parsedTimestamp.tm_year * 10 + (c - '0'); } else { m_error = true; } break; case 5: if(c == ':' && index - stateStartIndex == 2) { m_state = 6; stateStartIndex = index + 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_hour = m_parsedTimestamp.tm_hour * 10 + (c - '0'); } else { m_error = true; } break; case 6: if (c == ':' && index - stateStartIndex == 2) { m_state = 7; stateStartIndex = index + 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_min = m_parsedTimestamp.tm_min * 10 + (c - '0'); } else { m_error = true; } break; case 7: if (isspace(c) && index - stateStartIndex == 2) { m_state = 8; stateStartIndex = index + 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_sec = m_parsedTimestamp.tm_sec * 10 + (c - '0'); } else { m_error = true; } break; case 8: if ((isalnum(c) || c == '+' || c == '-') && (index - stateStartIndex < 5)) { m_tz[index - stateStartIndex] = c; } else { m_error = true; } break; default: m_error = true; break; } index++; } if (m_tz[0] != 0) { m_utcAssumed = IsUTCTimeZoneDesignator(m_tz); } m_error = (m_error || m_state != finalState); } int GetState() const { return m_state; } private: //Detects whether or not the passed in timezone string is a UTC zone. static bool IsUTCTimeZoneDesignator(const char* str) { size_t len = strlen(str); if (len < 3) { return false; } int index = 0; char c = str[index]; switch (c) { case 'U': case 'u': c = str[++index]; switch(c) { case 'T': case 't': c = str[++index]; switch(c) { case 'C': case 'c': return true; default: return false; } case 'C': case 'c': c = str[++index]; switch (c) { case 'T': case 't': return true; default: return false; } default: return false; } case 'G': case 'g': c = str[++index]; switch (c) { case 'M': case 'm': c = str[++index]; switch (c) { case 'T': case 't': return true; default: return false; } default: return false; } case '+': case '-': c = str[++index]; switch (c) { case '0': c = str[++index]; switch (c) { case '0': c = str[++index]; switch (c) { case '0': return true; default: return false; } default: return false; } default: return false; } case 'Z': return true; default: return false; } } int m_state; }; //Before you send me hate mail because I'm doing this manually, I encourage you to try using std::get_time on all platforms and getting //uniform results. Timezone information doesn't parse on Windows and it hardly even works on GCC 4.9.x. This is the only way to make sure //the standard is parsed correctly. strptime isn't available one Windows. This code gets hit pretty hard during http serialization/deserialization //as a result I'm going for no dynamic allocations and linear complexity class ISO_8601DateParser : public DateParser { public: ISO_8601DateParser(const char* stringToParse) : DateParser(stringToParse), m_state(0) { } //parses "%Y-%m-%dT%H:%M:%SZ or "%Y-%m-%dT%H:%M:%S.000Z" void Parse() override { size_t len = strlen(m_toParse); //DOS check if (len > MAX_LEN) { AWS_LOGSTREAM_WARN(CLASS_TAG, "Incoming String to parse too long with length: " << len) m_error = true; return; } size_t index = 0; size_t stateStartIndex = 0; const int finalState = 7; while (m_state <= finalState && !m_error && index < len) { char c = m_toParse[index]; switch (m_state) { case 0: if (c == '-' && index - stateStartIndex == 4) { m_state = 1; stateStartIndex = index + 1; m_parsedTimestamp.tm_year -= 1900; } else if (isdigit(c)) { m_parsedTimestamp.tm_year = m_parsedTimestamp.tm_year * 10 + (c - '0'); } else { m_error = true; } break; case 1: if (c == '-' && index - stateStartIndex == 2) { m_state = 2; stateStartIndex = index + 1; m_parsedTimestamp.tm_mon -= 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_mon = m_parsedTimestamp.tm_mon * 10 + (c - '0'); } else { m_error = true; } break; case 2: if (c == 'T' && index - stateStartIndex == 2) { m_state = 3; stateStartIndex = index + 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_mday = m_parsedTimestamp.tm_mday * 10 + (c - '0'); } else { m_error = true; } break; case 3: if (c == ':' && index - stateStartIndex == 2) { m_state = 4; stateStartIndex = index + 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_hour = m_parsedTimestamp.tm_hour * 10 + (c - '0'); } else { m_error = true; } break; case 4: if (c == ':' && index - stateStartIndex == 2) { m_state = 5; stateStartIndex = index + 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_min = m_parsedTimestamp.tm_min * 10 + (c - '0'); } else { m_error = true; } break; case 5: if ((c == 'Z' || c == '+' || c == '-' ) && (index - stateStartIndex == 2)) { m_tz[0] = c; m_state = 7; stateStartIndex = index + 1; } else if (c == '.' && index - stateStartIndex == 2) { m_state = 6; stateStartIndex = index + 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_sec = m_parsedTimestamp.tm_sec * 10 + (c - '0'); } else { m_error = true; } break; case 6: if ((c == 'Z' || c == '+' || c == '-' ) && (index - stateStartIndex >= 3) && (index - stateStartIndex <= 9)) { m_tz[0] = c; m_state = 7; stateStartIndex = index + 1; } else if(!isdigit(c)) { m_error = true; } break; case 7: if ((isdigit(c) || c == ':') && (index - stateStartIndex < 5)) { m_tz[1 + index - stateStartIndex] = c; } else { m_error = true; } break; default: m_error = true; break; } index++; } if (m_tz[0] != 0) { m_utcAssumed = IsUTCTimeZoneDesignator(m_tz); } m_error = (m_error || m_state != finalState); } private: //Detects whether or not the passed in timezone string is a UTC zone. static bool IsUTCTimeZoneDesignator(const char* str) { size_t len = strlen(str); if (len > 0) { if (len == 1 && str[0] == 'Z') { return true; } if (len == 6 && str[0] == '+' && str[1] == '0' && str[2] == '0' && str[3] == ':' && str[4] == '0' && str[5] == '0') { return true; } return false; } return false; } int m_state; }; class ISO_8601BasicDateParser : public DateParser { public: ISO_8601BasicDateParser(const char* stringToParse) : DateParser(stringToParse), m_state(0) { } //parses "%Y%m%dT%H%M%SZ or "%Y%m%dT%H%M%S000Z" void Parse() override { size_t len = strlen(m_toParse); //DOS check if (len > MAX_LEN) { AWS_LOGSTREAM_WARN(CLASS_TAG, "Incoming String to parse too long with length: " << len) m_error = true; return; } size_t index = 0; size_t stateStartIndex = 0; const int finalState = 7; while (m_state <= finalState && !m_error && index < len) { char c = m_toParse[index]; switch (m_state) { // On year: %Y case 0: if (isdigit(c)) { m_parsedTimestamp.tm_year = m_parsedTimestamp.tm_year * 10 + (c - '0'); if (index - stateStartIndex == 3) { m_state = 1; stateStartIndex = index + 1; m_parsedTimestamp.tm_year -= 1900; } } else { m_error = true; } break; // On month: %m case 1: if (isdigit(c)) { m_parsedTimestamp.tm_mon = m_parsedTimestamp.tm_mon * 10 + (c - '0'); if (index - stateStartIndex == 1) { m_state = 2; stateStartIndex = index + 1; m_parsedTimestamp.tm_mon -= 1; } } else { m_error = true; } break; // On month day: %d case 2: if (c == 'T' && index - stateStartIndex == 2) { m_state = 3; stateStartIndex = index + 1; } else if (isdigit(c)) { m_parsedTimestamp.tm_mday = m_parsedTimestamp.tm_mday * 10 + (c - '0'); } else { m_error = true; } break; // On hour: %H case 3: if (isdigit(c)) { m_parsedTimestamp.tm_hour = m_parsedTimestamp.tm_hour * 10 + (c - '0'); if (index - stateStartIndex == 1) { m_state = 4; stateStartIndex = index + 1; } } else { m_error = true; } break; // On minute: %M case 4: if (isdigit(c)) { m_parsedTimestamp.tm_min = m_parsedTimestamp.tm_min * 10 + (c - '0'); if (index - stateStartIndex == 1) { m_state = 5; stateStartIndex = index + 1; } } else { m_error = true; } break; // On second: %S case 5: if (isdigit(c)) { m_parsedTimestamp.tm_sec = m_parsedTimestamp.tm_sec * 10 + (c - '0'); if (index - stateStartIndex == 1) { m_state = 6; stateStartIndex = index + 1; } } else { m_error = true; } break; // On TZ: Z or 000Z case 6: if ((c == 'Z' || c == '+' || c == '-' ) && (index - stateStartIndex == 0 || index - stateStartIndex == 3)) { m_tz[0] = c; m_state = 7; stateStartIndex = index + 1; } else if (!isdigit(c) || index - stateStartIndex > 3) { m_error = true; } break; case 7: if ((isdigit(c) || c == ':') && (index - stateStartIndex < 5)) { m_tz[1 + index - stateStartIndex] = c; } else { m_error = true; } break; default: m_error = true; break; } index++; } if (m_tz[0] != 0) { m_utcAssumed = IsUTCTimeZoneDesignator(m_tz); } m_error = (m_error || m_state != finalState); } private: //Detects whether or not the passed in timezone string is a UTC zone. static bool IsUTCTimeZoneDesignator(const char* str) { size_t len = strlen(str); if (len > 0) { if (len == 1 && str[0] == 'Z') { return true; } if (len == 5 && str[0] == '+' && str[1] == '0' && str[2] == '0' && str[3] == '0' && str[4] == '0') { return true; } return false; } return false; } int m_state; }; } // namespace DateTime::DateTime(const std::chrono::system_clock::time_point& timepointToAssign) : m_time(timepointToAssign), m_valid(true) { } DateTime::DateTime(int64_t millisSinceEpoch) : m_valid(true) { std::chrono::duration timestamp(millisSinceEpoch); m_time = std::chrono::system_clock::time_point(timestamp); } DateTime::DateTime(double epoch_millis) : m_valid(true) { std::chrono::duration timestamp(epoch_millis); m_time = std::chrono::system_clock::time_point(std::chrono::duration_cast(timestamp)); } DateTime::DateTime(const Aws::String& timestamp, DateFormat format) : m_valid(true) { ConvertTimestampStringToTimePoint(timestamp.c_str(), format); } DateTime::DateTime(const char* timestamp, DateFormat format) : m_valid(true) { ConvertTimestampStringToTimePoint(timestamp, format); } DateTime::DateTime() : m_valid(true) { //init time_point to default by doing nothing. } DateTime& DateTime::operator=(const Aws::String& timestamp) { *this = DateTime(timestamp, DateFormat::AutoDetect); return *this; } DateTime& DateTime::operator=(double secondsMillis) { *this = DateTime(secondsMillis); return *this; } DateTime& DateTime::operator=(int64_t millisSinceEpoch) { *this = DateTime(millisSinceEpoch); return *this; } DateTime& DateTime::operator=(const std::chrono::system_clock::time_point& timepointToAssign) { *this = DateTime(timepointToAssign); return *this; } bool DateTime::operator == (const DateTime& other) const { return m_time == other.m_time; } bool DateTime::operator < (const DateTime& other) const { return m_time < other.m_time; } bool DateTime::operator > (const DateTime& other) const { return m_time > other.m_time; } bool DateTime::operator != (const DateTime& other) const { return m_time != other.m_time; } bool DateTime::operator <= (const DateTime& other) const { return m_time <= other.m_time; } bool DateTime::operator >= (const DateTime& other) const { return m_time >= other.m_time; } DateTime DateTime::operator +(const std::chrono::milliseconds& a) const { auto timepointCpy = m_time; timepointCpy += a; return DateTime(timepointCpy); } DateTime DateTime::operator -(const std::chrono::milliseconds& a) const { auto timepointCpy = m_time; timepointCpy -= a; return DateTime(timepointCpy); } Aws::String DateTime::ToLocalTimeString(DateFormat format) const { switch (format) { case DateFormat::ISO_8601: return ToLocalTimeString(ISO_8601_LONG_DATE_FORMAT_STR); case DateFormat::ISO_8601_BASIC: return ToLocalTimeString(ISO_8601_LONG_BASIC_DATE_FORMAT_STR); case DateFormat::RFC822: return ToLocalTimeString(RFC822_DATE_FORMAT_STR_WITH_Z); default: assert(0); return ""; } } Aws::String DateTime::ToLocalTimeString(const char* formatStr) const { struct tm localTimeStamp = ConvertTimestampToLocalTimeStruct(); char formattedString[100]; std::strftime(formattedString, sizeof(formattedString), formatStr, &localTimeStamp); return formattedString; } Aws::String DateTime::ToGmtString(DateFormat format) const { switch (format) { case DateFormat::ISO_8601: return ToGmtString(ISO_8601_LONG_DATE_FORMAT_STR); case DateFormat::ISO_8601_BASIC: return ToGmtString(ISO_8601_LONG_BASIC_DATE_FORMAT_STR); case DateFormat::RFC822: { //Windows erroneously drops the local timezone in for %Z Aws::String rfc822GmtString = ToGmtString(RFC822_DATE_FORMAT_STR_MINUS_Z); rfc822GmtString += " GMT"; return rfc822GmtString; } default: assert(0); return ""; } } Aws::String DateTime::ToGmtString(const char* formatStr) const { struct tm gmtTimeStamp = ConvertTimestampToGmtStruct(); char formattedString[100]; std::strftime(formattedString, sizeof(formattedString), formatStr, &gmtTimeStamp); return formattedString; } double DateTime::SecondsWithMSPrecision() const { std::chrono::duration timestamp(m_time.time_since_epoch()); return timestamp.count(); } int64_t DateTime::Seconds() const { auto timestamp = std::chrono::duration_cast(m_time.time_since_epoch()); return timestamp.count(); } int64_t DateTime::Millis() const { auto timestamp = std::chrono::duration_cast(m_time.time_since_epoch()); return timestamp.count(); } std::chrono::system_clock::time_point DateTime::UnderlyingTimestamp() const { return m_time; } int DateTime::GetYear(bool localTime) const { return GetTimeStruct(localTime).tm_year + 1900; } Month DateTime::GetMonth(bool localTime) const { return static_cast(GetTimeStruct(localTime).tm_mon); } int DateTime::GetDay(bool localTime) const { return GetTimeStruct(localTime).tm_mday; } DayOfWeek DateTime::GetDayOfWeek(bool localTime) const { return static_cast(GetTimeStruct(localTime).tm_wday); } int DateTime::GetHour(bool localTime) const { return GetTimeStruct(localTime).tm_hour; } int DateTime::GetMinute(bool localTime) const { return GetTimeStruct(localTime).tm_min; } int DateTime::GetSecond(bool localTime) const { return GetTimeStruct(localTime).tm_sec; } bool DateTime::IsDST(bool localTime) const { return GetTimeStruct(localTime).tm_isdst == 0 ? false : true; } DateTime DateTime::Now() { DateTime dateTime; dateTime.m_time = std::chrono::system_clock::now(); return dateTime; } int64_t DateTime::CurrentTimeMillis() { return Now().Millis(); } Aws::String DateTime::CalculateLocalTimestampAsString(const char* formatStr) { DateTime now = Now(); return now.ToLocalTimeString(formatStr); } Aws::String DateTime::CalculateGmtTimestampAsString(const char* formatStr) { DateTime now = Now(); return now.ToGmtString(formatStr); } Aws::String DateTime::CalculateGmtTimeWithMsPrecision() { auto now = DateTime::Now(); struct tm gmtTimeStamp = now.ConvertTimestampToGmtStruct(); char formattedString[100]; auto len = std::strftime(formattedString, sizeof(formattedString), "%Y-%m-%d %H:%M:%S", &gmtTimeStamp); if (len) { auto ms = now.Millis(); ms = ms - ms / 1000 * 1000; // calculate the milliseconds as fraction. formattedString[len++] = '.'; int divisor = 100; while(divisor) { auto digit = ms / divisor; formattedString[len++] = char('0' + digit); ms = ms - divisor * digit; divisor /= 10; } formattedString[len] = '\0'; } return formattedString; } int DateTime::CalculateCurrentHour() { return Now().GetHour(true); } double DateTime::ComputeCurrentTimestampInAmazonFormat() { return Now().SecondsWithMSPrecision(); } std::chrono::milliseconds DateTime::Diff(const DateTime& a, const DateTime& b) { auto diff = a.m_time - b.m_time; return std::chrono::duration_cast(diff); } std::chrono::milliseconds DateTime::operator-(const DateTime& other) const { auto diff = this->m_time - other.m_time; return std::chrono::duration_cast(diff); } void DateTime::ConvertTimestampStringToTimePoint(const char* timestamp, DateFormat format) { std::tm timeStruct; bool isUtc = true; switch (format) { case DateFormat::RFC822: { RFC822DateParser parser(timestamp); parser.Parse(); m_valid = parser.WasParseSuccessful(); isUtc = parser.ShouldIAssumeThisIsUTC(); timeStruct = parser.GetParsedTimestamp(); break; } case DateFormat::ISO_8601: { ISO_8601DateParser parser(timestamp); parser.Parse(); m_valid = parser.WasParseSuccessful(); isUtc = parser.ShouldIAssumeThisIsUTC(); timeStruct = parser.GetParsedTimestamp(); break; } case DateFormat::ISO_8601_BASIC: { ISO_8601BasicDateParser parser(timestamp); parser.Parse(); m_valid = parser.WasParseSuccessful(); isUtc = parser.ShouldIAssumeThisIsUTC(); timeStruct = parser.GetParsedTimestamp(); break; } case DateFormat::AutoDetect: { RFC822DateParser rfcParser(timestamp); rfcParser.Parse(); if(rfcParser.WasParseSuccessful()) { m_valid = true; isUtc = rfcParser.ShouldIAssumeThisIsUTC(); timeStruct = rfcParser.GetParsedTimestamp(); break; } ISO_8601DateParser isoParser(timestamp); isoParser.Parse(); if (isoParser.WasParseSuccessful()) { m_valid = true; isUtc = isoParser.ShouldIAssumeThisIsUTC(); timeStruct = isoParser.GetParsedTimestamp(); break; } ISO_8601BasicDateParser isoBasicParser(timestamp); isoBasicParser.Parse(); if (isoBasicParser.WasParseSuccessful()) { m_valid = true; isUtc = isoBasicParser.ShouldIAssumeThisIsUTC(); timeStruct = isoBasicParser.GetParsedTimestamp(); break; } m_valid = false; break; } default: assert(0); } if (m_valid) { std::time_t tt; if(isUtc) { tt = Aws::Time::TimeGM(&timeStruct); } else { assert(0); AWS_LOGSTREAM_WARN(CLASS_TAG, "Non-UTC timestamp detected. This is always a bug. Make the world a better place and fix whatever sent you this timestamp: " << timestamp) tt = std::mktime(&timeStruct); } m_time = std::chrono::system_clock::from_time_t(tt); } } tm DateTime::GetTimeStruct(bool localTime) const { return localTime ? ConvertTimestampToLocalTimeStruct() : ConvertTimestampToGmtStruct(); } tm DateTime::ConvertTimestampToLocalTimeStruct() const { std::time_t time = std::chrono::system_clock::to_time_t(m_time); struct tm localTimeStamp; Aws::Time::LocalTime(&localTimeStamp, time); return localTimeStamp; } tm DateTime::ConvertTimestampToGmtStruct() const { std::time_t time = std::chrono::system_clock::to_time_t(m_time); struct tm gmtTimeStamp; Aws::Time::GMTime(&gmtTimeStamp, time); return gmtTimeStamp; }