bool str_to_time()

in mysql_sys/my_time.cc [747:939]


bool str_to_time(const char *str, std::size_t length, MYSQL_TIME *l_time,
                 MYSQL_TIME_STATUS *status, my_time_flags_t flags) {
  ulong date[5];
  ulonglong value;
  const char *end = str + length;
  const char *end_of_days;
  bool found_days;
  bool found_hours;
  uint state;
  const char *start;
  bool seen_colon = false;

  assert(status->warnings == 0 && status->fractional_digits == 0 &&
         status->nanoseconds == 0);

  l_time->neg = false;
  for (; str != end && isspace_char(*str); str++) length--;
  if (str != end && *str == '-') {
    l_time->neg = true;
    str++;
    length--;
  }
  if (str == end) return true;

  // Remember beginning of first non-space/- char.
  start = str;

  /* Check first if this is a full TIMESTAMP */
  if (length >= 12) { /* Probably full timestamp */
    MYSQL_TIME_STATUS tmpstatus;
    (void)str_to_datetime(str, length, l_time,
                          (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &tmpstatus);
    if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR) {
      *status = tmpstatus;
      return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
    }
    assert(status->warnings == 0 && status->fractional_digits == 0 &&
           status->nanoseconds == 0);
  }

  /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
  for (value = 0; str != end && isdigit_char(*str); str++)
    value = value * 10L + static_cast<long>(*str - '0');

  if (value > UINT_MAX) return true;

  /* Skip all space after 'days' */
  end_of_days = str;
  for (; str != end && isspace_char(str[0]); str++)
    ;

  state = 0;
  found_days = found_hours = false;
  if (static_cast<uint>(end - str) > 1 && str != end_of_days &&
      isdigit_char(*str)) { /* Found days part */
    date[0] = static_cast<ulong>(value);
    state = 1; /* Assume next is hours */
    found_days = true;
  } else if ((end - str) > 1 && *str == time_separator &&
             isdigit_char(str[1])) {
    date[0] = 0; /* Assume we found hours */
    date[1] = static_cast<ulong>(value);
    state = 2;
    found_hours = true;
    str++; /* skip ':' */
    seen_colon = true;
  } else {
    /* String given as one number; assume HHMMSS format */
    date[0] = 0;
    date[1] = static_cast<ulong>(value / 10000);
    date[2] = static_cast<ulong>(value / 100 % 100);
    date[3] = static_cast<ulong>(value % 100);
    state = 4;
    goto fractional;
  }

  /* Read hours, minutes and seconds */
  for (;;) {
    for (value = 0; str != end && isdigit_char(*str); str++)
      value = value * 10L + static_cast<long>(*str - '0');
    date[state++] = static_cast<ulong>(value);
    if (state == 4 || (end - str) < 2 || *str != time_separator ||
        !isdigit_char(str[1]))
      break;
    str++; /* Skip time_separator (':') */
    seen_colon = true;
  }

  if (state != 4) { /* Not HH:MM:SS */
    /* Fix the date to assume that seconds was given */
    if (!found_hours && !found_days) {
      std::size_t len = sizeof(long) * (state - 1);
      memmove(pointer_cast<uchar *>(date + 4) - len,
              pointer_cast<uchar *>(date + state) - len, len);
      memset(date, 0, sizeof(long) * (4 - state));
    } else
      memset((date + state), 0, sizeof(long) * (4 - state));
  }

fractional:
  /* Get fractional second part */
  if ((end - str) >= 2 && *str == '.' && isdigit_char(str[1])) {
    int field_length = 5;
    str++;
    value = static_cast<uint>(static_cast<uchar>(*str - '0'));
    while (++str != end && isdigit_char(*str)) {
      if (field_length-- > 0)
        value = value * 10 + static_cast<uint>(static_cast<uchar>(*str - '0'));
    }
    if (field_length >= 0) {
      status->fractional_digits = DATETIME_MAX_DECIMALS - field_length;
      if (field_length > 0)
        value *= static_cast<long>(log_10_int[field_length]);
    } else {
      /* Scan digits left after microseconds */
      status->fractional_digits = 6;
      status->nanoseconds = 100 * (str[-1] - '0');
      for (; str != end && isdigit_char(*str); str++) {
      }
    }
    date[4] = static_cast<ulong>(value);
  } else if ((end - str) == 1 && *str == '.') {
    str++;
    date[4] = 0;
  } else
    date[4] = 0;

  /* Check for exponent part: E<gigit> | E<sign><digit> */
  /* (may occur as result of %g formatting of time value) */
  if ((end - str) > 1 && (*str == 'e' || *str == 'E') &&
      (isdigit_char(str[1]) || ((str[1] == '-' || str[1] == '+') &&
                                (end - str) > 2 && isdigit_char(str[2]))))
    return true;

  if (internal_format_positions[7] != 255) {
    /* Read a possible AM/PM */
    while (str != end && isspace_char(*str)) str++;
    if (str + 2 <= end && (str[1] == 'M' || str[1] == 'm')) {
      if (str[0] == 'p' || str[0] == 'P') {
        str += 2;
        date[1] = date[1] % 12 + 12;
      } else if (str[0] == 'a' || str[0] == 'A')
        str += 2;
    }
  }

  /* Integer overflow checks */
  if (date[0] > UINT_MAX || date[1] > UINT_MAX || date[2] > UINT_MAX ||
      date[3] > UINT_MAX || date[4] > UINT_MAX)
    return true;

  if (!seen_colon && (flags & TIME_STRICT_COLON)) {
    memset(l_time, 0, sizeof(*l_time));
    status->warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
    return true;
  }

  l_time->year = 0; /* For protocol::store_time */
  l_time->month = 0;

  l_time->day = 0;
  l_time->hour = date[1] + date[0] * 24; /* Mix days and hours */

  l_time->minute = date[2];
  l_time->second = date[3];
  l_time->second_part = date[4];
  l_time->time_type = MYSQL_TIMESTAMP_TIME;
  l_time->time_zone_displacement = 0;

  if (check_time_mmssff_range(*l_time)) {
    status->warnings |= MYSQL_TIME_WARN_OUT_OF_RANGE;
    return true;
  }

  /* Adjust the value into supported MYSQL_TIME range */
  adjust_time_range(l_time, &status->warnings);

  /* Check if there is garbage at end of the MYSQL_TIME specification */
  if (str != end) {
    do {
      if (!isspace_char(*str)) {
        status->warnings |= MYSQL_TIME_WARN_TRUNCATED;
        // No char was actually used in conversion - bad value
        if (str == start) {
          l_time->time_type = MYSQL_TIMESTAMP_NONE;
          return true;
        }
        break;
      }
    } while (++str != end);
  }
  return false;
}