datetime __cdecl datetime::from_string_maximum_error()

in Release/src/utilities/asyncrt_utils.cpp [1012:1388]


datetime __cdecl datetime::from_string_maximum_error(const utility::string_t& dateString, date_format format)
{
    datetime result = datetime::maximum();
    int64_t secondsSince1601;
    uint64_t fracSec = 0;
    auto str = dateString.c_str();
    if (format == RFC_1123)
    {
        int parsedWeekday = 0;
        for (; parsedWeekday < 7; ++parsedWeekday)
        {
            if (string_starts_with(str, dayNames + parsedWeekday * 4) && str[3] == _XPLATSTR(',') &&
                str[4] == _XPLATSTR(' '))
            {
                str += 5; // parsed day of week
                break;
            }
        }

        int monthDay;
        if (ascii_isdigit3(str[0]) && ascii_isdigit(str[1]) && str[2] == _XPLATSTR(' '))
        {
            monthDay = atoi2(str); // validity checked later
            str += 3;              // parsed day
        }
        else if (ascii_isdigit(str[0]) && str[1] == _XPLATSTR(' '))
        {
            monthDay = str[0] - _XPLATSTR('0');
            str += 2; // parsed day
        }
        else
        {
            return result;
        }

        if (monthDay == 0)
        {
            return result;
        }

        int month = 0;
        for (;;)
        {
            if (string_starts_with(str, monthNames + month * 4))
            {
                break;
            }

            ++month;
            if (month == 12)
            {
                return result;
            }
        }

        if (str[3] != _XPLATSTR(' '))
        {
            return result;
        }

        str += 4; // parsed month

        if (!ascii_isdigit(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]) ||
            str[4] != ' ')
        {
            return result;
        }

        int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 +
                   (str[3] - _XPLATSTR('0'));
        if (year < 1601)
        {
            return result;
        }

        year -= 1601;

        // days in month validity check
        if (!validate_day_month_1601(monthDay, month, year))
        {
            return result;
        }

        str += 5; // parsed year
        const int yearDay = get_year_day_1601(month, monthDay, year);

        if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]) || str[2] != _XPLATSTR(':') || !ascii_isdigit5(str[3]) ||
            !ascii_isdigit(str[4]))
        {
            return result;
        }

        const int hour = atoi2(str);
        if (hour > 23)
        {
            return result;
        }

        str += 3; // parsed hour
        const int minute = atoi2(str);
        str += 2; // parsed mins

        int sec;
        if (str[0] == ':')
        {
            if (!ascii_isdigit6(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(' '))
            {
                return result;
            }

            sec = atoi2(str + 1);
            str += 4; // parsed seconds
        }
        else if (str[0] == _XPLATSTR(' '))
        {
            sec = 0;
            str += 1; // parsed seconds
        }
        else
        {
            return result;
        }

        if (sec > 60)
        { // 60 to allow leap seconds
            return result;
        }

        int daysSince1601 = year * DaysInYear + count_leap_years_1601(year) + yearDay;

        if (parsedWeekday != 7)
        {
            const int actualWeekday = (daysSince1601 + 1) % 7;

            if (parsedWeekday != actualWeekday)
            {
                return result;
            }
        }

        secondsSince1601 =
            static_cast<int64_t>(daysSince1601) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;

        if (!string_starts_with(str, "GMT") && !string_starts_with(str, "UT"))
        {
            // some timezone adjustment necessary
            auto tzCh = _XPLATSTR('-');
            int tzHours;
            int tzMinutes = 0;
            if (string_starts_with(str, "EDT"))
            {
                tzHours = 4;
            }
            else if (string_starts_with(str, "EST") || string_starts_with(str, "CDT"))
            {
                tzHours = 5;
            }
            else if (string_starts_with(str, "CST") || string_starts_with(str, "MDT"))
            {
                tzHours = 6;
            }
            else if (string_starts_with(str, "MST") || string_starts_with(str, "PDT"))
            {
                tzHours = 7;
            }
            else if (string_starts_with(str, "PST"))
            {
                tzHours = 8;
            }
            else if ((str[0] == _XPLATSTR('+') || str[0] == _XPLATSTR('-')) && ascii_isdigit2(str[1]) &&
                     ascii_isdigit(str[2]) && ascii_isdigit5(str[3]) && ascii_isdigit(str[4]))
            {
                tzCh = str[0];
                tzHours = atoi2(str + 1);
                tzMinutes = atoi2(str + 3);
            }
            else
            {
                return result;
            }

            secondsSince1601 = timezone_adjust(secondsSince1601, static_cast<unsigned char>(tzCh), tzHours, tzMinutes);
            if (secondsSince1601 < 0)
            {
                return result;
            }
        }
    }
    else if (format == ISO_8601)
    {
        // parse year
        if (!ascii_isdigit(str[0]) || !ascii_isdigit(str[1]) || !ascii_isdigit(str[2]) || !ascii_isdigit(str[3]))
        {
            return result;
        }

        int year = (str[0] - _XPLATSTR('0')) * 1000 + (str[1] - _XPLATSTR('0')) * 100 + (str[2] - _XPLATSTR('0')) * 10 +
                   (str[3] - _XPLATSTR('0'));
        if (year < 1601)
        {
            return result;
        }

        year -= 1601;

        str += 4;
        if (*str == _XPLATSTR('-'))
        {
            ++str;
        }

        // parse month
        if (!ascii_isdigit1(str[0]) || !ascii_isdigit(str[1]))
        {
            return result;
        }

        int month = atoi2(str);
        if (month < 1 || month > 12)
        {
            return result;
        }

        month -= 1;
        str += 2;

        if (*str == _XPLATSTR('-'))
        {
            ++str;
        }

        // parse day
        if (!ascii_isdigit3(str[0]) || !ascii_isdigit(str[1]))
        {
            return result;
        }

        int monthDay = atoi2(str);
        if (!validate_day_month_1601(monthDay, month, year))
        {
            return result;
        }

        const int yearDay = get_year_day_1601(month, monthDay, year);

        str += 2;
        int daysSince1601 = year * DaysInYear + count_leap_years_1601(year) + yearDay;

        if (str[0] != _XPLATSTR('T') && str[0] != _XPLATSTR('t'))
        {
            // No time
            secondsSince1601 = static_cast<int64_t>(daysSince1601) * SecondsInDay;

            result.m_interval = static_cast<interval_type>(secondsSince1601 * _secondTicks + fracSec);
            return result;
        }

        ++str; // skip 'T'

        // parse hour
        if (!ascii_isdigit2(str[0]) || !ascii_isdigit(str[1]))
        {
            return result;
        }

        const int hour = atoi2(str);
        str += 2;
        if (hour > 23)
        {
            return result;
        }

        if (*str == _XPLATSTR(':'))
        {
            ++str;
        }

        // parse minute
        if (!ascii_isdigit5(str[0]) || !ascii_isdigit(str[1]))
        {
            return result;
        }

        const int minute = atoi2(str);
        // minute > 59 is impossible because we checked that the first digit is <= 5 in the basic format
        // check above

        str += 2;

        if (*str == _XPLATSTR(':'))
        {
            ++str;
        }

        // parse seconds
        if (!ascii_isdigit6(str[0]) || !ascii_isdigit(str[1]))
        {
            return result;
        }

        const int sec = atoi2(str);
        // We allow 60 to account for leap seconds
        if (sec > 60)
        {
            return result;
        }

        str += 2;
        if (str[0] == _XPLATSTR('.') && ascii_isdigit(str[1]))
        {
            ++str;
            int digits = 7;
            for (;;)
            {
                fracSec *= 10;
                fracSec += *str - _XPLATSTR('0');
                --digits;
                ++str;
                if (digits == 0)
                {
                    while (ascii_isdigit(*str))
                    {
                        // consume remaining fractional second digits we can't use
                        ++str;
                    }

                    break;
                }

                if (!ascii_isdigit(*str))
                {
                    // no more digits in the input, do the remaining multiplies we need
                    for (; digits != 0; --digits)
                    {
                        fracSec *= 10;
                    }

                    break;
                }
            }
        }

        secondsSince1601 =
            static_cast<int64_t>(daysSince1601) * SecondsInDay + hour * SecondsInHour + minute * SecondsInMinute + sec;

        if (str[0] == _XPLATSTR('Z') || str[0] == _XPLATSTR('z'))
        {
            // no adjustment needed for zulu time
        }
        else if (str[0] == _XPLATSTR('+') || str[0] == _XPLATSTR('-'))
        {
            const unsigned char offsetDirection = static_cast<unsigned char>(str[0]);
            if (!ascii_isdigit2(str[1]) || !ascii_isdigit(str[2]) || str[3] != _XPLATSTR(':') ||
                !ascii_isdigit5(str[4]) || !ascii_isdigit(str[5]))
            {
                return result;
            }

            secondsSince1601 = timezone_adjust(secondsSince1601, offsetDirection, atoi2(str + 1), atoi2(str + 4));
            if (secondsSince1601 < 0)
            {
                return result;
            }
        }
        else
        {
            // the timezone is malformed, but cpprestsdk currently accepts this as no timezone
        }
    }
    else
    {
        throw std::invalid_argument("unrecognized date format");
    }

    result.m_interval = static_cast<interval_type>(secondsSince1601 * _secondTicks + fracSec);
    return result;
}