Example #1
0
        public DateTimeParseResult Parse(ExtractResult er, DateObject refTime)
        {
            var referenceTime      = refTime;
            DateTimeParseResult pr = null;

            var originText = er.Text;

            if ((this.Config.Options & DateTimeOptions.EnablePreview) != 0)
            {
                er.Text    = MatchingUtil.PreProcessTextRemoveSuperfluousWords(er.Text, Config.SuperfluousWordMatcher, out var _);
                er.Length += er.Text.Length - originText.Length;
            }

            bool hasBefore = false, hasAfter = false, hasSince = false, hasAround = false, hasEqual = false, hasDateAfter = false;

            // "InclusiveModifier" means MOD should include the start/end time
            // For example, cases like "on or later than", "earlier than or in" have inclusive modifier
            var hasInclusiveModifier = false;
            var matchIsAfter         = false;
            var modStr = string.Empty;

            // Analyze and process modifiers
            // Push, save the MOD string
            if (er.Metadata != null && er.Metadata.HasMod)
            {
                var beforeMatch = Config.BeforeRegex.MatchBegin(er.Text, trim: true);
                var afterMatch  = Config.AfterRegex.MatchBegin(er.Text, trim: true);
                var sinceMatch  = Config.SinceRegex.MatchBegin(er.Text, trim: true);
                var preLength   = 0;
                if (beforeMatch.Success)
                {
                    preLength = beforeMatch.Index + beforeMatch.Length;
                }
                else if (afterMatch.Success)
                {
                    preLength = afterMatch.Index + afterMatch.Length;
                }
                else if (sinceMatch.Success)
                {
                    preLength = sinceMatch.Index + sinceMatch.Length;
                }

                var aroundText  = er.Text.Substring(preLength);
                var aroundMatch = Config.AroundRegex.MatchBegin(aroundText, trim: true);
                var equalMatch  = Config.EqualRegex.MatchBegin(er.Text, trim: true);

                // check also after match
                if (this.Config.CheckBothBeforeAfter && er.Data != null && er.Data.Equals(Constants.HAS_MOD))
                {
                    if (!beforeMatch.Success)
                    {
                        beforeMatch  = Config.BeforeRegex.MatchEnd(er.Text, trim: true);
                        matchIsAfter = matchIsAfter || beforeMatch.Success;
                    }

                    if (!afterMatch.Success)
                    {
                        afterMatch   = Config.AfterRegex.MatchEnd(er.Text, trim: true);
                        matchIsAfter = matchIsAfter || afterMatch.Success;
                    }

                    if (!sinceMatch.Success)
                    {
                        sinceMatch   = Config.SinceRegex.MatchEnd(er.Text, trim: true);
                        matchIsAfter = matchIsAfter || sinceMatch.Success;
                    }

                    if (!aroundMatch.Success)
                    {
                        aroundMatch  = Config.AroundRegex.MatchEnd(er.Text, trim: true);
                        matchIsAfter = matchIsAfter || aroundMatch.Success;
                    }

                    if (!equalMatch.Success)
                    {
                        equalMatch   = Config.EqualRegex.MatchEnd(er.Text, trim: true);
                        matchIsAfter = matchIsAfter || equalMatch.Success;
                    }
                }

                if (aroundMatch.Success)
                {
                    hasAround  = true;
                    er.Start  += matchIsAfter ? 0 : preLength + aroundMatch.Index + aroundMatch.Length;
                    er.Length -= matchIsAfter ? aroundMatch.Length : preLength + aroundMatch.Index + aroundMatch.Length;
                    er.Text    = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(preLength + aroundMatch.Index + aroundMatch.Length);
                    modStr     = matchIsAfter ? aroundMatch.Value : aroundText.Substring(0, aroundMatch.Index + aroundMatch.Length);
                }

                if (beforeMatch.Success)
                {
                    hasBefore = true;
                    if (!hasAround)
                    {
                        er.Start  += matchIsAfter ? 0 : beforeMatch.Length;
                        er.Length -= beforeMatch.Length;
                        er.Text    = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(beforeMatch.Length);
                    }

                    modStr = beforeMatch.Value + modStr;

                    if (!string.IsNullOrEmpty(beforeMatch.Groups[Constants.IncludeGroupName].Value))
                    {
                        hasInclusiveModifier = true;
                    }
                }
                else if (afterMatch.Success)
                {
                    hasAfter = true;
                    if (!hasAround)
                    {
                        er.Start  += matchIsAfter ? 0 : afterMatch.Length;
                        er.Length -= afterMatch.Length;
                        er.Text    = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(afterMatch.Length);
                    }

                    modStr = afterMatch.Value + modStr;

                    if (!string.IsNullOrEmpty(afterMatch.Groups[Constants.IncludeGroupName].Value))
                    {
                        hasInclusiveModifier = true;
                    }
                }
                else if (sinceMatch.Success)
                {
                    hasSince = true;
                    if (!hasAround)
                    {
                        er.Start  += matchIsAfter ? 0 : sinceMatch.Length;
                        er.Length -= sinceMatch.Length;
                        er.Text    = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(sinceMatch.Length);
                    }

                    modStr = sinceMatch.Value + modStr;
                }
                else if (equalMatch.Success)
                {
                    hasEqual   = true;
                    er.Start  += matchIsAfter ? 0 : equalMatch.Length;
                    er.Length -= equalMatch.Length;
                    er.Text    = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(equalMatch.Length);
                    modStr     = equalMatch.Value;
                }
                else if ((er.Type.Equals(Constants.SYS_DATETIME_DATEPERIOD, StringComparison.Ordinal) && Config.YearRegex.Match(er.Text).Success) ||
                         er.Type.Equals(Constants.SYS_DATETIME_DATE, StringComparison.Ordinal) ||
                         er.Type.Equals(Constants.SYS_DATETIME_TIME, StringComparison.Ordinal))
                {
                    // This has to be put at the end of the if, or cases like "before 2012" and "after 2012" would fall into this
                    // 2012 or after/above
                    // 3 pm or later
                    var match = Config.SuffixAfter.MatchEnd(er.Text, trim: true);
                    if (match.Success)
                    {
                        hasDateAfter = true;
                        er.Length   -= match.Length;
                        er.Text      = er.Text.Substring(0, er.Length ?? 0);
                        modStr       = match.Value;
                    }
                }
            }

            // Parse extracted datetime mention
            pr = ParseResult(er, referenceTime);
            if (pr == null)
            {
                return(null);
            }

            // Apply processed modifiers
            // Pop, restore the MOD string
            if (hasBefore && pr.Value != null)
            {
                pr.Length += modStr.Length;
                pr.Start  -= matchIsAfter ? 0 : modStr.Length;
                pr.Text    = matchIsAfter ? pr.Text + modStr : modStr + pr.Text;
                var val = (DateTimeResolutionResult)pr.Value;

                val.Mod = MergedParserUtil.CombineMod(val.Mod, !hasInclusiveModifier ? Constants.BEFORE_MOD : Constants.UNTIL_MOD);

                if (hasAround)
                {
                    val.Mod   = MergedParserUtil.CombineMod(Constants.APPROX_MOD, val.Mod);
                    hasAround = false;
                }

                pr.Value = val;
            }

            if (hasAfter && pr.Value != null)
            {
                pr.Length += modStr.Length;
                pr.Start  -= matchIsAfter ? 0 : modStr.Length;
                pr.Text    = matchIsAfter ? pr.Text + modStr : modStr + pr.Text;
                var val = (DateTimeResolutionResult)pr.Value;

                val.Mod = MergedParserUtil.CombineMod(val.Mod, !hasInclusiveModifier ? Constants.AFTER_MOD : Constants.SINCE_MOD);

                if (hasAround)
                {
                    val.Mod   = MergedParserUtil.CombineMod(Constants.APPROX_MOD, val.Mod);
                    hasAround = false;
                }

                pr.Value = val;
            }

            if (hasSince && pr.Value != null)
            {
                pr.Length += modStr.Length;
                pr.Start  -= matchIsAfter ? 0 : modStr.Length;
                pr.Text    = matchIsAfter ? pr.Text + modStr : modStr + pr.Text;
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod = MergedParserUtil.CombineMod(val.Mod, Constants.SINCE_MOD);

                if (hasAround)
                {
                    val.Mod   = MergedParserUtil.CombineMod(Constants.APPROX_MOD, val.Mod);
                    hasAround = false;
                }

                pr.Value = val;
            }

            if (hasAround && pr.Value != null)
            {
                pr.Length += modStr.Length;
                pr.Start  -= matchIsAfter ? 0 : modStr.Length;
                pr.Text    = matchIsAfter ? pr.Text + modStr : modStr + pr.Text;
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod  = MergedParserUtil.CombineMod(val.Mod, Constants.APPROX_MOD);
                pr.Value = val;
            }

            if (hasEqual && pr.Value != null)
            {
                pr.Length += modStr.Length;
                pr.Start  -= matchIsAfter ? 0 : modStr.Length;
                pr.Text    = matchIsAfter ? pr.Text + modStr : modStr + pr.Text;
            }

            if (hasDateAfter && pr.Value != null)
            {
                pr.Length += modStr.Length;
                pr.Text   += modStr;
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod  = MergedParserUtil.CombineMod(val.Mod, Constants.SINCE_MOD);
                pr.Value = val;
                hasSince = true;
            }

            // For cases like "3 pm or later on monday"
            if (pr.Value != null && Config.SuffixAfter.Match(pr.Text)?.Index != 0 &&
                pr.Type.Equals(Constants.SYS_DATETIME_DATETIME, StringComparison.Ordinal) && !this.Config.CheckBothBeforeAfter)
            {
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod  = MergedParserUtil.CombineMod(val.Mod, Constants.SINCE_MOD);
                pr.Value = val;
                hasSince = true;
            }

            if ((Config.Options & DateTimeOptions.SplitDateAndTime) != 0 &&
                ((DateTimeResolutionResult)pr?.Value)?.SubDateTimeEntities != null)
            {
                pr.Value = MergedParserUtil.DateTimeResolutionForSplit(pr, this.Config);
            }
            else
            {
                var hasRangeChangingMod = hasBefore || hasAfter || hasSince;
                if (pr.Value != null)
                {
                    ((DateTimeResolutionResult)pr.Value).HasRangeChangingMod = hasRangeChangingMod;
                }

                pr = MergedParserUtil.SetParseResult(pr, hasRangeChangingMod, this.Config);
            }

            // In this version, ExperimentalMode only cope with the "IncludePeriodEnd" case
            if ((this.Config.Options & DateTimeOptions.ExperimentalMode) != 0)
            {
                if (pr?.Metadata != null && pr.Metadata.PossiblyIncludePeriodEnd)
                {
                    pr = MergedParserUtil.SetInclusivePeriodEnd(pr);
                }
            }

            if ((this.Config.Options & DateTimeOptions.EnablePreview) != 0)
            {
                if (pr != null)
                {
                    pr.Length += originText.Length - pr.Text.Length;
                    pr.Text    = originText;
                }
            }

            return(pr);
        }
        public DateTimeParseResult Parse(ExtractResult er, DateObject refTime)
        {
            var referenceTime = refTime;
            DateTimeParseResult pr;

            // push, save teh MOD string
            var    hasInclusiveModifier = false;
            bool   hasBefore = false, hasAfter = false, hasUntil = false, hasSince = false, hasEqual = false;
            string modStr = string.Empty, modStrPrefix = string.Empty, modStrSuffix = string.Empty;

            if (er.Metadata != null)
            {
                var beforeMatch      = config.BeforeRegex.MatchEnd(er.Text, trim: true);
                var afterMatch       = config.AfterRegex.MatchEnd(er.Text, trim: true);
                var untilMatch       = config.UntilRegex.MatchBegin(er.Text, trim: true);
                var sinceMatchPrefix = config.SincePrefixRegex.MatchBegin(er.Text, trim: true);
                var sinceMatchSuffix = config.SinceSuffixRegex.MatchEnd(er.Text, trim: true);
                var equalMatch       = config.EqualRegex.MatchBegin(er.Text, trim: true);

                if (beforeMatch.Success && !MergedParserUtil.IsDurationWithAgoAndLater(er))
                {
                    hasBefore  = true;
                    er.Length -= beforeMatch.Length;
                    er.Text    = er.Text.Substring(0, er.Length ?? 0);
                    modStr     = beforeMatch.Value;

                    if (!string.IsNullOrEmpty(beforeMatch.Groups[Constants.IncludeGroupName].Value))
                    {
                        hasInclusiveModifier = true;
                    }
                }
                else if (afterMatch.Success && !MergedParserUtil.IsDurationWithAgoAndLater(er))
                {
                    hasAfter   = true;
                    er.Length -= afterMatch.Length;
                    er.Text    = er.Text.Substring(0, er.Length ?? 0);
                    modStr     = afterMatch.Value;

                    if (!string.IsNullOrEmpty(afterMatch.Groups[Constants.IncludeGroupName].Value))
                    {
                        hasInclusiveModifier = true;
                    }
                }
                else if (untilMatch.Success)
                {
                    hasUntil   = true;
                    er.Start  += untilMatch.Length;
                    er.Length -= untilMatch.Length;
                    er.Text    = er.Text.Substring(untilMatch.Length);
                    modStr     = untilMatch.Value;
                }
                else if (equalMatch.Success)
                {
                    hasEqual   = true;
                    er.Start  += equalMatch.Length;
                    er.Length -= equalMatch.Length;
                    er.Text    = er.Text.Substring(equalMatch.Length);
                    modStr     = equalMatch.Value;
                }
                else
                {
                    if (sinceMatchPrefix.Success)
                    {
                        hasSince     = true;
                        er.Start    += sinceMatchPrefix.Length;
                        er.Length   -= sinceMatchPrefix.Length;
                        er.Text      = er.Text.Substring(sinceMatchPrefix.Length);
                        modStrPrefix = sinceMatchPrefix.Value;
                    }

                    if (sinceMatchSuffix.Success)
                    {
                        hasSince     = true;
                        er.Length   -= sinceMatchSuffix.Length;
                        er.Text      = er.Text.Substring(0, er.Length ?? 0);
                        modStrSuffix = sinceMatchSuffix.Value;
                    }
                }
            }

            // Parse extracted datetime mention
            pr = ParseResult(er, referenceTime);
            if (pr == null)
            {
                return(null);
            }

            // pop, restore the MOD string
            if (hasBefore)
            {
                pr.Length += modStr.Length;
                pr.Text    = pr.Text + modStr;
                var val = (DateTimeResolutionResult)pr.Value;

                val.Mod = MergedParserUtil.CombineMod(val.Mod, !hasInclusiveModifier ? Constants.BEFORE_MOD : Constants.UNTIL_MOD);

                pr.Value = val;
            }

            if (hasAfter)
            {
                pr.Length += modStr.Length;
                pr.Text    = pr.Text + modStr;
                var val = (DateTimeResolutionResult)pr.Value;

                val.Mod = MergedParserUtil.CombineMod(val.Mod, !hasInclusiveModifier ? Constants.AFTER_MOD : Constants.SINCE_MOD);

                pr.Value = val;
            }

            if (hasUntil)
            {
                pr.Length += modStr.Length;
                pr.Start  -= modStr.Length;
                pr.Text    = modStr + pr.Text;
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod   = Constants.BEFORE_MOD;
                pr.Value  = val;
                hasBefore = true;
            }

            if (hasSince)
            {
                pr.Length += modStrPrefix.Length + modStrSuffix.Length;
                pr.Start  -= modStrPrefix.Length;
                pr.Text    = modStrPrefix + pr.Text + modStrSuffix;
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod  = Constants.SINCE_MOD;
                pr.Value = val;
            }

            if (hasEqual)
            {
                pr.Length += modStr.Length;
                pr.Start  -= modStr.Length;
                pr.Text    = modStr + pr.Text;
            }

            var hasRangeChangingMod = hasBefore || hasAfter || hasSince;

            if (pr.Value != null)
            {
                ((DateTimeResolutionResult)pr.Value).HasRangeChangingMod = hasRangeChangingMod;
            }

            pr = MergedParserUtil.SetParseResult(pr, hasRangeChangingMod, this.config);

            return(pr);
        }