aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/utils/DateTimeCommon.cpp
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/utils/DateTimeCommon.cpp
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/utils/DateTimeCommon.cpp')
-rw-r--r--contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/utils/DateTimeCommon.cpp1502
1 files changed, 1502 insertions, 0 deletions
diff --git a/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/utils/DateTimeCommon.cpp b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/utils/DateTimeCommon.cpp
new file mode 100644
index 0000000000..b690c90c2d
--- /dev/null
+++ b/contrib/libs/aws-sdk-cpp/aws-cpp-sdk-core/source/utils/DateTimeCommon.cpp
@@ -0,0 +1,1502 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#include <aws/core/utils/DateTime.h>
+
+#include <aws/core/platform/Time.h>
+#include <aws/core/utils/memory/stl/AWSStringStream.h>
+#include <aws/core/utils/logging/LogMacros.h>
+#include <time.h>
+#include <cassert>
+#include <iostream>
+#include <cstring>
+
+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 monthy 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))
+ {
+ 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<int64_t, std::chrono::milliseconds::period> timestamp(millisSinceEpoch);
+ m_time = std::chrono::system_clock::time_point(timestamp);
+}
+
+DateTime::DateTime(double epoch_millis) : m_valid(true)
+{
+ std::chrono::duration<double, std::chrono::seconds::period> timestamp(epoch_millis);
+ m_time = std::chrono::system_clock::time_point(std::chrono::duration_cast<std::chrono::milliseconds>(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<double, std::chrono::seconds::period> timestamp(m_time.time_since_epoch());
+ return timestamp.count();
+}
+
+int64_t DateTime::Millis() const
+{
+ auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(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<Aws::Utils::Month>(GetTimeStruct(localTime).tm_mon);
+}
+
+int DateTime::GetDay(bool localTime) const
+{
+ return GetTimeStruct(localTime).tm_mday;
+}
+
+DayOfWeek DateTime::GetDayOfWeek(bool localTime) const
+{
+ return static_cast<Aws::Utils::DayOfWeek>(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<std::chrono::milliseconds>(diff);
+}
+
+std::chrono::milliseconds DateTime::operator-(const DateTime& other) const
+{
+ auto diff = this->m_time - other.m_time;
+ return std::chrono::duration_cast<std::chrono::milliseconds>(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;
+}