void DateTimePreparser::ParseDateTime()

in source/shared/cpp/ObjectModel/DateTimePreparser.cpp [106:273]


void DateTimePreparser::ParseDateTime(const std::string& in)
{
    if (in.find("{{") != std::string::npos)
    {
        static const std::regex pattern(
            "\\{\\{((DATE)|(TIME))\\((\\d{4})-{1}(\\d{2})-{1}(\\d{2})T(\\d{2}):{1}(\\d{2}):{1}(\\d{2})(Z|(([+-])(\\d{2}"
            "):{1}(\\d{2})))((((, ?SHORT)|(, ?LONG))|(, ?COMPACT))|)\\)\\}\\}");
        std::smatch matches;
        std::string text = in;
        enum MatchIndex
        {
            IsDate = 2,
            Year = 4,
            Month,
            Day,
            Hour,
            Min,
            Sec,
            TimeZone = 12,
            TZHr,
            TZMn,
            Format,
            Style,
        };
        std::vector<int> indexer = {Year, Month, Day, Hour, Min, Sec, TZHr, TZMn};

        while (std::regex_search(text, matches, pattern))
        {
            int formatStyle{};
            // Date is matched
            const bool isDate = matches[IsDate].matched;
            int hours{}, minutes{};
            struct tm parsedTm
            {
            };
            std::vector<int*> addrs = {
                &parsedTm.tm_year, &parsedTm.tm_mon, &parsedTm.tm_mday, &parsedTm.tm_hour, &parsedTm.tm_min, &parsedTm.tm_sec, &hours, &minutes};

            if (matches[Style].matched)
            {
                // match for long/short/compact
                bool formatHasSpace = matches[Format].str().at(1) == ' ';
                const int formatStartIndex = formatHasSpace ? 2 : 1;
                formatStyle = matches[Format].str().at(formatStartIndex);
            }

            AddTextToken(matches.prefix().str(), DateTimePreparsedTokenFormat::RegularString);

            if (!isDate && formatStyle)
            {
                AddTextToken(matches[0].str(), DateTimePreparsedTokenFormat::RegularString);
                text = matches.suffix().str();
                continue;
            }

            for (unsigned int idx = 0; idx < indexer.size(); idx++)
            {
                if (matches[indexer.at(idx)].matched)
                {
                    // get indexes for time attributes to index into corresponding matches
                    // and convert it to string
                    *(addrs.at(idx)) = stoi(matches[indexer.at(idx)]);
                }
            }

            // check for date and time validation
            if (IsValidTimeAndDate(parsedTm, hours, minutes))
            {
                time_t offset{};
                // maches offset sign,
                // Z == UTC,
                // + == time added from UTC
                // - == time subtracted from UTC
                if (matches[TimeZone].matched)
                {
                    // converts to seconds
                    hours *= 3600;
                    minutes *= 60;
                    offset = IntToTimeT(hours) + IntToTimeT(minutes);

                    wchar_t zone = matches[TimeZone].str().at(0);
                    // time zone offset calculation
                    if (zone == '+')
                    {
                        offset *= -1;
                    }
                }

                // measured from year 1900
                parsedTm.tm_year -= 1900;
                parsedTm.tm_mon -= 1;

                time_t utc{};
                // converts to ticks in UTC
                utc = mktime(&parsedTm);
                if (utc == -1)
                {
                    AddTextToken(matches[0], DateTimePreparsedTokenFormat::RegularString);
                }

// Disable "array to pointer decay" check for tzOffsetBuff since we can't change strftime's signature
#pragma warning(push)
#pragma warning(disable : 26485)
                char tzOffsetBuff[6]{};
                // gets local time zone offset
                strftime(tzOffsetBuff, 6, "%z", &parsedTm);
                std::string localTimeZoneOffsetStr(tzOffsetBuff);
                const time_t nTzOffset = IntToTimeT(std::stoi(localTimeZoneOffsetStr));
                offset += ((nTzOffset / 100) * 3600 + (nTzOffset % 100) * 60);
                // add offset to utc
                utc += offset;
                struct tm result
                {
                };
#pragma warning(pop)

                // converts to local time from utc
                if (!LOCALTIME(&result, &utc))
                {
                    // localtime() set dst, put_time adjusts time accordingly which is not what we want since
                    // we have already taken cared of it in our calculation
                    if (result.tm_isdst == 1)
                    {
                        result.tm_hour -= 1;
                    }

                    if (isDate)
                    {
                        switch (formatStyle)
                        {
                        // SHORT Style
                        case 'S':
                            AddDateToken(matches[0].str(), result, DateTimePreparsedTokenFormat::DateShort);
                            break;
                        // LONG Style
                        case 'L':
                            AddDateToken(matches[0].str(), result, DateTimePreparsedTokenFormat::DateLong);
                            break;
                        // COMPACT or DEFAULT Style
                        case 'C':
                        default:
                            AddDateToken(matches[0].str(), result, DateTimePreparsedTokenFormat::DateCompact);
                            break;
                        }
                    }
                    else
                    {
                        std::ostringstream parsedTime;
                        parsedTime << std::put_time(&result, "%I:%M %p");
                        AddTextToken(parsedTime.str(), DateTimePreparsedTokenFormat::RegularString);
                    }
                }
            }
            else
            {
                AddTextToken(matches[0].str(), DateTimePreparsedTokenFormat::RegularString);
            }

            text = matches.suffix().str();
        }

        AddTextToken(text, DateTimePreparsedTokenFormat::RegularString);
    }
    else
    {
        AddTextToken(in, DateTimePreparsedTokenFormat::RegularString);
    }
}