LCOV - code coverage report
Current view: top level - aws-c-common/source - date_time.c (source / functions) Hit Total Coverage
Test: all_fuzz.info Lines: 0 702 0.0 %
Date: 2021-04-23 16:28:21 Functions: 0 29 0.0 %

          Line data    Source code
       1             : /**
       2             :  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       3             :  * SPDX-License-Identifier: Apache-2.0.
       4             :  */
       5             : #include <aws/common/date_time.h>
       6             : 
       7             : #include <aws/common/array_list.h>
       8             : #include <aws/common/byte_buf.h>
       9             : #include <aws/common/byte_order.h>
      10             : #include <aws/common/clock.h>
      11             : #include <aws/common/string.h>
      12             : #include <aws/common/time.h>
      13             : 
      14             : #include <ctype.h>
      15             : 
      16             : static const char *RFC822_DATE_FORMAT_STR_MINUS_Z = "%a, %d %b %Y %H:%M:%S GMT";
      17             : static const char *RFC822_DATE_FORMAT_STR_WITH_Z = "%a, %d %b %Y %H:%M:%S %Z";
      18             : static const char *RFC822_SHORT_DATE_FORMAT_STR = "%a, %d %b %Y";
      19             : static const char *ISO_8601_LONG_DATE_FORMAT_STR = "%Y-%m-%dT%H:%M:%SZ";
      20             : static const char *ISO_8601_SHORT_DATE_FORMAT_STR = "%Y-%m-%d";
      21             : static const char *ISO_8601_LONG_BASIC_DATE_FORMAT_STR = "%Y%m%dT%H%M%SZ";
      22             : static const char *ISO_8601_SHORT_BASIC_DATE_FORMAT_STR = "%Y%m%d";
      23             : 
      24             : #define STR_TRIPLET_TO_INDEX(str)                                                                                      \
      25           0 :     (((uint32_t)tolower((uint8_t)((str)[0])) << 0) | ((uint32_t)tolower((uint8_t)((str)[1])) << 8) |                   \
      26           0 :      ((uint32_t)tolower((uint8_t)((str)[2])) << 16))
      27             : 
      28             : static uint32_t s_jan = 0;
      29             : static uint32_t s_feb = 0;
      30             : static uint32_t s_mar = 0;
      31             : static uint32_t s_apr = 0;
      32             : static uint32_t s_may = 0;
      33             : static uint32_t s_jun = 0;
      34             : static uint32_t s_jul = 0;
      35             : static uint32_t s_aug = 0;
      36             : static uint32_t s_sep = 0;
      37             : static uint32_t s_oct = 0;
      38             : static uint32_t s_nov = 0;
      39             : static uint32_t s_dec = 0;
      40             : 
      41             : static uint32_t s_utc = 0;
      42             : static uint32_t s_gmt = 0;
      43             : 
      44           0 : static void s_check_init_str_to_int(void) {
      45           0 :     if (!s_jan) {
      46           0 :         s_jan = STR_TRIPLET_TO_INDEX("jan");
      47           0 :         s_feb = STR_TRIPLET_TO_INDEX("feb");
      48           0 :         s_mar = STR_TRIPLET_TO_INDEX("mar");
      49           0 :         s_apr = STR_TRIPLET_TO_INDEX("apr");
      50           0 :         s_may = STR_TRIPLET_TO_INDEX("may");
      51           0 :         s_jun = STR_TRIPLET_TO_INDEX("jun");
      52           0 :         s_jul = STR_TRIPLET_TO_INDEX("jul");
      53           0 :         s_aug = STR_TRIPLET_TO_INDEX("aug");
      54           0 :         s_sep = STR_TRIPLET_TO_INDEX("sep");
      55           0 :         s_oct = STR_TRIPLET_TO_INDEX("oct");
      56           0 :         s_nov = STR_TRIPLET_TO_INDEX("nov");
      57           0 :         s_dec = STR_TRIPLET_TO_INDEX("dec");
      58           0 :         s_utc = STR_TRIPLET_TO_INDEX("utc");
      59           0 :         s_gmt = STR_TRIPLET_TO_INDEX("gmt");
      60           0 :     }
      61           0 : }
      62             : 
      63             : /* Get the 0-11 monthy number from a string representing Month. Case insensitive and will stop on abbreviation*/
      64           0 : static int get_month_number_from_str(const char *time_string, size_t start_index, size_t stop_index) {
      65           0 :     s_check_init_str_to_int();
      66           0 : 
      67           0 :     if (stop_index - start_index < 3) {
      68           0 :         return -1;
      69           0 :     }
      70           0 : 
      71           0 :     /* This AND forces the string to lowercase (assuming ASCII) */
      72           0 :     uint32_t comp_val = STR_TRIPLET_TO_INDEX(time_string + start_index);
      73           0 : 
      74           0 :     /* this can't be a switch, because I can't make it a constant expression. */
      75           0 :     if (s_jan == comp_val) {
      76           0 :         return 0;
      77           0 :     }
      78           0 : 
      79           0 :     if (s_feb == comp_val) {
      80           0 :         return 1;
      81           0 :     }
      82           0 : 
      83           0 :     if (s_mar == comp_val) {
      84           0 :         return 2;
      85           0 :     }
      86           0 : 
      87           0 :     if (s_apr == comp_val) {
      88           0 :         return 3;
      89           0 :     }
      90           0 : 
      91           0 :     if (s_may == comp_val) {
      92           0 :         return 4;
      93           0 :     }
      94           0 : 
      95           0 :     if (s_jun == comp_val) {
      96           0 :         return 5;
      97           0 :     }
      98           0 : 
      99           0 :     if (s_jul == comp_val) {
     100           0 :         return 6;
     101           0 :     }
     102           0 : 
     103           0 :     if (s_aug == comp_val) {
     104           0 :         return 7;
     105           0 :     }
     106           0 : 
     107           0 :     if (s_sep == comp_val) {
     108           0 :         return 8;
     109           0 :     }
     110           0 : 
     111           0 :     if (s_oct == comp_val) {
     112           0 :         return 9;
     113           0 :     }
     114           0 : 
     115           0 :     if (s_nov == comp_val) {
     116           0 :         return 10;
     117           0 :     }
     118           0 : 
     119           0 :     if (s_dec == comp_val) {
     120           0 :         return 11;
     121           0 :     }
     122           0 : 
     123           0 :     return -1;
     124           0 : }
     125             : 
     126             : /* Detects whether or not the passed in timezone string is a UTC zone. */
     127           0 : static bool is_utc_time_zone(const char *str) {
     128           0 :     s_check_init_str_to_int();
     129           0 : 
     130           0 :     size_t len = strlen(str);
     131           0 : 
     132           0 :     if (len > 0) {
     133           0 :         if (str[0] == 'Z') {
     134           0 :             return true;
     135           0 :         }
     136           0 : 
     137           0 :         /* offsets count since their usable */
     138           0 :         if (len == 5 && (str[0] == '+' || str[0] == '-')) {
     139           0 :             return true;
     140           0 :         }
     141           0 : 
     142           0 :         if (len == 2) {
     143           0 :             return tolower((uint8_t)str[0]) == 'u' && tolower((uint8_t)str[1]) == 't';
     144           0 :         }
     145           0 : 
     146           0 :         if (len < 3) {
     147           0 :             return false;
     148           0 :         }
     149           0 : 
     150           0 :         uint32_t comp_val = STR_TRIPLET_TO_INDEX(str);
     151           0 : 
     152           0 :         if (comp_val == s_utc || comp_val == s_gmt) {
     153           0 :             return true;
     154           0 :         }
     155           0 :     }
     156           0 : 
     157           0 :     return false;
     158           0 : }
     159             : 
     160           0 : struct tm s_get_time_struct(struct aws_date_time *dt, bool local_time) {
     161           0 :     struct tm time;
     162           0 :     AWS_ZERO_STRUCT(time);
     163           0 :     if (local_time) {
     164           0 :         aws_localtime(dt->timestamp, &time);
     165           0 :     } else {
     166           0 :         aws_gmtime(dt->timestamp, &time);
     167           0 :     }
     168           0 : 
     169           0 :     return time;
     170           0 : }
     171             : 
     172           0 : void aws_date_time_init_now(struct aws_date_time *dt) {
     173           0 :     uint64_t current_time = 0;
     174           0 :     aws_sys_clock_get_ticks(&current_time);
     175           0 :     dt->timestamp = (time_t)aws_timestamp_convert(current_time, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL);
     176           0 :     dt->gmt_time = s_get_time_struct(dt, false);
     177           0 :     dt->local_time = s_get_time_struct(dt, true);
     178           0 : }
     179             : 
     180           0 : void aws_date_time_init_epoch_millis(struct aws_date_time *dt, uint64_t ms_since_epoch) {
     181           0 :     dt->timestamp = (time_t)(ms_since_epoch / AWS_TIMESTAMP_MILLIS);
     182           0 :     dt->gmt_time = s_get_time_struct(dt, false);
     183           0 :     dt->local_time = s_get_time_struct(dt, true);
     184           0 : }
     185             : 
     186           0 : void aws_date_time_init_epoch_secs(struct aws_date_time *dt, double sec_ms) {
     187           0 :     dt->timestamp = (time_t)sec_ms;
     188           0 :     dt->gmt_time = s_get_time_struct(dt, false);
     189           0 :     dt->local_time = s_get_time_struct(dt, true);
     190           0 : }
     191             : 
     192             : enum parser_state {
     193             :     ON_WEEKDAY,
     194             :     ON_SPACE_DELIM,
     195             :     ON_YEAR,
     196             :     ON_MONTH,
     197             :     ON_MONTH_DAY,
     198             :     ON_HOUR,
     199             :     ON_MINUTE,
     200             :     ON_SECOND,
     201             :     ON_TZ,
     202             :     FINISHED,
     203             : };
     204             : 
     205           0 : static int s_parse_iso_8601_basic(const struct aws_byte_cursor *date_str_cursor, struct tm *parsed_time) {
     206           0 :     size_t index = 0;
     207           0 :     size_t state_start_index = 0;
     208           0 :     enum parser_state state = ON_YEAR;
     209           0 :     bool error = false;
     210           0 : 
     211           0 :     AWS_ZERO_STRUCT(*parsed_time);
     212           0 : 
     213           0 :     while (state < FINISHED && !error && index < date_str_cursor->len) {
     214           0 :         char c = date_str_cursor->ptr[index];
     215           0 :         size_t sub_index = index - state_start_index;
     216           0 :         switch (state) {
     217           0 :             case ON_YEAR:
     218           0 :                 if (aws_isdigit(c)) {
     219           0 :                     parsed_time->tm_year = parsed_time->tm_year * 10 + (c - '0');
     220           0 :                     if (sub_index == 3) {
     221           0 :                         state = ON_MONTH;
     222           0 :                         state_start_index = index + 1;
     223           0 :                         parsed_time->tm_year -= 1900;
     224           0 :                     }
     225           0 :                 } else {
     226           0 :                     error = true;
     227           0 :                 }
     228           0 :                 break;
     229           0 : 
     230           0 :             case ON_MONTH:
     231           0 :                 if (aws_isdigit(c)) {
     232           0 :                     parsed_time->tm_mon = parsed_time->tm_mon * 10 + (c - '0');
     233           0 :                     if (sub_index == 1) {
     234           0 :                         state = ON_MONTH_DAY;
     235           0 :                         state_start_index = index + 1;
     236           0 :                         parsed_time->tm_mon -= 1;
     237           0 :                     }
     238           0 :                 } else {
     239           0 :                     error = true;
     240           0 :                 }
     241           0 :                 break;
     242           0 : 
     243           0 :             case ON_MONTH_DAY:
     244           0 :                 if (c == 'T' && sub_index == 2) {
     245           0 :                     state = ON_HOUR;
     246           0 :                     state_start_index = index + 1;
     247           0 :                 } else if (aws_isdigit(c)) {
     248           0 :                     parsed_time->tm_mday = parsed_time->tm_mday * 10 + (c - '0');
     249           0 :                 } else {
     250           0 :                     error = true;
     251           0 :                 }
     252           0 :                 break;
     253           0 : 
     254           0 :             case ON_HOUR:
     255           0 :                 if (aws_isdigit(c)) {
     256           0 :                     parsed_time->tm_hour = parsed_time->tm_hour * 10 + (c - '0');
     257           0 :                     if (sub_index == 1) {
     258           0 :                         state = ON_MINUTE;
     259           0 :                         state_start_index = index + 1;
     260           0 :                     }
     261           0 :                 } else {
     262           0 :                     error = true;
     263           0 :                 }
     264           0 :                 break;
     265           0 : 
     266           0 :             case ON_MINUTE:
     267           0 :                 if (aws_isdigit(c)) {
     268           0 :                     parsed_time->tm_min = parsed_time->tm_min * 10 + (c - '0');
     269           0 :                     if (sub_index == 1) {
     270           0 :                         state = ON_SECOND;
     271           0 :                         state_start_index = index + 1;
     272           0 :                     }
     273           0 :                 } else {
     274           0 :                     error = true;
     275           0 :                 }
     276           0 :                 break;
     277           0 : 
     278           0 :             case ON_SECOND:
     279           0 :                 if (aws_isdigit(c)) {
     280           0 :                     parsed_time->tm_sec = parsed_time->tm_sec * 10 + (c - '0');
     281           0 :                     if (sub_index == 1) {
     282           0 :                         state = ON_TZ;
     283           0 :                         state_start_index = index + 1;
     284           0 :                     }
     285           0 :                 } else {
     286           0 :                     error = true;
     287           0 :                 }
     288           0 :                 break;
     289           0 : 
     290           0 :             case ON_TZ:
     291           0 :                 if (c == 'Z' && (sub_index == 0 || sub_index == 3)) {
     292           0 :                     state = FINISHED;
     293           0 :                 } else if (!aws_isdigit(c) || sub_index > 3) {
     294           0 :                     error = true;
     295           0 :                 }
     296           0 :                 break;
     297           0 : 
     298           0 :             default:
     299           0 :                 error = true;
     300           0 :                 break;
     301           0 :         }
     302           0 : 
     303           0 :         index++;
     304           0 :     }
     305           0 : 
     306           0 :     /* ISO8601 supports date only with no time portion. state ==ON_MONTH_DAY catches this case. */
     307           0 :     return (state == FINISHED || state == ON_MONTH_DAY) && !error ? AWS_OP_SUCCESS : AWS_OP_ERR;
     308           0 : }
     309             : 
     310           0 : static int s_parse_iso_8601(const struct aws_byte_cursor *date_str_cursor, struct tm *parsed_time) {
     311           0 :     size_t index = 0;
     312           0 :     size_t state_start_index = 0;
     313           0 :     enum parser_state state = ON_YEAR;
     314           0 :     bool error = false;
     315           0 :     bool advance = true;
     316           0 : 
     317           0 :     AWS_ZERO_STRUCT(*parsed_time);
     318           0 : 
     319           0 :     while (state < FINISHED && !error && index < date_str_cursor->len) {
     320           0 :         char c = date_str_cursor->ptr[index];
     321           0 :         switch (state) {
     322           0 :             case ON_YEAR:
     323           0 :                 if (c == '-' && index - state_start_index == 4) {
     324           0 :                     state = ON_MONTH;
     325           0 :                     state_start_index = index + 1;
     326           0 :                     parsed_time->tm_year -= 1900;
     327           0 :                 } else if (aws_isdigit(c)) {
     328           0 :                     parsed_time->tm_year = parsed_time->tm_year * 10 + (c - '0');
     329           0 :                 } else {
     330           0 :                     error = true;
     331           0 :                 }
     332           0 :                 break;
     333           0 :             case ON_MONTH:
     334           0 :                 if (c == '-' && index - state_start_index == 2) {
     335           0 :                     state = ON_MONTH_DAY;
     336           0 :                     state_start_index = index + 1;
     337           0 :                     parsed_time->tm_mon -= 1;
     338           0 :                 } else if (aws_isdigit(c)) {
     339           0 :                     parsed_time->tm_mon = parsed_time->tm_mon * 10 + (c - '0');
     340           0 :                 } else {
     341           0 :                     error = true;
     342           0 :                 }
     343           0 : 
     344           0 :                 break;
     345           0 :             case ON_MONTH_DAY:
     346           0 :                 if (c == 'T' && index - state_start_index == 2) {
     347           0 :                     state = ON_HOUR;
     348           0 :                     state_start_index = index + 1;
     349           0 :                 } else if (aws_isdigit(c)) {
     350           0 :                     parsed_time->tm_mday = parsed_time->tm_mday * 10 + (c - '0');
     351           0 :                 } else {
     352           0 :                     error = true;
     353           0 :                 }
     354           0 :                 break;
     355           0 :             /* note: no time portion is spec compliant. */
     356           0 :             case ON_HOUR:
     357           0 :                 /* time parts can be delimited by ':' or just concatenated together, but must always be 2 digits. */
     358           0 :                 if (index - state_start_index == 2) {
     359           0 :                     state = ON_MINUTE;
     360           0 :                     state_start_index = index + 1;
     361           0 :                     if (aws_isdigit(c)) {
     362           0 :                         state_start_index = index;
     363           0 :                         advance = false;
     364           0 :                     } else if (c != ':') {
     365           0 :                         error = true;
     366           0 :                     }
     367           0 :                 } else if (aws_isdigit(c)) {
     368           0 :                     parsed_time->tm_hour = parsed_time->tm_hour * 10 + (c - '0');
     369           0 :                 } else {
     370           0 :                     error = true;
     371           0 :                 }
     372           0 : 
     373           0 :                 break;
     374           0 :             case ON_MINUTE:
     375           0 :                 /* time parts can be delimited by ':' or just concatenated together, but must always be 2 digits. */
     376           0 :                 if (index - state_start_index == 2) {
     377           0 :                     state = ON_SECOND;
     378           0 :                     state_start_index = index + 1;
     379           0 :                     if (aws_isdigit(c)) {
     380           0 :                         state_start_index = index;
     381           0 :                         advance = false;
     382           0 :                     } else if (c != ':') {
     383           0 :                         error = true;
     384           0 :                     }
     385           0 :                 } else if (aws_isdigit(c)) {
     386           0 :                     parsed_time->tm_min = parsed_time->tm_min * 10 + (c - '0');
     387           0 :                 } else {
     388           0 :                     error = true;
     389           0 :                 }
     390           0 : 
     391           0 :                 break;
     392           0 :             case ON_SECOND:
     393           0 :                 if (c == 'Z' && index - state_start_index == 2) {
     394           0 :                     state = FINISHED;
     395           0 :                     state_start_index = index + 1;
     396           0 :                 } else if (c == '.' && index - state_start_index == 2) {
     397           0 :                     state = ON_TZ;
     398           0 :                     state_start_index = index + 1;
     399           0 :                 } else if (aws_isdigit(c)) {
     400           0 :                     parsed_time->tm_sec = parsed_time->tm_sec * 10 + (c - '0');
     401           0 :                 } else {
     402           0 :                     error = true;
     403           0 :                 }
     404           0 : 
     405           0 :                 break;
     406           0 :             case ON_TZ:
     407           0 :                 if (c == 'Z') {
     408           0 :                     state = FINISHED;
     409           0 :                     state_start_index = index + 1;
     410           0 :                 } else if (!aws_isdigit(c)) {
     411           0 :                     error = true;
     412           0 :                 }
     413           0 :                 break;
     414           0 :             default:
     415           0 :                 error = true;
     416           0 :                 break;
     417           0 :         }
     418           0 : 
     419           0 :         if (advance) {
     420           0 :             index++;
     421           0 :         } else {
     422           0 :             advance = true;
     423           0 :         }
     424           0 :     }
     425           0 : 
     426           0 :     /* ISO8601 supports date only with no time portion. state ==ON_MONTH_DAY catches this case. */
     427           0 :     return (state == FINISHED || state == ON_MONTH_DAY) && !error ? AWS_OP_SUCCESS : AWS_OP_ERR;
     428           0 : }
     429             : 
     430             : static int s_parse_rfc_822(
     431             :     const struct aws_byte_cursor *date_str_cursor,
     432             :     struct tm *parsed_time,
     433           0 :     struct aws_date_time *dt) {
     434           0 :     size_t len = date_str_cursor->len;
     435           0 : 
     436           0 :     size_t index = 0;
     437           0 :     size_t state_start_index = 0;
     438           0 :     int state = ON_WEEKDAY;
     439           0 :     bool error = false;
     440           0 : 
     441           0 :     AWS_ZERO_STRUCT(*parsed_time);
     442           0 : 
     443           0 :     while (!error && index < len) {
     444           0 :         char c = date_str_cursor->ptr[index];
     445           0 : 
     446           0 :         switch (state) {
     447           0 :             /* week day abbr is optional. */
     448           0 :             case ON_WEEKDAY:
     449           0 :                 if (c == ',') {
     450           0 :                     state = ON_SPACE_DELIM;
     451           0 :                     state_start_index = index + 1;
     452           0 :                 } else if (aws_isdigit(c)) {
     453           0 :                     state = ON_MONTH_DAY;
     454           0 :                 } else if (!aws_isalpha(c)) {
     455           0 :                     error = true;
     456           0 :                 }
     457           0 :                 break;
     458           0 :             case ON_SPACE_DELIM:
     459           0 :                 if (aws_isspace(c)) {
     460           0 :                     state = ON_MONTH_DAY;
     461           0 :                     state_start_index = index + 1;
     462           0 :                 } else {
     463           0 :                     error = true;
     464           0 :                 }
     465           0 :                 break;
     466           0 :             case ON_MONTH_DAY:
     467           0 :                 if (aws_isdigit(c)) {
     468           0 :                     parsed_time->tm_mday = parsed_time->tm_mday * 10 + (c - '0');
     469           0 :                 } else if (aws_isspace(c)) {
     470           0 :                     state = ON_MONTH;
     471           0 :                     state_start_index = index + 1;
     472           0 :                 } else {
     473           0 :                     error = true;
     474           0 :                 }
     475           0 :                 break;
     476           0 :             case ON_MONTH:
     477           0 :                 if (aws_isspace(c)) {
     478           0 :                     int monthNumber =
     479           0 :                         get_month_number_from_str((const char *)date_str_cursor->ptr, state_start_index, index + 1);
     480           0 : 
     481           0 :                     if (monthNumber > -1) {
     482           0 :                         state = ON_YEAR;
     483           0 :                         state_start_index = index + 1;
     484           0 :                         parsed_time->tm_mon = monthNumber;
     485           0 :                     } else {
     486           0 :                         error = true;
     487           0 :                     }
     488           0 :                 } else if (!aws_isalpha(c)) {
     489           0 :                     error = true;
     490           0 :                 }
     491           0 :                 break;
     492           0 :             /* year can be 4 or 2 digits. */
     493           0 :             case ON_YEAR:
     494           0 :                 if (aws_isspace(c) && index - state_start_index == 4) {
     495           0 :                     state = ON_HOUR;
     496           0 :                     state_start_index = index + 1;
     497           0 :                     parsed_time->tm_year -= 1900;
     498           0 :                 } else if (aws_isspace(c) && index - state_start_index == 2) {
     499           0 :                     state = 5;
     500           0 :                     state_start_index = index + 1;
     501           0 :                     parsed_time->tm_year += 2000 - 1900;
     502           0 :                 } else if (aws_isdigit(c)) {
     503           0 :                     parsed_time->tm_year = parsed_time->tm_year * 10 + (c - '0');
     504           0 :                 } else {
     505           0 :                     error = true;
     506           0 :                 }
     507           0 :                 break;
     508           0 :             case ON_HOUR:
     509           0 :                 if (c == ':' && index - state_start_index == 2) {
     510           0 :                     state = ON_MINUTE;
     511           0 :                     state_start_index = index + 1;
     512           0 :                 } else if (aws_isdigit(c)) {
     513           0 :                     parsed_time->tm_hour = parsed_time->tm_hour * 10 + (c - '0');
     514           0 :                 } else {
     515           0 :                     error = true;
     516           0 :                 }
     517           0 :                 break;
     518           0 :             case ON_MINUTE:
     519           0 :                 if (c == ':' && index - state_start_index == 2) {
     520           0 :                     state = ON_SECOND;
     521           0 :                     state_start_index = index + 1;
     522           0 :                 } else if (aws_isdigit(c)) {
     523           0 :                     parsed_time->tm_min = parsed_time->tm_min * 10 + (c - '0');
     524           0 :                 } else {
     525           0 :                     error = true;
     526           0 :                 }
     527           0 :                 break;
     528           0 :             case ON_SECOND:
     529           0 :                 if (aws_isspace(c) && index - state_start_index == 2) {
     530           0 :                     state = ON_TZ;
     531           0 :                     state_start_index = index + 1;
     532           0 :                 } else if (aws_isdigit(c)) {
     533           0 :                     parsed_time->tm_sec = parsed_time->tm_sec * 10 + (c - '0');
     534           0 :                 } else {
     535           0 :                     error = true;
     536           0 :                 }
     537           0 :                 break;
     538           0 :             case ON_TZ:
     539           0 :                 if ((aws_isalnum(c) || c == '-' || c == '+') && (index - state_start_index) < 5) {
     540           0 :                     dt->tz[index - state_start_index] = c;
     541           0 :                 } else {
     542           0 :                     error = true;
     543           0 :                 }
     544           0 : 
     545           0 :                 break;
     546           0 :             default:
     547           0 :                 error = true;
     548           0 :                 break;
     549           0 :         }
     550           0 : 
     551           0 :         index++;
     552           0 :     }
     553           0 : 
     554           0 :     if (dt->tz[0] != 0) {
     555           0 :         if (is_utc_time_zone(dt->tz)) {
     556           0 :             dt->utc_assumed = true;
     557           0 :         } else {
     558           0 :             error = true;
     559           0 :         }
     560           0 :     }
     561           0 : 
     562           0 :     return error || state != ON_TZ ? AWS_OP_ERR : AWS_OP_SUCCESS;
     563           0 : }
     564             : 
     565             : int aws_date_time_init_from_str_cursor(
     566             :     struct aws_date_time *dt,
     567             :     const struct aws_byte_cursor *date_str_cursor,
     568           0 :     enum aws_date_format fmt) {
     569           0 :     AWS_ERROR_PRECONDITION(date_str_cursor->len <= AWS_DATE_TIME_STR_MAX_LEN, AWS_ERROR_OVERFLOW_DETECTED);
     570           0 : 
     571           0 :     AWS_ZERO_STRUCT(*dt);
     572           0 : 
     573           0 :     struct tm parsed_time;
     574           0 :     bool successfully_parsed = false;
     575           0 : 
     576           0 :     time_t seconds_offset = 0;
     577           0 :     if (fmt == AWS_DATE_FORMAT_ISO_8601 || fmt == AWS_DATE_FORMAT_AUTO_DETECT) {
     578           0 :         if (!s_parse_iso_8601(date_str_cursor, &parsed_time)) {
     579           0 :             dt->utc_assumed = true;
     580           0 :             successfully_parsed = true;
     581           0 :         }
     582           0 :     }
     583           0 : 
     584           0 :     if (fmt == AWS_DATE_FORMAT_ISO_8601_BASIC || (fmt == AWS_DATE_FORMAT_AUTO_DETECT && !successfully_parsed)) {
     585           0 :         if (!s_parse_iso_8601_basic(date_str_cursor, &parsed_time)) {
     586           0 :             dt->utc_assumed = true;
     587           0 :             successfully_parsed = true;
     588           0 :         }
     589           0 :     }
     590           0 : 
     591           0 :     if (fmt == AWS_DATE_FORMAT_RFC822 || (fmt == AWS_DATE_FORMAT_AUTO_DETECT && !successfully_parsed)) {
     592           0 :         if (!s_parse_rfc_822(date_str_cursor, &parsed_time, dt)) {
     593           0 :             successfully_parsed = true;
     594           0 : 
     595           0 :             if (dt->utc_assumed) {
     596           0 :                 if (dt->tz[0] == '+' || dt->tz[0] == '-') {
     597           0 :                     /* in this format, the offset is in format +/-HHMM so convert that to seconds and we'll use
     598           0 :                      * the offset later. */
     599           0 :                     char min_str[3] = {0};
     600           0 :                     char hour_str[3] = {0};
     601           0 :                     hour_str[0] = dt->tz[1];
     602           0 :                     hour_str[1] = dt->tz[2];
     603           0 :                     min_str[0] = dt->tz[3];
     604           0 :                     min_str[1] = dt->tz[4];
     605           0 : 
     606           0 :                     long hour = strtol(hour_str, NULL, 10);
     607           0 :                     long min = strtol(min_str, NULL, 10);
     608           0 :                     seconds_offset = (time_t)(hour * 3600 + min * 60);
     609           0 : 
     610           0 :                     if (dt->tz[0] == '-') {
     611           0 :                         seconds_offset = -seconds_offset;
     612           0 :                     }
     613           0 :                 }
     614           0 :             }
     615           0 :         }
     616           0 :     }
     617           0 : 
     618           0 :     if (!successfully_parsed) {
     619           0 :         return aws_raise_error(AWS_ERROR_INVALID_DATE_STR);
     620           0 :     }
     621           0 : 
     622           0 :     if (dt->utc_assumed || seconds_offset) {
     623           0 :         dt->timestamp = aws_timegm(&parsed_time);
     624           0 :     } else {
     625           0 :         dt->timestamp = mktime(&parsed_time);
     626           0 :     }
     627           0 : 
     628           0 :     /* negative means we need to move west (increase the timestamp), positive means head east, so decrease the
     629           0 :      * timestamp. */
     630           0 :     dt->timestamp -= seconds_offset;
     631           0 : 
     632           0 :     dt->gmt_time = s_get_time_struct(dt, false);
     633           0 :     dt->local_time = s_get_time_struct(dt, true);
     634           0 : 
     635           0 :     return AWS_OP_SUCCESS;
     636           0 : }
     637             : 
     638             : int aws_date_time_init_from_str(
     639             :     struct aws_date_time *dt,
     640             :     const struct aws_byte_buf *date_str,
     641           0 :     enum aws_date_format fmt) {
     642           0 :     AWS_ERROR_PRECONDITION(date_str->len <= AWS_DATE_TIME_STR_MAX_LEN, AWS_ERROR_OVERFLOW_DETECTED);
     643           0 : 
     644           0 :     struct aws_byte_cursor date_cursor = aws_byte_cursor_from_buf(date_str);
     645           0 :     return aws_date_time_init_from_str_cursor(dt, &date_cursor, fmt);
     646           0 : }
     647             : 
     648           0 : static inline int s_date_to_str(const struct tm *tm, const char *format_str, struct aws_byte_buf *output_buf) {
     649           0 :     size_t remaining_space = output_buf->capacity - output_buf->len;
     650           0 :     size_t bytes_written = strftime((char *)output_buf->buffer + output_buf->len, remaining_space, format_str, tm);
     651           0 : 
     652           0 :     if (bytes_written == 0) {
     653           0 :         return aws_raise_error(AWS_ERROR_SHORT_BUFFER);
     654           0 :     }
     655           0 : 
     656           0 :     output_buf->len += bytes_written;
     657           0 : 
     658           0 :     return AWS_OP_SUCCESS;
     659           0 : }
     660             : 
     661             : int aws_date_time_to_local_time_str(
     662             :     const struct aws_date_time *dt,
     663             :     enum aws_date_format fmt,
     664           0 :     struct aws_byte_buf *output_buf) {
     665           0 :     AWS_ASSERT(fmt != AWS_DATE_FORMAT_AUTO_DETECT);
     666           0 : 
     667           0 :     switch (fmt) {
     668           0 :         case AWS_DATE_FORMAT_RFC822:
     669           0 :             return s_date_to_str(&dt->local_time, RFC822_DATE_FORMAT_STR_WITH_Z, output_buf);
     670           0 : 
     671           0 :         case AWS_DATE_FORMAT_ISO_8601:
     672           0 :             return s_date_to_str(&dt->local_time, ISO_8601_LONG_DATE_FORMAT_STR, output_buf);
     673           0 : 
     674           0 :         case AWS_DATE_FORMAT_ISO_8601_BASIC:
     675           0 :             return s_date_to_str(&dt->local_time, ISO_8601_LONG_BASIC_DATE_FORMAT_STR, output_buf);
     676           0 : 
     677           0 :         default:
     678           0 :             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
     679           0 :     }
     680           0 : }
     681             : 
     682             : int aws_date_time_to_utc_time_str(
     683             :     const struct aws_date_time *dt,
     684             :     enum aws_date_format fmt,
     685           0 :     struct aws_byte_buf *output_buf) {
     686           0 :     AWS_ASSERT(fmt != AWS_DATE_FORMAT_AUTO_DETECT);
     687           0 : 
     688           0 :     switch (fmt) {
     689           0 :         case AWS_DATE_FORMAT_RFC822:
     690           0 :             return s_date_to_str(&dt->gmt_time, RFC822_DATE_FORMAT_STR_MINUS_Z, output_buf);
     691           0 : 
     692           0 :         case AWS_DATE_FORMAT_ISO_8601:
     693           0 :             return s_date_to_str(&dt->gmt_time, ISO_8601_LONG_DATE_FORMAT_STR, output_buf);
     694           0 : 
     695           0 :         case AWS_DATE_FORMAT_ISO_8601_BASIC:
     696           0 :             return s_date_to_str(&dt->gmt_time, ISO_8601_LONG_BASIC_DATE_FORMAT_STR, output_buf);
     697           0 : 
     698           0 :         default:
     699           0 :             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
     700           0 :     }
     701           0 : }
     702             : 
     703             : int aws_date_time_to_local_time_short_str(
     704             :     const struct aws_date_time *dt,
     705             :     enum aws_date_format fmt,
     706           0 :     struct aws_byte_buf *output_buf) {
     707           0 :     AWS_ASSERT(fmt != AWS_DATE_FORMAT_AUTO_DETECT);
     708           0 : 
     709           0 :     switch (fmt) {
     710           0 :         case AWS_DATE_FORMAT_RFC822:
     711           0 :             return s_date_to_str(&dt->local_time, RFC822_SHORT_DATE_FORMAT_STR, output_buf);
     712           0 : 
     713           0 :         case AWS_DATE_FORMAT_ISO_8601:
     714           0 :             return s_date_to_str(&dt->local_time, ISO_8601_SHORT_DATE_FORMAT_STR, output_buf);
     715           0 : 
     716           0 :         case AWS_DATE_FORMAT_ISO_8601_BASIC:
     717           0 :             return s_date_to_str(&dt->local_time, ISO_8601_SHORT_BASIC_DATE_FORMAT_STR, output_buf);
     718           0 : 
     719           0 :         default:
     720           0 :             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
     721           0 :     }
     722           0 : }
     723             : 
     724             : int aws_date_time_to_utc_time_short_str(
     725             :     const struct aws_date_time *dt,
     726             :     enum aws_date_format fmt,
     727           0 :     struct aws_byte_buf *output_buf) {
     728           0 :     AWS_ASSERT(fmt != AWS_DATE_FORMAT_AUTO_DETECT);
     729           0 : 
     730           0 :     switch (fmt) {
     731           0 :         case AWS_DATE_FORMAT_RFC822:
     732           0 :             return s_date_to_str(&dt->gmt_time, RFC822_SHORT_DATE_FORMAT_STR, output_buf);
     733           0 : 
     734           0 :         case AWS_DATE_FORMAT_ISO_8601:
     735           0 :             return s_date_to_str(&dt->gmt_time, ISO_8601_SHORT_DATE_FORMAT_STR, output_buf);
     736           0 : 
     737           0 :         case AWS_DATE_FORMAT_ISO_8601_BASIC:
     738           0 :             return s_date_to_str(&dt->gmt_time, ISO_8601_SHORT_BASIC_DATE_FORMAT_STR, output_buf);
     739           0 : 
     740           0 :         default:
     741           0 :             return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
     742           0 :     }
     743           0 : }
     744             : 
     745           0 : double aws_date_time_as_epoch_secs(const struct aws_date_time *dt) {
     746           0 :     return (double)dt->timestamp;
     747           0 : }
     748             : 
     749           0 : uint64_t aws_date_time_as_nanos(const struct aws_date_time *dt) {
     750           0 :     return (uint64_t)dt->timestamp * AWS_TIMESTAMP_NANOS;
     751           0 : }
     752             : 
     753           0 : uint64_t aws_date_time_as_millis(const struct aws_date_time *dt) {
     754           0 :     return (uint64_t)dt->timestamp * AWS_TIMESTAMP_MILLIS;
     755           0 : }
     756             : 
     757           0 : uint16_t aws_date_time_year(const struct aws_date_time *dt, bool local_time) {
     758           0 :     const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
     759           0 : 
     760           0 :     return (uint16_t)(time->tm_year + 1900);
     761           0 : }
     762             : 
     763           0 : enum aws_date_month aws_date_time_month(const struct aws_date_time *dt, bool local_time) {
     764           0 :     const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
     765           0 : 
     766           0 :     return time->tm_mon;
     767           0 : }
     768             : 
     769           0 : uint8_t aws_date_time_month_day(const struct aws_date_time *dt, bool local_time) {
     770           0 :     const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
     771           0 : 
     772           0 :     return (uint8_t)time->tm_mday;
     773           0 : }
     774             : 
     775           0 : enum aws_date_day_of_week aws_date_time_day_of_week(const struct aws_date_time *dt, bool local_time) {
     776           0 :     const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
     777           0 : 
     778           0 :     return time->tm_wday;
     779           0 : }
     780             : 
     781           0 : uint8_t aws_date_time_hour(const struct aws_date_time *dt, bool local_time) {
     782           0 :     const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
     783           0 : 
     784           0 :     return (uint8_t)time->tm_hour;
     785           0 : }
     786             : 
     787           0 : uint8_t aws_date_time_minute(const struct aws_date_time *dt, bool local_time) {
     788           0 :     const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
     789           0 : 
     790           0 :     return (uint8_t)time->tm_min;
     791           0 : }
     792             : 
     793           0 : uint8_t aws_date_time_second(const struct aws_date_time *dt, bool local_time) {
     794           0 :     const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
     795           0 : 
     796           0 :     return (uint8_t)time->tm_sec;
     797           0 : }
     798             : 
     799           0 : bool aws_date_time_dst(const struct aws_date_time *dt, bool local_time) {
     800           0 :     const struct tm *time = local_time ? &dt->local_time : &dt->gmt_time;
     801           0 : 
     802           0 :     return (bool)time->tm_isdst;
     803           0 : }
     804             : 
     805           0 : time_t aws_date_time_diff(const struct aws_date_time *a, const struct aws_date_time *b) {
     806           0 :     return a->timestamp - b->timestamp;
     807           0 : }

Generated by: LCOV version 1.13