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 | |
26 | static const char* CLASS_TAG = "DateTime" ; |
27 | static const char* RFC822_DATE_FORMAT_STR_MINUS_Z = "%a, %d %b %Y %H:%M:%S" ; |
28 | static const char* RFC822_DATE_FORMAT_STR_WITH_Z = "%a, %d %b %Y %H:%M:%S %Z" ; |
29 | static const char* ISO_8601_LONG_DATE_FORMAT_STR = "%Y-%m-%dT%H:%M:%SZ" ; |
30 | |
31 | using namespace Aws::Utils; |
32 | |
33 | |
34 | std::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 |
51 | static 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 |
189 | static 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. |
390 | static 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 | |
484 | class DateParser |
485 | { |
486 | public: |
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 | |
501 | protected: |
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 | |
509 | static 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 |
515 | class RFC822DateParser : public DateParser |
516 | { |
517 | public: |
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 | |
704 | private: |
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 |
712 | class ISO_8601DateParser : public DateParser |
713 | { |
714 | public: |
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 | |
862 | private: |
863 | int m_state; |
864 | }; |
865 | |
866 | DateTime::DateTime(const std::chrono::system_clock::time_point& timepointToAssign) : m_time(timepointToAssign), m_valid(true) |
867 | { |
868 | } |
869 | |
870 | DateTime::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 | |
876 | DateTime::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 | |
882 | DateTime::DateTime(const Aws::String& timestamp, DateFormat format) : m_valid(true) |
883 | { |
884 | ConvertTimestampStringToTimePoint(timestamp.c_str(), format); |
885 | } |
886 | |
887 | DateTime::DateTime(const char* timestamp, DateFormat format) : m_valid(true) |
888 | { |
889 | ConvertTimestampStringToTimePoint(timestamp, format); |
890 | } |
891 | |
892 | DateTime::DateTime() : m_valid(true) |
893 | { |
894 | //init time_point to default by doing nothing. |
895 | } |
896 | |
897 | DateTime& DateTime::operator=(const Aws::String& timestamp) |
898 | { |
899 | *this = DateTime(timestamp, DateFormat::AutoDetect); |
900 | return *this; |
901 | } |
902 | |
903 | DateTime& DateTime::operator=(double secondsMillis) |
904 | { |
905 | *this = DateTime(secondsMillis); |
906 | return *this; |
907 | } |
908 | |
909 | DateTime& DateTime::operator=(int64_t millisSinceEpoch) |
910 | { |
911 | *this = DateTime(millisSinceEpoch); |
912 | return *this; |
913 | } |
914 | |
915 | DateTime& DateTime::operator=(const std::chrono::system_clock::time_point& timepointToAssign) |
916 | { |
917 | *this = DateTime(timepointToAssign); |
918 | return *this; |
919 | } |
920 | |
921 | bool DateTime::operator == (const DateTime& other) const |
922 | { |
923 | return m_time == other.m_time; |
924 | } |
925 | |
926 | bool DateTime::operator < (const DateTime& other) const |
927 | { |
928 | return m_time < other.m_time; |
929 | } |
930 | |
931 | bool DateTime::operator > (const DateTime& other) const |
932 | { |
933 | return m_time > other.m_time; |
934 | } |
935 | |
936 | bool DateTime::operator != (const DateTime& other) const |
937 | { |
938 | return m_time != other.m_time; |
939 | } |
940 | |
941 | bool DateTime::operator <= (const DateTime& other) const |
942 | { |
943 | return m_time <= other.m_time; |
944 | } |
945 | |
946 | bool DateTime::operator >= (const DateTime& other) const |
947 | { |
948 | return m_time >= other.m_time; |
949 | } |
950 | |
951 | DateTime DateTime::operator +(const std::chrono::milliseconds& a) const |
952 | { |
953 | auto timepointCpy = m_time; |
954 | timepointCpy += a; |
955 | return DateTime(timepointCpy); |
956 | } |
957 | |
958 | DateTime DateTime::operator -(const std::chrono::milliseconds& a) const |
959 | { |
960 | auto timepointCpy = m_time; |
961 | timepointCpy -= a; |
962 | return DateTime(timepointCpy); |
963 | } |
964 | |
965 | Aws::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 | |
979 | Aws::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 | |
988 | Aws::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 | |
1007 | Aws::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 | |
1016 | double 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 | |
1022 | int64_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 | |
1028 | std::chrono::system_clock::time_point DateTime::UnderlyingTimestamp() const |
1029 | { |
1030 | return m_time; |
1031 | } |
1032 | |
1033 | int DateTime::GetYear(bool localTime) const |
1034 | { |
1035 | return GetTimeStruct(localTime).tm_year + 1900; |
1036 | } |
1037 | |
1038 | Month DateTime::GetMonth(bool localTime) const |
1039 | { |
1040 | return static_cast<Aws::Utils::Month>(GetTimeStruct(localTime).tm_mon); |
1041 | } |
1042 | |
1043 | int DateTime::GetDay(bool localTime) const |
1044 | { |
1045 | return GetTimeStruct(localTime).tm_mday; |
1046 | } |
1047 | |
1048 | DayOfWeek DateTime::GetDayOfWeek(bool localTime) const |
1049 | { |
1050 | return static_cast<Aws::Utils::DayOfWeek>(GetTimeStruct(localTime).tm_wday); |
1051 | } |
1052 | |
1053 | int DateTime::GetHour(bool localTime) const |
1054 | { |
1055 | return GetTimeStruct(localTime).tm_hour; |
1056 | } |
1057 | |
1058 | int DateTime::GetMinute(bool localTime) const |
1059 | { |
1060 | return GetTimeStruct(localTime).tm_min; |
1061 | } |
1062 | |
1063 | int DateTime::GetSecond(bool localTime) const |
1064 | { |
1065 | return GetTimeStruct(localTime).tm_sec; |
1066 | } |
1067 | |
1068 | bool DateTime::IsDST(bool localTime) const |
1069 | { |
1070 | return GetTimeStruct(localTime).tm_isdst == 0 ? false : true; |
1071 | } |
1072 | |
1073 | DateTime DateTime::Now() |
1074 | { |
1075 | DateTime dateTime; |
1076 | dateTime.m_time = std::chrono::system_clock::now(); |
1077 | return dateTime; |
1078 | } |
1079 | |
1080 | int64_t DateTime::CurrentTimeMillis() |
1081 | { |
1082 | return Now().Millis(); |
1083 | } |
1084 | |
1085 | Aws::String DateTime::CalculateLocalTimestampAsString(const char* formatStr) |
1086 | { |
1087 | DateTime now = Now(); |
1088 | return now.ToLocalTimeString(formatStr); |
1089 | } |
1090 | |
1091 | Aws::String DateTime::CalculateGmtTimestampAsString(const char* formatStr) |
1092 | { |
1093 | DateTime now = Now(); |
1094 | return now.ToGmtString(formatStr); |
1095 | } |
1096 | |
1097 | Aws::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 | |
1122 | int DateTime::CalculateCurrentHour() |
1123 | { |
1124 | return Now().GetHour(true); |
1125 | } |
1126 | |
1127 | double DateTime::ComputeCurrentTimestampInAmazonFormat() |
1128 | { |
1129 | return Now().SecondsWithMSPrecision(); |
1130 | } |
1131 | |
1132 | std::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 | |
1138 | std::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 | |
1144 | void 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 | |
1213 | tm DateTime::GetTimeStruct(bool localTime) const |
1214 | { |
1215 | return localTime ? ConvertTimestampToLocalTimeStruct() : ConvertTimestampToGmtStruct(); |
1216 | } |
1217 | |
1218 | tm 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 | |
1228 | tm 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 | |