1/*
2 * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16#include <aws/core/utils/DateTime.h>
17
18#include <aws/core/platform/Time.h>
19#include <aws/core/utils/memory/stl/AWSStringStream.h>
20#include <aws/core/utils/logging/LogMacros.h>
21#include <time.h>
22#include <cassert>
23#include <iostream>
24#include <cstring>
25
26static const char* CLASS_TAG = "DateTime";
27static const char* RFC822_DATE_FORMAT_STR_MINUS_Z = "%a, %d %b %Y %H:%M:%S";
28static const char* RFC822_DATE_FORMAT_STR_WITH_Z = "%a, %d %b %Y %H:%M:%S %Z";
29static const char* ISO_8601_LONG_DATE_FORMAT_STR = "%Y-%m-%dT%H:%M:%SZ";
30
31using namespace Aws::Utils;
32
33
34std::tm CreateZeroedTm()
35{
36 std::tm timeStruct;
37 timeStruct.tm_hour = 0;
38 timeStruct.tm_isdst = -1;
39 timeStruct.tm_mday = 0;
40 timeStruct.tm_min = 0;
41 timeStruct.tm_mon = 0;
42 timeStruct.tm_sec = 0;
43 timeStruct.tm_wday = 0;
44 timeStruct.tm_yday = 0;
45 timeStruct.tm_year = 0;
46
47 return timeStruct;
48}
49
50//Get the 0-6 week day number from a string representing WeekDay. Case insensitive and will stop on abbreviation
51static int GetWeekDayNumberFromStr(const char* timeString, size_t startIndex, size_t stopIndex)
52{
53 if(stopIndex - startIndex < 3)
54 {
55 return -1;
56 }
57
58 size_t index = startIndex;
59
60 char c = timeString[index];
61 char next = 0;
62
63 //it's ugly but this should compile down to EXACTLY 3 comparisons and no memory allocations
64 switch(c)
65 {
66 case 'S':
67 case 's':
68 next = timeString[++index];
69 switch(next)
70 {
71 case 'A':
72 case 'a':
73 next = timeString[++index];
74 switch (next)
75 {
76 case 'T':
77 case 't':
78 return 6;
79 default:
80 return -1;
81 }
82 case 'U':
83 case 'u':
84 next = timeString[++index];
85 switch (next)
86 {
87 case 'N':
88 case 'n':
89 return 0;
90 default:
91 return -1;
92 }
93 default:
94 return -1;
95 }
96 case 'T':
97 case 't':
98 next = timeString[++index];
99 switch (next)
100 {
101 case 'H':
102 case 'h':
103 next = timeString[++index];
104 switch(next)
105 {
106 case 'U':
107 case 'u':
108 return 4;
109 default:
110 return -1;
111 }
112 case 'U':
113 case 'u':
114 next = timeString[++index];
115 switch(next)
116 {
117 case 'E':
118 case 'e':
119 return 2;
120 default:
121 return -1;
122 }
123 default:
124 return -1;
125 }
126 case 'M':
127 case 'm':
128 next = timeString[++index];
129 switch(next)
130 {
131 case 'O':
132 case 'o':
133 next = timeString[++index];
134 switch (next)
135 {
136 case 'N':
137 case 'n':
138 return 1;
139 default:
140 return -1;
141 }
142 default:
143 return -1;
144 }
145 case 'W':
146 case 'w':
147 next = timeString[++index];
148 switch (next)
149 {
150 case 'E':
151 case 'e':
152 next = timeString[++index];
153 switch (next)
154 {
155 case 'D':
156 case 'd':
157 return 3;
158 default:
159 return -1;
160 }
161 default:
162 return -1;
163 }
164 case 'F':
165 case 'f':
166 next = timeString[++index];
167 switch (next)
168 {
169 case 'R':
170 case 'r':
171 next = timeString[++index];
172 switch (next)
173 {
174 case 'I':
175 case 'i':
176 return 5;
177 default:
178 return -1;
179 }
180 default:
181 return -1;
182 }
183 default:
184 return -1;
185 }
186}
187
188//Get the 0-11 monthy number from a string representing Month. Case insensitive and will stop on abbreviation
189static int GetMonthNumberFromStr(const char* timeString, size_t startIndex, size_t stopIndex)
190{
191 if (stopIndex - startIndex < 3)
192 {
193 return -1;
194 }
195
196 size_t index = startIndex;
197
198 char c = timeString[index];
199 char next = 0;
200
201 //it's ugly but this should compile down to EXACTLY 3 comparisons and no memory allocations
202 switch (c)
203 {
204 case 'M':
205 case 'm':
206 next = timeString[++index];
207 switch (next)
208 {
209 case 'A':
210 case 'a':
211 next = timeString[++index];
212 switch (next)
213 {
214 case 'Y':
215 case 'y':
216 return 4;
217 case 'R':
218 case 'r':
219 return 2;
220 default:
221 return -1;
222 }
223 default:
224 return -1;
225 }
226 case 'A':
227 case 'a':
228 next = timeString[++index];
229 switch (next)
230 {
231 case 'P':
232 case 'p':
233 next = timeString[++index];
234 switch (next)
235 {
236 case 'R':
237 case 'r':
238 return 3;
239 default:
240 return -1;
241 }
242 case 'U':
243 case 'u':
244 next = timeString[++index];
245 switch (next)
246 {
247 case 'G':
248 case 'g':
249 return 7;
250 default:
251 return -1;
252 }
253 default:
254 return -1;
255 }
256 case 'J':
257 case 'j':
258 next = timeString[++index];
259 switch (next)
260 {
261 case 'A':
262 case 'a':
263 next = timeString[++index];
264 switch (next)
265 {
266 case 'N':
267 case 'n':
268 return 0;
269 default:
270 return -1;
271 }
272 case 'U':
273 case 'u':
274 next = timeString[++index];
275 switch (next)
276 {
277 case 'N':
278 case 'n':
279 return 5;
280 case 'L':
281 case 'l':
282 return 6;
283 default:
284 return -1;
285 }
286 default:
287 return -1;
288 }
289 case 'F':
290 case 'f':
291 next = timeString[++index];
292 switch (next)
293 {
294 case 'E':
295 case 'e':
296 next = timeString[++index];
297 switch (next)
298 {
299 case 'B':
300 case 'b':
301 return 1;
302 default:
303 return -1;
304 }
305 default:
306 return -1;
307 }
308 case 'S':
309 case 's':
310 next = timeString[++index];
311 switch (next)
312 {
313 case 'E':
314 case 'e':
315 next = timeString[++index];
316 switch (next)
317 {
318 case 'P':
319 case 'p':
320 return 8;
321 default:
322 return -1;
323 }
324 default:
325 return -1;
326 }
327 case 'O':
328 case 'o':
329 next = timeString[++index];
330 switch (next)
331 {
332 case 'C':
333 case 'c':
334 next = timeString[++index];
335 switch (next)
336 {
337 case 'T':
338 case 't':
339 return 9;
340 default:
341 return -1;
342 }
343 default:
344 return -1;
345 }
346 case 'N':
347 case 'n':
348 next = timeString[++index];
349 switch (next)
350 {
351 case 'O':
352 case 'o':
353 next = timeString[++index];
354 switch (next)
355 {
356 case 'V':
357 case 'v':
358 return 10;
359 default:
360 return -1;
361 }
362 default:
363 return -1;
364 }
365 case 'D':
366 case 'd':
367 next = timeString[++index];
368 switch (next)
369 {
370 case 'E':
371 case 'e':
372 next = timeString[++index];
373 switch (next)
374 {
375 case 'C':
376 case 'c':
377 return 11;
378 default:
379 return -1;
380 }
381 default:
382 return -1;
383 }
384 default:
385 return -1;
386 }
387}
388
389//Detects whether or not the passed in timezone string is a UTC zone.
390static bool IsUtcTimeZone(const char* str)
391{
392 size_t len = strlen(str);
393 if (len < 3)
394 {
395 return false;
396 }
397
398 int index = 0;
399 char c = str[index];
400 switch (c)
401 {
402 case 'U':
403 case 'u':
404 c = str[++index];
405 switch(c)
406 {
407 case 'T':
408 case 't':
409 c = str[++index];
410 switch(c)
411 {
412 case 'C':
413 case 'c':
414 return true;
415 default:
416 return false;
417 }
418
419 case 'C':
420 case 'c':
421 c = str[++index];
422 switch (c)
423 {
424 case 'T':
425 case 't':
426 return true;
427 default:
428 return false;
429 }
430 default:
431 return false;
432 }
433 case 'G':
434 case 'g':
435 c = str[++index];
436 switch (c)
437 {
438 case 'M':
439 case 'm':
440 c = str[++index];
441 switch (c)
442 {
443 case 'T':
444 case 't':
445 return true;
446 default:
447 return false;
448 }
449 default:
450 return false;
451 }
452 case '+':
453 case '-':
454 c = str[++index];
455 switch (c)
456 {
457 case '0':
458 c = str[++index];
459 switch (c)
460 {
461 case '0':
462 c = str[++index];
463 switch (c)
464 {
465 case '0':
466 return true;
467 default:
468 return false;
469 }
470 default:
471 return false;
472 }
473 default:
474 return false;
475 }
476 case 'Z':
477 return true;
478 default:
479 return false;
480 }
481
482}
483
484class DateParser
485{
486public:
487 DateParser(const char* toParse) : m_error(false), m_toParse(toParse), m_utcAssumed(true)
488 {
489 m_parsedTimestamp = CreateZeroedTm();
490 memset(m_tz, 0, 5);
491 }
492
493 virtual ~DateParser() = default;
494
495 virtual void Parse() = 0;
496 bool WasParseSuccessful() const { return !m_error; }
497 std::tm& GetParsedTimestamp() { return m_parsedTimestamp; }
498 bool ShouldIAssumeThisIsUTC() const { return m_utcAssumed; }
499 const char* GetParsedTimezone() const { return m_tz; }
500
501protected:
502 bool m_error;
503 const char* m_toParse;
504 std::tm m_parsedTimestamp;
505 bool m_utcAssumed;
506 char m_tz[5];
507};
508
509static const int MAX_LEN = 100;
510
511//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
512//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
513//the standard is parsed correctly. strptime isn't available one Windows. This code gets hit pretty hard during http serialization/deserialization
514//as a result I'm going for no dynamic allocations and linear complexity
515class RFC822DateParser : public DateParser
516{
517public:
518 RFC822DateParser(const char* toParse) : DateParser(toParse), m_state(0)
519 {
520 }
521
522 /**
523 * Really simple state machine for the format %a, %d %b %Y %H:%M:%S %Z
524 */
525 void Parse() override
526 {
527 size_t len = strlen(m_toParse);
528
529 //DOS check
530 if (len > MAX_LEN)
531 {
532 AWS_LOGSTREAM_WARN(CLASS_TAG, "Incoming String to parse too long with len " << len)
533 m_error = true;
534 return;
535 }
536
537 size_t index = 0;
538 size_t stateStartIndex = 0;
539 int finalState = 8;
540
541 while(m_state <= finalState && !m_error && index < len)
542 {
543 char c = m_toParse[index];
544
545 switch (m_state)
546 {
547 case 0:
548 if(c == ',')
549 {
550 int weekNumber = GetWeekDayNumberFromStr(m_toParse, stateStartIndex, index + 1);
551
552 if (weekNumber > -1)
553 {
554 m_state = 1;
555 stateStartIndex = index + 1;
556 m_parsedTimestamp.tm_wday = weekNumber;
557 }
558 else
559 {
560 m_error = true;
561 }
562 }
563 else if(!isalpha(c))
564 {
565 m_error = true;
566 }
567 break;
568 case 1:
569 if (isspace(c))
570 {
571 m_state = 2;
572 stateStartIndex = index + 1;
573 }
574 else
575 {
576 m_error = true;
577 }
578 break;
579 case 2:
580 if (isdigit(c))
581 {
582 m_parsedTimestamp.tm_mday = m_parsedTimestamp.tm_mday * 10 + (c - '0');
583 }
584 else if(isspace(c))
585 {
586 m_state = 3;
587 stateStartIndex = index + 1;
588 }
589 else
590 {
591 m_error = true;
592 }
593 break;
594 case 3:
595 if (isspace(c))
596 {
597 int monthNumber = GetMonthNumberFromStr(m_toParse, stateStartIndex, index + 1);
598
599 if (monthNumber > -1)
600 {
601 m_state = 4;
602 stateStartIndex = index + 1;
603 m_parsedTimestamp.tm_mon = monthNumber;
604 }
605 else
606 {
607 m_error = true;
608 }
609 }
610 else if (!isalpha(c))
611 {
612 m_error = true;
613 }
614 break;
615 case 4:
616 if (isspace(c) && index - stateStartIndex == 4)
617 {
618 m_state = 5;
619 stateStartIndex = index + 1;
620 m_parsedTimestamp.tm_year -= 1900;
621 }
622 else if (isspace(c) && index - stateStartIndex == 2)
623 {
624 m_state = 5;
625 stateStartIndex = index + 1;
626 m_parsedTimestamp.tm_year += 2000 - 1900;
627 }
628 else if (isdigit(c))
629 {
630 m_parsedTimestamp.tm_year = m_parsedTimestamp.tm_year * 10 + (c - '0');
631 }
632 else
633 {
634 m_error = true;
635 }
636 break;
637 case 5:
638 if(c == ':' && index - stateStartIndex == 2)
639 {
640 m_state = 6;
641 stateStartIndex = index + 1;
642 }
643 else if (isdigit(c))
644 {
645 m_parsedTimestamp.tm_hour = m_parsedTimestamp.tm_hour * 10 + (c - '0');
646 }
647 else
648 {
649 m_error = true;
650 }
651 break;
652 case 6:
653 if (c == ':' && index - stateStartIndex == 2)
654 {
655 m_state = 7;
656 stateStartIndex = index + 1;
657 }
658 else if (isdigit(c))
659 {
660 m_parsedTimestamp.tm_min = m_parsedTimestamp.tm_min * 10 + (c - '0');
661 }
662 else
663 {
664 m_error = true;
665 }
666 break;
667 case 7:
668 if (isspace(c) && index - stateStartIndex == 2)
669 {
670 m_state = 8;
671 stateStartIndex = index + 1;
672 }
673 else if (isdigit(c))
674 {
675 m_parsedTimestamp.tm_sec = m_parsedTimestamp.tm_sec * 10 + (c - '0');
676 }
677 else
678 {
679 m_error = true;
680 }
681 break;
682 case 8:
683 if (isalpha(c) && (index - stateStartIndex) < 5)
684 {
685 m_tz[index - stateStartIndex] = c;
686 }
687
688 break;
689 }
690
691 index++;
692 }
693
694 if (m_tz[0] != 0)
695 {
696 m_utcAssumed = IsUtcTimeZone(m_tz);
697 }
698
699 m_error = (m_error || m_state != finalState);
700 }
701
702 int GetState() const { return m_state; }
703
704private:
705 int m_state;
706};
707
708//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
709//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
710//the standard is parsed correctly. strptime isn't available one Windows. This code gets hit pretty hard during http serialization/deserialization
711//as a result I'm going for no dynamic allocations and linear complexity
712class ISO_8601DateParser : public DateParser
713{
714public:
715 ISO_8601DateParser(const char* stringToParse) : DateParser(stringToParse), m_state(0)
716 {
717 }
718
719 //parses "%Y-%m-%dT%H:%M:%SZ or "%Y-%m-%dT%H:%M:%S.000Z"
720 void Parse() override
721 {
722 size_t len = strlen(m_toParse);
723
724 //DOS check
725 if (len > MAX_LEN)
726 {
727 AWS_LOGSTREAM_WARN(CLASS_TAG, "Incoming String to parse too long with len " << len)
728 m_error = true;
729 return;
730 }
731
732 size_t index = 0;
733 size_t stateStartIndex = 0;
734 const int finalState = 7;
735
736 while (m_state <= finalState && !m_error && index < len)
737 {
738 char c = m_toParse[index];
739 switch (m_state)
740 {
741 case 0:
742 if (c == '-' && index - stateStartIndex == 4)
743 {
744 m_state = 1;
745 stateStartIndex = index + 1;
746 m_parsedTimestamp.tm_year -= 1900;
747 }
748 else if (isdigit(c))
749 {
750 m_parsedTimestamp.tm_year = m_parsedTimestamp.tm_year * 10 + (c - '0');
751 }
752 else
753 {
754 m_error = true;
755 }
756 break;
757 case 1:
758 if (c == '-' && index - stateStartIndex == 2)
759 {
760 m_state = 2;
761 stateStartIndex = index + 1;
762 m_parsedTimestamp.tm_mon -= 1;
763 }
764 else if (isdigit(c))
765 {
766 m_parsedTimestamp.tm_mon = m_parsedTimestamp.tm_mon * 10 + (c - '0');
767 }
768 else
769 {
770 m_error = true;
771 }
772
773 break;
774 case 2:
775 if (c == 'T' && index - stateStartIndex == 2)
776 {
777 m_state = 3;
778 stateStartIndex = index + 1;
779 }
780 else if (isdigit(c))
781 {
782 m_parsedTimestamp.tm_mday = m_parsedTimestamp.tm_mday * 10 + (c - '0');
783 }
784 else
785 {
786 m_error = true;
787 }
788
789 break;
790 case 3:
791 if (c == ':' && index - stateStartIndex == 2)
792 {
793 m_state = 4;
794 stateStartIndex = index + 1;
795 }
796 else if (isdigit(c))
797 {
798 m_parsedTimestamp.tm_hour = m_parsedTimestamp.tm_hour * 10 + (c - '0');
799 }
800 else
801 {
802 m_error = true;
803 }
804
805 break;
806 case 4:
807 if (c == ':' && index - stateStartIndex == 2)
808 {
809 m_state = 5;
810 stateStartIndex = index + 1;
811 }
812 else if (isdigit(c))
813 {
814 m_parsedTimestamp.tm_min = m_parsedTimestamp.tm_min * 10 + (c - '0');
815 }
816 else
817 {
818 m_error = true;
819 }
820
821 break;
822 case 5:
823 if (c == 'Z' && index - stateStartIndex == 2)
824 {
825 m_state = finalState;
826 stateStartIndex = index + 1;
827 }
828 else if (c == '.' && index - stateStartIndex == 2)
829 {
830 m_state = 6;
831 stateStartIndex = index + 1;
832 }
833 else if (isdigit(c))
834 {
835 m_parsedTimestamp.tm_sec = m_parsedTimestamp.tm_sec * 10 + (c - '0');
836 }
837 else
838 {
839 m_error = true;
840 }
841
842 break;
843 case 6:
844 if (c == 'Z')
845 {
846 m_state = finalState;
847 stateStartIndex = index + 1;
848 }
849 else if(!isdigit(c))
850 {
851 m_error = true;
852 }
853 break;
854 }
855 index++;
856 }
857
858 m_error = (m_error || m_state != finalState);
859 }
860
861
862private:
863 int m_state;
864};
865
866DateTime::DateTime(const std::chrono::system_clock::time_point& timepointToAssign) : m_time(timepointToAssign), m_valid(true)
867{
868}
869
870DateTime::DateTime(int64_t millisSinceEpoch) : m_valid(true)
871{
872 std::chrono::duration<int64_t, std::chrono::milliseconds::period> timestamp(millisSinceEpoch);
873 m_time = std::chrono::system_clock::time_point(timestamp);
874}
875
876DateTime::DateTime(double epoch_millis) : m_valid(true)
877{
878 std::chrono::duration<double, std::chrono::seconds::period> timestamp(epoch_millis);
879 m_time = std::chrono::system_clock::time_point(std::chrono::duration_cast<std::chrono::milliseconds>(timestamp));
880}
881
882DateTime::DateTime(const Aws::String& timestamp, DateFormat format) : m_valid(true)
883{
884 ConvertTimestampStringToTimePoint(timestamp.c_str(), format);
885}
886
887DateTime::DateTime(const char* timestamp, DateFormat format) : m_valid(true)
888{
889 ConvertTimestampStringToTimePoint(timestamp, format);
890}
891
892DateTime::DateTime() : m_valid(true)
893{
894 //init time_point to default by doing nothing.
895}
896
897DateTime& DateTime::operator=(const Aws::String& timestamp)
898{
899 *this = DateTime(timestamp, DateFormat::AutoDetect);
900 return *this;
901}
902
903DateTime& DateTime::operator=(double secondsMillis)
904{
905 *this = DateTime(secondsMillis);
906 return *this;
907}
908
909DateTime& DateTime::operator=(int64_t millisSinceEpoch)
910{
911 *this = DateTime(millisSinceEpoch);
912 return *this;
913}
914
915DateTime& DateTime::operator=(const std::chrono::system_clock::time_point& timepointToAssign)
916{
917 *this = DateTime(timepointToAssign);
918 return *this;
919}
920
921bool DateTime::operator == (const DateTime& other) const
922{
923 return m_time == other.m_time;
924}
925
926bool DateTime::operator < (const DateTime& other) const
927{
928 return m_time < other.m_time;
929}
930
931bool DateTime::operator > (const DateTime& other) const
932{
933 return m_time > other.m_time;
934}
935
936bool DateTime::operator != (const DateTime& other) const
937{
938 return m_time != other.m_time;
939}
940
941bool DateTime::operator <= (const DateTime& other) const
942{
943 return m_time <= other.m_time;
944}
945
946bool DateTime::operator >= (const DateTime& other) const
947{
948 return m_time >= other.m_time;
949}
950
951DateTime DateTime::operator +(const std::chrono::milliseconds& a) const
952{
953 auto timepointCpy = m_time;
954 timepointCpy += a;
955 return DateTime(timepointCpy);
956}
957
958DateTime DateTime::operator -(const std::chrono::milliseconds& a) const
959{
960 auto timepointCpy = m_time;
961 timepointCpy -= a;
962 return DateTime(timepointCpy);
963}
964
965Aws::String DateTime::ToLocalTimeString(DateFormat format) const
966{
967 switch (format)
968 {
969 case DateFormat::ISO_8601:
970 return ToLocalTimeString(ISO_8601_LONG_DATE_FORMAT_STR);
971 case DateFormat::RFC822:
972 return ToLocalTimeString(RFC822_DATE_FORMAT_STR_WITH_Z);
973 default:
974 assert(0);
975 return "";
976 }
977}
978
979Aws::String DateTime::ToLocalTimeString(const char* formatStr) const
980{
981 struct tm localTimeStamp = ConvertTimestampToLocalTimeStruct();
982
983 char formattedString[100];
984 std::strftime(formattedString, sizeof(formattedString), formatStr, &localTimeStamp);
985 return formattedString;
986}
987
988Aws::String DateTime::ToGmtString(DateFormat format) const
989{
990 switch (format)
991 {
992 case DateFormat::ISO_8601:
993 return ToGmtString(ISO_8601_LONG_DATE_FORMAT_STR);
994 case DateFormat::RFC822:
995 {
996 //Windows erronously drops the local timezone in for %Z
997 Aws::String rfc822GmtString = ToGmtString(RFC822_DATE_FORMAT_STR_MINUS_Z);
998 rfc822GmtString += " GMT";
999 return rfc822GmtString;
1000 }
1001 default:
1002 assert(0);
1003 return "";
1004 }
1005}
1006
1007Aws::String DateTime::ToGmtString(const char* formatStr) const
1008{
1009 struct tm gmtTimeStamp = ConvertTimestampToGmtStruct();
1010
1011 char formattedString[100];
1012 std::strftime(formattedString, sizeof(formattedString), formatStr, &gmtTimeStamp);
1013 return formattedString;
1014}
1015
1016double DateTime::SecondsWithMSPrecision() const
1017{
1018 std::chrono::duration<double, std::chrono::seconds::period> timestamp(m_time.time_since_epoch());
1019 return timestamp.count();
1020}
1021
1022int64_t DateTime::Millis() const
1023{
1024 auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(m_time.time_since_epoch());
1025 return timestamp.count();
1026}
1027
1028std::chrono::system_clock::time_point DateTime::UnderlyingTimestamp() const
1029{
1030 return m_time;
1031}
1032
1033int DateTime::GetYear(bool localTime) const
1034{
1035 return GetTimeStruct(localTime).tm_year + 1900;
1036}
1037
1038Month DateTime::GetMonth(bool localTime) const
1039{
1040 return static_cast<Aws::Utils::Month>(GetTimeStruct(localTime).tm_mon);
1041}
1042
1043int DateTime::GetDay(bool localTime) const
1044{
1045 return GetTimeStruct(localTime).tm_mday;
1046}
1047
1048DayOfWeek DateTime::GetDayOfWeek(bool localTime) const
1049{
1050 return static_cast<Aws::Utils::DayOfWeek>(GetTimeStruct(localTime).tm_wday);
1051}
1052
1053int DateTime::GetHour(bool localTime) const
1054{
1055 return GetTimeStruct(localTime).tm_hour;
1056}
1057
1058int DateTime::GetMinute(bool localTime) const
1059{
1060 return GetTimeStruct(localTime).tm_min;
1061}
1062
1063int DateTime::GetSecond(bool localTime) const
1064{
1065 return GetTimeStruct(localTime).tm_sec;
1066}
1067
1068bool DateTime::IsDST(bool localTime) const
1069{
1070 return GetTimeStruct(localTime).tm_isdst == 0 ? false : true;
1071}
1072
1073DateTime DateTime::Now()
1074{
1075 DateTime dateTime;
1076 dateTime.m_time = std::chrono::system_clock::now();
1077 return dateTime;
1078}
1079
1080int64_t DateTime::CurrentTimeMillis()
1081{
1082 return Now().Millis();
1083}
1084
1085Aws::String DateTime::CalculateLocalTimestampAsString(const char* formatStr)
1086{
1087 DateTime now = Now();
1088 return now.ToLocalTimeString(formatStr);
1089}
1090
1091Aws::String DateTime::CalculateGmtTimestampAsString(const char* formatStr)
1092{
1093 DateTime now = Now();
1094 return now.ToGmtString(formatStr);
1095}
1096
1097Aws::String DateTime::CalculateGmtTimeWithMsPrecision()
1098{
1099 auto now = DateTime::Now();
1100 struct tm gmtTimeStamp = now.ConvertTimestampToGmtStruct();
1101
1102 char formattedString[100];
1103 auto len = std::strftime(formattedString, sizeof(formattedString), "%Y-%m-%d %H:%M:%S", &gmtTimeStamp);
1104 if (len)
1105 {
1106 auto ms = now.Millis();
1107 ms = ms - ms / 1000 * 1000; // calculate the milliseconds as fraction.
1108 formattedString[len++] = '.';
1109 int divisor = 100;
1110 while(divisor)
1111 {
1112 auto digit = ms / divisor;
1113 formattedString[len++] = char('0' + digit);
1114 ms = ms - divisor * digit;
1115 divisor /= 10;
1116 }
1117 formattedString[len] = '\0';
1118 }
1119 return formattedString;
1120}
1121
1122int DateTime::CalculateCurrentHour()
1123{
1124 return Now().GetHour(true);
1125}
1126
1127double DateTime::ComputeCurrentTimestampInAmazonFormat()
1128{
1129 return Now().SecondsWithMSPrecision();
1130}
1131
1132std::chrono::milliseconds DateTime::Diff(const DateTime& a, const DateTime& b)
1133{
1134 auto diff = a.m_time - b.m_time;
1135 return std::chrono::duration_cast<std::chrono::milliseconds>(diff);
1136}
1137
1138std::chrono::milliseconds DateTime::operator-(const DateTime& other) const
1139{
1140 auto diff = this->m_time - other.m_time;
1141 return std::chrono::duration_cast<std::chrono::milliseconds>(diff);
1142}
1143
1144void DateTime::ConvertTimestampStringToTimePoint(const char* timestamp, DateFormat format)
1145{
1146 std::tm timeStruct;
1147 bool isUtc = true;
1148
1149 switch (format)
1150 {
1151 case DateFormat::RFC822:
1152 {
1153 RFC822DateParser parser(timestamp);
1154 parser.Parse();
1155 m_valid = parser.WasParseSuccessful();
1156 isUtc = parser.ShouldIAssumeThisIsUTC();
1157 timeStruct = parser.GetParsedTimestamp();
1158 break;
1159 }
1160 case DateFormat::ISO_8601:
1161 {
1162 ISO_8601DateParser parser(timestamp);
1163 parser.Parse();
1164 m_valid = parser.WasParseSuccessful();
1165 isUtc = parser.ShouldIAssumeThisIsUTC();
1166 timeStruct = parser.GetParsedTimestamp();
1167 break;
1168 }
1169 case DateFormat::AutoDetect:
1170 {
1171 RFC822DateParser rfcParser(timestamp);
1172 rfcParser.Parse();
1173 if(rfcParser.WasParseSuccessful())
1174 {
1175 m_valid = true;
1176 isUtc = rfcParser.ShouldIAssumeThisIsUTC();
1177 timeStruct = rfcParser.GetParsedTimestamp();
1178 break;
1179 }
1180 ISO_8601DateParser isoParser(timestamp);
1181 isoParser.Parse();
1182 if (isoParser.WasParseSuccessful())
1183 {
1184 m_valid = true;
1185 isUtc = isoParser.ShouldIAssumeThisIsUTC();
1186 timeStruct = isoParser.GetParsedTimestamp();
1187 break;
1188 }
1189 m_valid = false;
1190 break;
1191 }
1192 default:
1193 assert(0);
1194 }
1195
1196 if (m_valid)
1197 {
1198 std::time_t tt;
1199 if(isUtc)
1200 {
1201 tt = Aws::Time::TimeGM(&timeStruct);
1202 }
1203 else
1204 {
1205 assert(0);
1206 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)
1207 tt = std::mktime(&timeStruct);
1208 }
1209 m_time = std::chrono::system_clock::from_time_t(tt);
1210 }
1211}
1212
1213tm DateTime::GetTimeStruct(bool localTime) const
1214{
1215 return localTime ? ConvertTimestampToLocalTimeStruct() : ConvertTimestampToGmtStruct();
1216}
1217
1218tm DateTime::ConvertTimestampToLocalTimeStruct() const
1219{
1220 std::time_t time = std::chrono::system_clock::to_time_t(m_time);
1221 struct tm localTimeStamp;
1222
1223 Aws::Time::LocalTime(&localTimeStamp, time);
1224
1225 return localTimeStamp;
1226}
1227
1228tm DateTime::ConvertTimestampToGmtStruct() const
1229{
1230 std::time_t time = std::chrono::system_clock::to_time_t(m_time);
1231 struct tm gmtTimeStamp;
1232 Aws::Time::GMTime(&gmtTimeStamp, time);
1233
1234 return gmtTimeStamp;
1235}
1236