예제 #1
0
        public static List <Token> ExtractorDurationWithBeforeAndAfter(string text,
                                                                       ExtractResult er,
                                                                       List <Token> ret,
                                                                       IDateTimeUtilityConfiguration utilityConfiguration)
        {
            var pos = (int)er.Start + (int)er.Length;

            if (pos <= text.Length)
            {
                var afterString  = text.Substring(pos);
                var beforeString = text.Substring(0, (int)er.Start);
                var index        = -1;
                if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.AgoStringList, out index))
                {
                    ret.Add(new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index));
                }
                else if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.LaterStringList, out index))
                {
                    ret.Add(new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index));
                }
                else if (MatchingUtil.GetInIndex(beforeString, utilityConfiguration.InStringList, out index))
                {
                    if (er.Start != null && er.Length != null && (int)er.Start > index)
                    {
                        ret.Add(new Token((int)er.Start - index, (int)er.Start + (int)er.Length));
                    }
                }
            }
            return(ret);
        }
예제 #2
0
        public static List <Token> ExtractorDurationWithBeforeAndAfter(string text, ExtractResult er, List <Token> ret,
                                                                       IDateTimeUtilityConfiguration utilityConfiguration)
        {
            var pos = (int)er.Start + (int)er.Length;

            if (pos <= text.Length)
            {
                var afterString  = text.Substring(pos);
                var beforeString = text.Substring(0, (int)er.Start);
                var index        = -1;
                if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.AgoRegex, out index))
                {
                    ret.Add(new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index));
                }
                else if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.LaterRegex, out index))
                {
                    ret.Add(new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index));
                }
                else if (MatchingUtil.GetInIndex(beforeString, utilityConfiguration.InConnectorRegex, out index))
                {
                    // for range unit like "week, month, year", it should output dateRange or datetimeRange
                    if (!utilityConfiguration.RangeUnitRegex.IsMatch(er.Text))
                    {
                        if (er.Start != null && er.Length != null && (int)er.Start >= index)
                        {
                            ret.Add(new Token((int)er.Start - index, (int)er.Start + (int)er.Length));
                        }
                    }
                }
            }
            return(ret);
        }
예제 #3
0
        public static List <Token> ExtractorDurationWithBeforeAndAfter(string text, ExtractResult er, List <Token> ret,
                                                                       IDateTimeUtilityConfiguration utilityConfiguration)
        {
            var pos = (int)er.Start + (int)er.Length;

            if (pos <= text.Length)
            {
                var afterString    = text.Substring(pos);
                var beforeString   = text.Substring(0, (int)er.Start);
                var index          = -1;
                var isTimeDuration = utilityConfiguration.TimeUnitRegex.Match(er.Text).Success;

                if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.AgoRegex, out index))
                {
                    // We don't support cases like "5 minutes from today" for now
                    // Cases like "5 minutes ago" or "5 minutes from now" are supported
                    // Cases like "2 days before today" or "2 weeks from today" are also supported
                    var isDayMatchInAfterString = utilityConfiguration.AgoRegex.Match(afterString).Groups["day"].Success;

                    if (!(isTimeDuration && isDayMatchInAfterString))
                    {
                        ret.Add(new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index));
                    }
                }
                else if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.LaterRegex, out index))
                {
                    var isDayMatchInAfterString = utilityConfiguration.LaterRegex.Match(afterString).Groups["day"].Success;

                    if (!(isTimeDuration && isDayMatchInAfterString))
                    {
                        ret.Add(new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index));
                    }
                }
                else if (MatchingUtil.GetTermIndex(beforeString, utilityConfiguration.InConnectorRegex, out index))
                {
                    // For range unit like "week, month, year", it should output dateRange or datetimeRange
                    if (!utilityConfiguration.RangeUnitRegex.IsMatch(er.Text))
                    {
                        if (er.Start != null && er.Length != null && (int)er.Start >= index)
                        {
                            ret.Add(new Token((int)er.Start - index, (int)er.Start + (int)er.Length));
                        }
                    }
                }
                else if (MatchingUtil.GetTermIndex(beforeString, utilityConfiguration.WithinNextPrefixRegex, out index))
                {
                    // For range unit like "week, month, year, day, second, minute, hour", it should output dateRange or datetimeRange
                    if (!utilityConfiguration.DateUnitRegex.IsMatch(er.Text) && !utilityConfiguration.TimeUnitRegex.IsMatch(er.Text))
                    {
                        if (er.Start != null && er.Length != null && (int)er.Start >= index)
                        {
                            ret.Add(new Token((int)er.Start - index, (int)er.Start + (int)er.Length));
                        }
                    }
                }
            }

            return(ret);
        }
예제 #4
0
        public List <ExtractResult> Extract(string text, DateObject reference)
        {
            var ret = new List <ExtractResult>();

            var originText = text;
            List <MatchResult <string> > superfluousWordMatches = null;

            if ((this.config.Options & DateTimeOptions.EnablePreview) != 0)
            {
                text = MatchingUtil.PreProcessTextRemoveSuperfluousWords(text, this.config.SuperfluousWordMatcher, out superfluousWordMatches);
            }

            // The order is important, since there can be conflicts in merging
            AddTo(ret, this.config.DateExtractor.Extract(text, reference), text);
            AddTo(ret, this.config.TimeExtractor.Extract(text, reference), text);
            AddTo(ret, this.config.DatePeriodExtractor.Extract(text, reference), text);
            AddTo(ret, this.config.DurationExtractor.Extract(text, reference), text);
            AddTo(ret, this.config.TimePeriodExtractor.Extract(text, reference), text);
            AddTo(ret, this.config.DateTimePeriodExtractor.Extract(text, reference), text);
            AddTo(ret, this.config.DateTimeExtractor.Extract(text, reference), text);
            AddTo(ret, this.config.SetExtractor.Extract(text, reference), text);
            AddTo(ret, this.config.HolidayExtractor.Extract(text, reference), text);

            if ((this.config.Options & DateTimeOptions.EnablePreview) != 0)
            {
                AddTo(ret, this.config.TimeZoneExtractor.Extract(text, reference), text);
                ret = this.config.TimeZoneExtractor.RemoveAmbiguousTimezone(ret);
            }

            // This should be at the end since if need the extractor to determine the previous text contains time or not
            AddTo(ret, NumberEndingRegexMatch(text, ret), text);

            // Modify time entity to an alternative DateTime expression if it follows a DateTime entity
            if ((this.config.Options & DateTimeOptions.ExtendedTypes) != 0)
            {
                ret = this.config.DateTimeAltExtractor.Extract(ret, text, reference);
            }

            ret = FilterUnspecificDatePeriod(ret);

            ret = FilterAmbiguity(ret, text);

            ret = AddMod(ret, text);

            // Filtering
            if ((this.config.Options & DateTimeOptions.CalendarMode) != 0)
            {
                ret = CheckCalendarFilterList(ret, text);
            }

            ret = ret.OrderBy(p => p.Start).ToList();

            if ((this.config.Options & DateTimeOptions.EnablePreview) != 0)
            {
                ret = MatchingUtil.PosProcessExtractionRecoverSuperfluousWords(ret, superfluousWordMatches, originText);
            }

            return(ret);
        }
예제 #5
0
        private static DateTimeResolutionResult GetAgoLaterResult(
            DateTimeParseResult durationParseResult,
            double number,
            IImmutableDictionary <string, string> unitMap,
            string srcUnit,
            string afterStr,
            string beforeStr,
            System.DateTime referenceTime,
            IDateTimeUtilityConfiguration utilityConfiguration,
            AgoLaterMode mode)
        {
            var ret = new DateTimeResolutionResult();

            if (unitMap.ContainsKey(srcUnit))
            {
                var unitStr = unitMap[srcUnit];
                var numStr  = number.ToString(CultureInfo.InvariantCulture);
                var result  = new DateTimeResolutionResult();

                if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.AgoRegex))
                {
                    if (mode.Equals(AgoLaterMode.Date))
                    {
                        result = GetDateResult(unitStr, numStr, referenceTime, false);
                    }
                    else if (mode.Equals(AgoLaterMode.DateTime))
                    {
                        result = GetDateTimeResult(unitStr, numStr, referenceTime, false);
                    }

                    ((DateTimeResolutionResult)durationParseResult.Value).Mod = TimeTypeConstants.beforeMod;
                    result.SubDateTimeEntities = new List <object> {
                        durationParseResult
                    };
                    return(result);
                }

                if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.LaterRegex) ||
                    MatchingUtil.ContainsInIndex(beforeStr, utilityConfiguration.InConnectorRegex))
                {
                    if (mode.Equals(AgoLaterMode.Date))
                    {
                        result = GetDateResult(unitStr, numStr, referenceTime, true);
                    }
                    else if (mode.Equals(AgoLaterMode.DateTime))
                    {
                        result = GetDateTimeResult(unitStr, numStr, referenceTime, true);
                    }

                    ((DateTimeResolutionResult)durationParseResult.Value).Mod = TimeTypeConstants.afterMod;
                    result.SubDateTimeEntities = new List <object> {
                        durationParseResult
                    };
                    return(result);
                }
            }
            return(ret);
        }
예제 #6
0
        private static DateTimeResolutionResult GetAgoLaterResult(
            DateTimeParseResult durationParseResult,
            string afterStr,
            string beforeStr,
            System.DateTime referenceTime,
            IDateTimeUtilityConfiguration utilityConfiguration,
            AgoLaterMode mode)
        {
            var ret            = new DateTimeResolutionResult();
            var resultDateTime = referenceTime;
            var timex          = durationParseResult.TimexStr;

            if (((DateTimeResolutionResult)durationParseResult.Value).Mod == Constants.MORE_THAN_MOD)
            {
                ret.Mod = Constants.MORE_THAN_MOD;
            }
            else if (((DateTimeResolutionResult)durationParseResult.Value).Mod == Constants.LESS_THAN_MOD)
            {
                ret.Mod = Constants.LESS_THAN_MOD;
            }

            if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.AgoRegex))
            {
                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime, false);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = Constants.BEFORE_MOD;
            }
            else if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.LaterRegex) ||
                     MatchingUtil.ContainsTermIndex(beforeStr, utilityConfiguration.InConnectorRegex))
            {
                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime, true);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = Constants.AFTER_MOD;
            }

            if (resultDateTime != referenceTime)
            {
                if (mode.Equals(AgoLaterMode.Date))
                {
                    ret.Timex = $"{FormatUtil.LuisDate(resultDateTime)}";
                }
                else if (mode.Equals(AgoLaterMode.DateTime))
                {
                    ret.Timex = $"{FormatUtil.LuisDateTime(resultDateTime)}";
                }

                ret.FutureValue         = ret.PastValue = resultDateTime;
                ret.SubDateTimeEntities = new List <object> {
                    durationParseResult
                };
                ret.Success = true;
            }

            return(ret);
        }
예제 #7
0
        private static DateTimeResolutionResult GetAgoLaterResult(IParser numberParser,
                                                                  ExtractResult er,
                                                                  IImmutableDictionary <string, string> unitMap,
                                                                  string srcUnit,
                                                                  string afterStr,
                                                                  string beforeStr,
                                                                  System.DateTime referenceTime,
                                                                  IDateTimeUtilityConfiguration utilityConfiguration,
                                                                  AgoLaterMode mode)
        {
            var ret = new DateTimeResolutionResult();
            var pr  = numberParser.Parse(er);

            var number = int.Parse(pr.ResolutionStr);

            if (unitMap.ContainsKey(srcUnit))
            {
                var unitStr = unitMap[srcUnit];
                var numStr  = number.ToString();
                if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.AgoStringList))
                {
                    if (mode.Equals(AgoLaterMode.Date))
                    {
                        return(GetDateResult(unitStr, numStr, referenceTime, false));
                    }

                    if (mode.Equals(AgoLaterMode.DateTime))
                    {
                        return(GetDateTimeResult(unitStr, numStr, referenceTime, false));
                    }
                }

                if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.LaterStringList) ||
                    MatchingUtil.ContainsInIndex(beforeStr, utilityConfiguration.InStringList))
                {
                    if (mode.Equals(AgoLaterMode.Date))
                    {
                        return(GetDateResult(unitStr, numStr, referenceTime, true));
                    }

                    if (mode.Equals(AgoLaterMode.DateTime))
                    {
                        return(GetDateTimeResult(unitStr, numStr, referenceTime, true));
                    }
                }
            }
            return(ret);
        }
        private IEnumerable <Token> MatchLocationTimes(string text)
        {
            var ret = new List <Token>();

            if (config.LocationTimeSuffixRegex == null)
            {
                return(ret);
            }

            var timeMatch = config.LocationTimeSuffixRegex.Matches(text);

            if (timeMatch.Count != 0)
            {
                var lastMatchIndex  = timeMatch[timeMatch.Count - 1].Index;
                var matches         = config.LocationMatcher.Find(text.Substring(0, lastMatchIndex).ToLowerInvariant());
                var locationMatches = MatchingUtil.RemoveSubMatches(matches);

                var i = 0;
                foreach (Match match in timeMatch)
                {
                    var hasCityBefore = false;

                    while (i < locationMatches.Count && locationMatches[i].End <= match.Index)
                    {
                        hasCityBefore = true;
                        i++;

                        if (i == locationMatches.Count)
                        {
                            break;
                        }
                    }

                    if (hasCityBefore && locationMatches[i - 1].End == match.Index)
                    {
                        ret.Add(new Token(locationMatches[i - 1].Start, match.Index + match.Length));
                    }

                    if (i == locationMatches.Count)
                    {
                        break;
                    }
                }
            }

            return(ret);
        }
예제 #9
0
        private static DateTimeResolutionResult GetAgoLaterResult(double number,
                                                                  IImmutableDictionary <string, string> unitMap,
                                                                  string srcUnit,
                                                                  string afterStr,
                                                                  string beforeStr,
                                                                  System.DateTime referenceTime,
                                                                  IDateTimeUtilityConfiguration utilityConfiguration,
                                                                  AgoLaterMode mode)
        {
            var ret = new DateTimeResolutionResult();

            if (unitMap.ContainsKey(srcUnit))
            {
                var unitStr = unitMap[srcUnit];
                var numStr  = number.ToString(CultureInfo.InvariantCulture);
                if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.AgoRegex))
                {
                    if (mode.Equals(AgoLaterMode.Date))
                    {
                        return(GetDateResult(unitStr, numStr, referenceTime, false));
                    }

                    if (mode.Equals(AgoLaterMode.DateTime))
                    {
                        return(GetDateTimeResult(unitStr, numStr, referenceTime, false));
                    }
                }

                if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.LaterRegex) ||
                    MatchingUtil.ContainsInIndex(beforeStr, utilityConfiguration.InConnectorRegex))
                {
                    if (mode.Equals(AgoLaterMode.Date))
                    {
                        return(GetDateResult(unitStr, numStr, referenceTime, true));
                    }

                    if (mode.Equals(AgoLaterMode.DateTime))
                    {
                        return(GetDateTimeResult(unitStr, numStr, referenceTime, true));
                    }
                }
            }
            return(ret);
        }
예제 #10
0
        private static DateTimeResolutionResult GetAgoLaterResult(
            DateTimeParseResult durationParseResult,
            string afterStr,
            string beforeStr,
            DateObject referenceTime,
            IDateTimeUtilityConfiguration utilityConfiguration,
            AgoLaterMode mode,
            SwiftDayDelegate swiftDay)
        {
            var ret            = new DateTimeResolutionResult();
            var resultDateTime = referenceTime;
            var timex          = durationParseResult.TimexStr;

            if (((DateTimeResolutionResult)durationParseResult.Value).Mod == Constants.MORE_THAN_MOD)
            {
                ret.Mod = Constants.MORE_THAN_MOD;
            }
            else if (((DateTimeResolutionResult)durationParseResult.Value).Mod == Constants.LESS_THAN_MOD)
            {
                ret.Mod = Constants.LESS_THAN_MOD;
            }

            if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.AgoRegex))
            {
                var match = utilityConfiguration.AgoRegex.Match(afterStr);
                var swift = 0;

                // Handle cases like "3 days before yesterday"
                if (match.Success && !string.IsNullOrEmpty(match.Groups["day"].Value))
                {
                    swift = swiftDay(match.Groups["day"].Value);
                }

                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime.AddDays(swift), false);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = Constants.BEFORE_MOD;
            }
            else if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.LaterRegex) ||
                     MatchingUtil.ContainsTermIndex(beforeStr, utilityConfiguration.InConnectorRegex))
            {
                var match = utilityConfiguration.LaterRegex.Match(afterStr);
                var swift = 0;

                // Handle cases like "3 days after tomorrow"
                if (match.Success && !string.IsNullOrEmpty(match.Groups["day"].Value))
                {
                    swift = swiftDay(match.Groups["day"].Value);
                }

                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime.AddDays(swift), true);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = Constants.AFTER_MOD;
            }

            if (resultDateTime != referenceTime)
            {
                if (mode.Equals(AgoLaterMode.Date))
                {
                    ret.Timex = $"{DateTimeFormatUtil.LuisDate(resultDateTime)}";
                }
                else if (mode.Equals(AgoLaterMode.DateTime))
                {
                    ret.Timex = $"{DateTimeFormatUtil.LuisDateTime(resultDateTime)}";
                }

                ret.FutureValue         = ret.PastValue = resultDateTime;
                ret.SubDateTimeEntities = new List <object> {
                    durationParseResult
                };
                ret.Success = true;
            }

            return(ret);
        }
        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 aroundMatch = Config.AroundRegex.MatchBegin(er.Text, 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 (beforeMatch.Success)
                {
                    hasBefore  = true;
                    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;

                    if (!string.IsNullOrEmpty(beforeMatch.Groups[Constants.IncludeGroupName].Value))
                    {
                        hasInclusiveModifier = true;
                    }
                }
                else if (afterMatch.Success)
                {
                    hasAfter   = true;
                    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;

                    if (!string.IsNullOrEmpty(afterMatch.Groups[Constants.IncludeGroupName].Value))
                    {
                        hasInclusiveModifier = true;
                    }
                }
                else if (sinceMatch.Success)
                {
                    hasSince   = true;
                    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;
                }
                else if (aroundMatch.Success)
                {
                    hasAround  = true;
                    er.Start  += matchIsAfter ? 0 : aroundMatch.Length;
                    er.Length -= aroundMatch.Length;
                    er.Text    = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(aroundMatch.Length);
                    modStr     = aroundMatch.Value;
                }
                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 = CombineMod(val.Mod, !hasInclusiveModifier ? Constants.BEFORE_MOD : Constants.UNTIL_MOD);

                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;

                if (!hasInclusiveModifier)
                {
                    val.Mod = CombineMod(val.Mod, Constants.AFTER_MOD);
                }
                else
                {
                    val.Mod = CombineMod(val.Mod, Constants.SINCE_MOD);
                }

                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  = CombineMod(val.Mod, Constants.SINCE_MOD);
                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  = 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    = pr.Text + modStr;
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod  = 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  = CombineMod(val.Mod, Constants.SINCE_MOD);
                pr.Value = val;
                hasSince = true;
            }

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

                pr = SetParseResult(pr, hasRangeChangingMod);
            }

            // 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 = SetInclusivePeriodEnd(pr);
                }
            }

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

            return(pr);
        }
예제 #12
0
        private static DateTimeResolutionResult GetAgoLaterResult(
            DateTimeParseResult durationParseResult,
            string afterStr,
            string beforeStr,
            DateObject referenceTime,
            IParser numberParser,
            IDateTimeUtilityConfiguration utilityConfiguration,
            AgoLaterMode mode,
            SwiftDayDelegate swiftDay)
        {
            var ret            = new DateTimeResolutionResult();
            var resultDateTime = referenceTime;
            var timex          = durationParseResult.TimexStr;

            if (((DateTimeResolutionResult)durationParseResult.Value).Mod == Constants.MORE_THAN_MOD)
            {
                ret.Mod = Constants.MORE_THAN_MOD;
            }
            else if (((DateTimeResolutionResult)durationParseResult.Value).Mod == Constants.LESS_THAN_MOD)
            {
                ret.Mod = Constants.LESS_THAN_MOD;
            }

            int    swift = 0;
            bool   isMatch = false, isLater = false;
            string dayStr          = null;
            var    agoLaterRegexes = new List <(Regex, string)>
            {
                (utilityConfiguration.AgoRegex, "ago"),
                (utilityConfiguration.LaterRegex, "later"),
            };

            // AgoRegex and LaterRegex cases
            foreach (var regex in agoLaterRegexes)
            {
                // Match in afterStr
                if (MatchingUtil.ContainsAgoLaterIndex(afterStr, regex.Item1, inSuffix: true))
                {
                    isMatch = true;
                    isLater = regex.Item2 == "later" ? true : false;
                    var match = regex.Item1.Match(afterStr);
                    dayStr = match.Groups["day"].Value;
                }

                if (utilityConfiguration.CheckBothBeforeAfter)
                {
                    // Match split between beforeStr and afterStr
                    if (string.IsNullOrEmpty(dayStr) && isMatch)
                    {
                        var match = regex.Item1.Match(beforeStr + " " + afterStr);
                        dayStr = match.Groups["day"].Value;
                    }

                    // Match in beforeStr
                    if (string.IsNullOrEmpty(dayStr) && MatchingUtil.ContainsAgoLaterIndex(beforeStr, regex.Item1, inSuffix: false))
                    {
                        isMatch = true;
                        isLater = regex.Item2 == "later" ? true : false;
                        var match = regex.Item1.Match(beforeStr);
                        dayStr = match.Groups["day"].Value;
                    }
                }

                if (isMatch)
                {
                    break;
                }
            }

            // InConnectorRegex cases
            if (!isMatch)
            {
                if (MatchingUtil.ContainsTermIndex(beforeStr, utilityConfiguration.InConnectorRegex))
                {
                    // Match in afterStr
                    isMatch = isLater = true;
                    var match = utilityConfiguration.LaterRegex.Match(afterStr);
                    dayStr = match.Groups["day"].Value;
                }
                else if (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.InConnectorRegex, inSuffix: true))
                {
                    // Match in beforeStr
                    isMatch = isLater = true;
                    var match = utilityConfiguration.LaterRegex.Match(beforeStr);
                    dayStr = match.Groups["day"].Value;
                }
            }

            if (isMatch)
            {
                // Handle cases like "3 days before yesterday", "3 days after tomorrow"
                if (!string.IsNullOrEmpty(dayStr))
                {
                    swift = swiftDay(dayStr);
                }

                if (isLater)
                {
                    var yearMatch = utilityConfiguration.SinceYearSuffixRegex.Match(afterStr);
                    if (yearMatch.Success)
                    {
                        var yearString = yearMatch.Groups[Constants.YearGroupName].Value;
                        var yearEr     = new ExtractResult {
                            Text = yearString
                        };
                        var year = Convert.ToInt32((double)(numberParser.Parse(yearEr).Value ?? 0));
                        referenceTime = DateObject.MinValue.SafeCreateFromValue(year, 1, 1);
                    }
                }

                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime.AddDays(swift), isLater ? true : false);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = isLater ? Constants.AFTER_MOD : Constants.BEFORE_MOD;
            }

            if (resultDateTime != referenceTime)
            {
                if (mode.Equals(AgoLaterMode.Date))
                {
                    ret.Timex = $"{DateTimeFormatUtil.LuisDate(resultDateTime)}";
                }
                else if (mode.Equals(AgoLaterMode.DateTime))
                {
                    ret.Timex = $"{DateTimeFormatUtil.LuisDateTime(resultDateTime)}";
                }

                ret.FutureValue         = ret.PastValue = resultDateTime;
                ret.SubDateTimeEntities = new List <object> {
                    durationParseResult
                };
                ret.Success = true;
            }

            return(ret);
        }
예제 #13
0
        public static List <Token> ExtractorDurationWithBeforeAndAfter(string text, ExtractResult er, List <Token> ret,
                                                                       IDateTimeUtilityConfiguration utilityConfiguration)
        {
            var pos = (int)er.Start + (int)er.Length;

            if (pos <= text.Length)
            {
                var afterString    = text.Substring(pos);
                var beforeString   = text.Substring(0, (int)er.Start);
                var isTimeDuration = utilityConfiguration.TimeUnitRegex.Match(er.Text).Success;

                if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.AgoRegex, out var index))
                {
                    // We don't support cases like "5 minutes from today" for now
                    // Cases like "5 minutes ago" or "5 minutes from now" are supported
                    // Cases like "2 days before today" or "2 weeks from today" are also supported
                    var isDayMatchInAfterString = utilityConfiguration.AgoRegex.Match(afterString).Groups["day"].Success;

                    if (!(isTimeDuration && isDayMatchInAfterString))
                    {
                        ret.Add(new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index));
                    }

                    if (utilityConfiguration.CheckBothBeforeAfter && !isDayMatchInAfterString)
                    {
                        // check if regex match is split between beforeString and afterString
                        string beforeAfterStr = beforeString + afterString.Substring(0, index);
                        if (MatchingUtil.GetAgoLaterIndexInBeforeString(beforeAfterStr, utilityConfiguration.AgoRegex, out var indexStart))
                        {
                            isDayMatchInAfterString = utilityConfiguration.AgoRegex.Match(beforeAfterStr).Groups["day"].Success;

                            if (isDayMatchInAfterString && !(isTimeDuration && isDayMatchInAfterString))
                            {
                                ret.Add(new Token(indexStart, (er.Start + er.Length ?? 0) + index));
                            }
                        }
                    }
                }
                else if (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.GetAgoLaterIndexInBeforeString(beforeString, utilityConfiguration.AgoRegex, out index))
                {
                    // Check also beforeString
                    var isDayMatchInBeforeString = utilityConfiguration.AgoRegex.Match(beforeString).Groups["day"].Success;
                    if (!(isTimeDuration && isDayMatchInBeforeString))
                    {
                        ret.Add(new Token(index, (er.Start + er.Length ?? 0) + index));
                    }
                }
                else if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.LaterRegex, out index) || (utilityConfiguration.CheckBothBeforeAfter &&
                                                                                                                    MatchingUtil.GetAgoLaterIndexInBeforeString(beforeString, utilityConfiguration.LaterRegex, out index)))
                {
                    Token tokAfter = null, tokBefore = null;
                    if (MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.LaterRegex, out index))
                    {
                        var isDayMatchInAfterString = utilityConfiguration.LaterRegex.Match(afterString).Groups["day"].Success;

                        if (!(isTimeDuration && isDayMatchInAfterString))
                        {
                            tokAfter = new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index);
                        }
                    }

                    // Check also beforeString
                    if (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.GetAgoLaterIndexInBeforeString(beforeString, utilityConfiguration.LaterRegex, out index))
                    {
                        var isDayMatchInBeforeString = utilityConfiguration.LaterRegex.Match(beforeString).Groups["day"].Success;
                        if (!(isTimeDuration && isDayMatchInBeforeString))
                        {
                            tokBefore = new Token(index, er.Start + er.Length ?? 0);
                        }
                    }

                    if (tokAfter != null && tokBefore != null && tokBefore.Start + tokBefore.Length > tokAfter.Start)
                    {
                        // merge overlapping tokens
                        ret.Add(new Token(tokBefore.Start, tokAfter.Start + tokAfter.Length - tokBefore.Start));
                    }
                    else if (tokAfter != null)
                    {
                        ret.Add(tokAfter);
                    }
                    else if (tokBefore != null)
                    {
                        ret.Add(tokBefore);
                    }
                }
                else if (MatchingUtil.GetTermIndex(beforeString, utilityConfiguration.InConnectorRegex, out index))
                {
                    // For range unit like "week, month, year", it should output dateRange or datetimeRange
                    if (!utilityConfiguration.RangeUnitRegex.IsMatch(er.Text))
                    {
                        if (er.Start != null && er.Length != null && (int)er.Start >= index)
                        {
                            ret.Add(new Token((int)er.Start - index, (int)er.Start + (int)er.Length));
                        }
                    }
                }
                else if (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.InConnectorRegex, out index))
                {
                    // Check also afterString
                    // For range unit like "week, month, year", it should output dateRange or datetimeRange
                    if (!utilityConfiguration.RangeUnitRegex.IsMatch(er.Text))
                    {
                        if (er.Start != null && er.Length != null)
                        {
                            ret.Add(new Token((int)er.Start, (int)er.Start + (int)er.Length + index));
                        }
                    }
                }
                else if (MatchingUtil.GetTermIndex(beforeString, utilityConfiguration.WithinNextPrefixRegex, out index))
                {
                    // For range unit like "week, month, year, day, second, minute, hour", it should output dateRange or datetimeRange
                    if (!utilityConfiguration.DateUnitRegex.IsMatch(er.Text) && !utilityConfiguration.TimeUnitRegex.IsMatch(er.Text))
                    {
                        if (er.Start != null && er.Length != null && (int)er.Start >= index)
                        {
                            ret.Add(new Token((int)er.Start - index, (int)er.Start + (int)er.Length));
                        }
                    }
                }
                else if (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.GetAgoLaterIndex(afterString, utilityConfiguration.WithinNextPrefixRegex, out index))
                {
                    // Check also afterString
                    // For range unit like "week, month, year, day, second, minute, hour", it should output dateRange or datetimeRange
                    if (!utilityConfiguration.DateUnitRegex.IsMatch(er.Text) && !utilityConfiguration.TimeUnitRegex.IsMatch(er.Text))
                    {
                        if (er.Start != null && er.Length != null)
                        {
                            ret.Add(new Token((int)er.Start, (int)er.Start + (int)er.Length + index));
                        }
                    }
                }
            }

            return(ret);
        }
예제 #14
0
        private IEnumerable <Token> MatchLocationTimes(string text, List <Token> tokens, string originalText, bool reIndex)
        {
            var ret = new List <Token>();

            if (config.LocationTimeSuffixRegex == null)
            {
                return(ret);
            }

            var timeMatch = config.LocationTimeSuffixRegex.Matches(text);

            // Before calling a Find() in location matcher, check if all the matched suffixes by
            // LocationTimeSuffixRegex are already inside tokens extracted by TimeZone matcher.
            // If so, don't call the Find() as they have been extracted by TimeZone matcher, otherwise, call it.
            bool isAllSuffixInsideTokens = true;

            foreach (Match match in timeMatch)
            {
                bool isInside = false;
                foreach (Token token in tokens)
                {
                    if (token.Start <= match.Index && token.End >= match.Index + match.Length)
                    {
                        isInside = true;
                        break;
                    }
                }

                if (!isInside)
                {
                    isAllSuffixInsideTokens = false;
                }

                if (!isAllSuffixInsideTokens)
                {
                    break;
                }
            }

            if (timeMatch.Count != 0 && !isAllSuffixInsideTokens)
            {
                var lastMatchIndex = timeMatch[timeMatch.Count - 1].Index;

                var matches         = config.LocationMatcher.Find(text.Substring(0, lastMatchIndex));
                var locationMatches = MatchingUtil.RemoveSubMatches(matches);

                if (reIndex)
                {
                    foreach (var locMatch in locationMatches)
                    {
                        locMatch.Start = originalText.IndexOf(locMatch.CanonicalValues.FirstOrDefault(), locMatch.Start, StringComparison.Ordinal);
                    }
                }

                var i = 0;
                foreach (Match match in timeMatch)
                {
                    var hasCityBefore = false;

                    var index = match.Index;

                    if (reIndex)
                    {
                        index = originalText.IndexOf(match.Value, match.Index, StringComparison.Ordinal);
                    }

                    while (i < locationMatches.Count && locationMatches[i].End <= index)
                    {
                        hasCityBefore = true;
                        i++;

                        if (i == locationMatches.Count)
                        {
                            break;
                        }
                    }

                    if (hasCityBefore && locationMatches[i - 1].End == index)
                    {
                        ret.Add(new Token(locationMatches[i - 1].Start, index + match.Length));
                    }

                    if (i == locationMatches.Count)
                    {
                        break;
                    }
                }
            }

            return(ret);
        }
예제 #15
0
        public static List <Token> ExtractorDurationWithBeforeAndAfter(string text, ExtractResult er, List <Token> ret,
                                                                       IDateTimeUtilityConfiguration utilityConfiguration)
        {
            var pos = (int)er.Start + (int)er.Length;

            if (pos <= text.Length)
            {
                var  afterString    = text.Substring(pos);
                var  beforeString   = text.Substring(0, (int)er.Start);
                var  isTimeDuration = utilityConfiguration.TimeUnitRegex.Match(er.Text).Success;
                int  index;
                bool isMatch         = false;
                var  agoLaterRegexes = new List <Regex>
                {
                    utilityConfiguration.AgoRegex,
                    utilityConfiguration.LaterRegex,
                };

                foreach (var regex in agoLaterRegexes)
                {
                    Token tokAfter = null, tokBefore = null;
                    bool  isDayMatch = false;

                    // Check afterString
                    if (MatchingUtil.GetAgoLaterIndex(afterString, regex, out index, inSuffix: true))
                    {
                        // We don't support cases like "5 minutes from today" for now
                        // Cases like "5 minutes ago" or "5 minutes from now" are supported
                        // Cases like "2 days before today" or "2 weeks from today" are also supported
                        isDayMatch = regex.Match(afterString).Groups["day"].Success;

                        if (!(isTimeDuration && isDayMatch))
                        {
                            tokAfter = new Token(er.Start ?? 0, (er.Start + er.Length ?? 0) + index);
                            isMatch  = true;
                        }
                    }

                    if (utilityConfiguration.CheckBothBeforeAfter)
                    {
                        // Check if regex match is split between beforeString and afterString
                        if (!isDayMatch && isMatch)
                        {
                            string beforeAfterStr = beforeString + afterString.Substring(0, index);
                            var    isRangeMatch   = utilityConfiguration.RangePrefixRegex.MatchBegin(afterString.Substring(index), trim: true).Success;
                            if (!isRangeMatch && MatchingUtil.GetAgoLaterIndex(beforeAfterStr, regex, out var indexStart, inSuffix: false))
                            {
                                isDayMatch = regex.Match(beforeAfterStr).Groups["day"].Success;

                                if (isDayMatch && !(isTimeDuration && isDayMatch))
                                {
                                    ret.Add(new Token(indexStart, (er.Start + er.Length ?? 0) + index));
                                    isMatch = true;
                                }
                            }
                        }

                        // Check also beforeString
                        if (MatchingUtil.GetAgoLaterIndex(beforeString, regex, out index, inSuffix: false))
                        {
                            isDayMatch = regex.Match(beforeString).Groups["day"].Success;
                            if (!(isTimeDuration && isDayMatch))
                            {
                                tokBefore = new Token(index, er.Start + er.Length ?? 0);
                                isMatch   = true;
                            }
                        }
                    }

                    if (tokAfter != null && tokBefore != null && tokBefore.Start + tokBefore.Length > tokAfter.Start)
                    {
                        // Merge overlapping tokens
                        ret.Add(new Token(tokBefore.Start, tokAfter.Start + tokAfter.Length - tokBefore.Start));
                    }
                    else if (tokAfter != null)
                    {
                        ret.Add(tokAfter);
                    }
                    else if (tokBefore != null)
                    {
                        ret.Add(tokBefore);
                    }

                    if (isMatch)
                    {
                        break;
                    }
                }

                if (!isMatch)
                {
                    var inWithinRegexes = new List <(Regex, List <Regex>)>
                    {
                        (utilityConfiguration.InConnectorRegex, new List <Regex> {
                            utilityConfiguration.RangeUnitRegex
                        }),
                        (utilityConfiguration.WithinNextPrefixRegex, new List <Regex> {
                            utilityConfiguration.DateUnitRegex, utilityConfiguration.TimeUnitRegex
                        }),
                    };

                    foreach (var regex in inWithinRegexes)
                    {
                        bool isMatchAfter = false;
                        if (MatchingUtil.GetTermIndex(beforeString, regex.Item1, out index))
                        {
                            isMatch = true;
                        }
                        else if (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.GetAgoLaterIndex(afterString, regex.Item1, out index, inSuffix: true))
                        {
                            // Check also afterString
                            isMatch = isMatchAfter = true;
                        }

                        if (isMatch)
                        {
                            // For InConnectorRegex and range unit like "week, month, year", it should output dateRange or datetimeRange
                            // For WithinNextPrefixRegex and range unit like "week, month, year, day, second, minute, hour", it should output dateRange or datetimeRange
                            bool isUnitMatch = false;
                            foreach (var unitRegex in regex.Item2)
                            {
                                isUnitMatch = isUnitMatch || unitRegex.IsMatch(er.Text);
                            }

                            if (!isUnitMatch)
                            {
                                if (er.Start != null && er.Length != null && ((int)er.Start >= index || isMatchAfter))
                                {
                                    int start = (int)er.Start - (!isMatchAfter ? index : 0);
                                    int end   = (int)er.Start + (int)er.Length + (isMatchAfter ? index : 0);
                                    ret.Add(new Token(start, end));
                                }
                            }

                            break;
                        }
                    }
                }
            }

            return(ret);
        }
        // Check every integers and ordinal number for date
        private List <Token> NumberWithMonth(string text, DateObject reference)
        {
            var ret = new List <Token>();

            var er = this.Config.OrdinalExtractor.Extract(text);

            er.AddRange(this.Config.IntegerExtractor.Extract(text));

            foreach (var result in er)
            {
                var parsed = int.TryParse((this.Config.NumberParser.Parse(result).Value ?? 0).ToString(), out int num);

                if (!parsed || (num < 1 || num > 31))
                {
                    continue;
                }

                if (result.Start >= 0)
                {
                    // Handling cases like '(Monday,) Jan twenty two'
                    var prefixStr = text.Substring(0, result.Start ?? 0);

                    // Check that the extracted number is not part of a decimal number, time expression or currency
                    // (e.g. '123.24', '12:24', '$12')
                    if (MatchingUtil.IsInvalidDayNumberPrefix(prefixStr))
                    {
                        continue;
                    }

                    var match = this.Config.MonthEnd.Match(prefixStr);
                    if (match.Success)
                    {
                        var startIndex = match.Index;
                        var endIndex   = match.Index + match.Length + (result.Length ?? 0);

                        ExtendWithWeekdayAndYear(
                            ref startIndex, ref endIndex, Config.MonthOfYear.GetValueOrDefault(match.Groups["month"].Value, reference.Month),
                            num, text, reference);

                        ret.Add(new Token(startIndex, endIndex));
                        continue;
                    }

                    // Handling cases like 'for the 25th'
                    var  matches = this.Config.ForTheRegex.Matches(text);
                    bool isFound = false;
                    foreach (Match matchCase in matches)
                    {
                        if (matchCase.Success)
                        {
                            var ordinalNum = matchCase.Groups["DayOfMonth"].Value;
                            if (ordinalNum == result.Text)
                            {
                                var endLength = 0;
                                if (matchCase.Groups["end"].Value.Length > 0)
                                {
                                    endLength = matchCase.Groups["end"].Value.Length;
                                }

                                ret.Add(new Token(matchCase.Index, matchCase.Index + matchCase.Length - endLength));
                                isFound = true;
                            }
                        }
                    }

                    if (isFound)
                    {
                        continue;
                    }

                    // Handling cases like 'Thursday the 21st', which both 'Thursday' and '21st' refer to a same date
                    matches = this.Config.WeekDayAndDayOfMonthRegex.Matches(text);

                    foreach (Match matchCase in matches)
                    {
                        if (matchCase.Success)
                        {
                            var ordinalNum = matchCase.Groups["DayOfMonth"].Value;
                            if (ordinalNum == result.Text && matchCase.Groups["DayOfMonth"].Index == result.Start)
                            {
                                // Get week of day for the ordinal number which is regarded as a date of reference month
                                var date          = DateObject.MinValue.SafeCreateFromValue(reference.Year, reference.Month, num);
                                var numWeekDayInt = (int)date.DayOfWeek;

                                // Get week day from text directly, compare it with the weekday generated above
                                // to see whether they refer to the same week day
                                var extractedWeekDayStr = matchCase.Groups["weekday"].Value;

                                if (!date.Equals(DateObject.MinValue) &&
                                    numWeekDayInt == Config.DayOfWeek[extractedWeekDayStr])
                                {
                                    ret.Add(new Token(matchCase.Index, matchCase.Index + matchCase.Length));

                                    isFound = true;
                                }
                            }
                        }
                    }

                    if (isFound)
                    {
                        continue;
                    }

                    // Handling cases like 'Monday 21', which both 'Monday' and '21' refer to the same date
                    // The year of expected date can be different to the year of referenceDate.
                    matches = this.Config.WeekDayAndDayRegex.Matches(text);
                    foreach (Match matchCase in matches)
                    {
                        if (matchCase.Success)
                        {
                            var matchLength = result.Start + result.Length - matchCase.Index;

                            if (matchLength == matchCase.Length)
                            {
                                // check if day number is compatible with reference month
                                if (DateObjectExtension.IsValidDate(reference.Year, reference.Month, num) || !this.Config.CheckBothBeforeAfter)
                                {
                                    ret.Add(new Token(matchCase.Index, result.Start + result.Length ?? 0));
                                    isFound = true;
                                }
                            }
                        }
                    }

                    if (isFound)
                    {
                        continue;
                    }

                    // Handling cases like '20th of next month'
                    var suffixStr  = text.Substring(result.Start + result.Length ?? 0);
                    var beginMatch = this.Config.RelativeMonthRegex.MatchBegin(suffixStr.Trim(), trim: true);

                    if (beginMatch.Success && beginMatch.Index == 0)
                    {
                        var spaceLen = suffixStr.Length - suffixStr.Trim().Length;
                        var resStart = result.Start;
                        var resEnd   = resStart + result.Length + spaceLen + beginMatch.Length;

                        // Check if prefix contains 'the', include it if any
                        var prefix      = text.Substring(0, resStart ?? 0);
                        var prefixMatch = this.Config.PrefixArticleRegex.Match(prefix);

                        if (prefixMatch.Success)
                        {
                            resStart = prefixMatch.Index;
                        }

                        ret.Add(new Token(resStart ?? 0, resEnd ?? 0));
                    }

                    // Handling cases like 'second Sunday'
                    suffixStr = text.Substring(result.Start + result.Length ?? 0);

                    beginMatch = this.Config.WeekDayRegex.MatchBegin(suffixStr.Trim(), trim: true);

                    if (beginMatch.Success && num >= 1 && num <= 5 &&
                        result.Type.Equals(Number.Constants.SYS_NUM_ORDINAL, StringComparison.Ordinal) &&
                        !this.Config.WeekDayRegex.IsExactMatch(result.Text, trim: true))
                    {
                        var weekDayStr = beginMatch.Groups["weekday"].Value;
                        if (this.Config.DayOfWeek.ContainsKey(weekDayStr))
                        {
                            var spaceLen = suffixStr.Length - suffixStr.Trim().Length;
                            ret.Add(new Token(result.Start ?? 0, result.Start + result.Length + spaceLen + beginMatch.Length ?? 0));
                        }
                    }
                }

                // For cases like "I'll go back twenty second of June"
                if (result.Start + result.Length < text.Length)
                {
                    var afterStr = text.Substring(result.Start + result.Length ?? 0);

                    var match = this.Config.OfMonth.Match(afterStr);
                    if (match.Success)
                    {
                        var startIndex = result.Start ?? 0;
                        var endIndex   = (result.Start + result.Length ?? 0) + match.Length;

                        ExtendWithWeekdayAndYear(ref startIndex, ref endIndex,
                                                 Config.MonthOfYear.GetValueOrDefault(match.Groups["month"].Value, reference.Month),
                                                 num, text, reference);

                        ret.Add(new Token(startIndex, endIndex));
                    }
                }
            }

            return(ret);
        }
        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;
            }

            // Push, save the MOD string
            bool hasBefore = false, hasAfter = false, hasSince = false, hasAround = 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
            bool hasInclusiveModifier = false;
            var  modStr      = string.Empty;
            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  aroundMatch = Config.AroundRegex.MatchBegin(er.Text, trim: true);

            if (beforeMatch.Success)
            {
                hasBefore  = true;
                er.Start  += beforeMatch.Length;
                er.Length -= beforeMatch.Length;
                er.Text    = er.Text.Substring(beforeMatch.Length);
                modStr     = beforeMatch.Value;

                if (!string.IsNullOrEmpty(beforeMatch.Groups["include"].Value))
                {
                    hasInclusiveModifier = true;
                }
            }
            else if (afterMatch.Success)
            {
                hasAfter   = true;
                er.Start  += afterMatch.Length;
                er.Length -= afterMatch.Length;
                er.Text    = er.Text.Substring(afterMatch.Length);
                modStr     = afterMatch.Value;

                if (!string.IsNullOrEmpty(afterMatch.Groups["include"].Value))
                {
                    hasInclusiveModifier = true;
                }
            }
            else if (sinceMatch.Success)
            {
                hasSince   = true;
                er.Start  += sinceMatch.Length;
                er.Length -= sinceMatch.Length;
                er.Text    = er.Text.Substring(sinceMatch.Length);
                modStr     = sinceMatch.Value;
            }
            else if (aroundMatch.Success)
            {
                hasAround  = true;
                er.Start  += aroundMatch.Length;
                er.Length -= aroundMatch.Length;
                er.Text    = er.Text.Substring(aroundMatch.Length);
                modStr     = aroundMatch.Value;
            }
            else if ((er.Type.Equals(Constants.SYS_DATETIME_DATEPERIOD) && Config.YearRegex.Match(er.Text).Success) || er.Type.Equals(Constants.SYS_DATETIME_DATE))
            {
                // 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
                var match = Config.DateAfter.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;
                }
            }

            if (er.Type.Equals(Constants.SYS_DATETIME_DATE))
            {
                pr = this.Config.DateParser.Parse(er, referenceTime);
                if (pr.Value == null)
                {
                    pr = Config.HolidayParser.Parse(er, referenceTime);
                }
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_TIME))
            {
                pr = this.Config.TimeParser.Parse(er, referenceTime);
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_DATETIME))
            {
                pr = this.Config.DateTimeParser.Parse(er, referenceTime);
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_DATEPERIOD))
            {
                pr = this.Config.DatePeriodParser.Parse(er, referenceTime);
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_TIMEPERIOD))
            {
                pr = this.Config.TimePeriodParser.Parse(er, referenceTime);
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_DATETIMEPERIOD))
            {
                pr = this.Config.DateTimePeriodParser.Parse(er, referenceTime);
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_DURATION))
            {
                pr = this.Config.DurationParser.Parse(er, referenceTime);
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_SET))
            {
                pr = this.Config.SetParser.Parse(er, referenceTime);
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_DATETIMEALT))
            {
                pr = this.Config.DateTimeAltParser.Parse(er, referenceTime);
            }
            else if (er.Type.Equals(Constants.SYS_DATETIME_TIMEZONE))
            {
                if ((Config.Options & DateTimeOptions.EnablePreview) != 0)
                {
                    pr = this.Config.TimeZoneParser.Parse(er, referenceTime);
                }
            }
            else
            {
                return(null);
            }

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

                if (!hasInclusiveModifier)
                {
                    val.Mod = Constants.BEFORE_MOD;
                }
                else
                {
                    val.Mod = Constants.UNTIL_MOD;
                }

                pr.Value = val;
            }

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

                if (!hasInclusiveModifier)
                {
                    val.Mod = Constants.AFTER_MOD;
                }
                else
                {
                    val.Mod = Constants.SINCE_MOD;
                }

                pr.Value = val;
            }

            if (hasSince && (pr != null && pr.Value != null))
            {
                pr.Length += modStr.Length;
                pr.Start  -= modStr.Length;
                pr.Text    = modStr + pr.Text;
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod  = Constants.SINCE_MOD;
                pr.Value = val;
            }

            if (hasAround && (pr != null && pr.Value != null))
            {
                pr.Length += modStr.Length;
                pr.Start  -= modStr.Length;
                pr.Text    = modStr + pr.Text;
                var val = (DateTimeResolutionResult)pr.Value;
                val.Mod  = Constants.APPROX_MOD;
                pr.Value = val;
            }

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

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

            // 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 = SetInclusivePeriodEnd(pr);
                }
            }

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

            return(pr);
        }
예제 #18
0
        private static DateTimeResolutionResult GetAgoLaterResult(
            DateTimeParseResult durationParseResult,
            string afterStr,
            string beforeStr,
            DateObject referenceTime,
            IParser numberParser,
            IDateTimeUtilityConfiguration utilityConfiguration,
            AgoLaterMode mode,
            SwiftDayDelegate swiftDay)
        {
            var ret            = new DateTimeResolutionResult();
            var resultDateTime = referenceTime;
            var timex          = durationParseResult.TimexStr;

            if (((DateTimeResolutionResult)durationParseResult.Value).Mod == Constants.MORE_THAN_MOD)
            {
                ret.Mod = Constants.MORE_THAN_MOD;
            }
            else if (((DateTimeResolutionResult)durationParseResult.Value).Mod == Constants.LESS_THAN_MOD)
            {
                ret.Mod = Constants.LESS_THAN_MOD;
            }

            if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.AgoRegex))
            {
                var match = utilityConfiguration.AgoRegex.Match(afterStr);
                var swift = 0;

                // Handle cases like "3 days before yesterday"
                if (match.Success && !string.IsNullOrEmpty(match.Groups["day"].Value))
                {
                    swift = swiftDay(match.Groups["day"].Value);
                }
                else if (utilityConfiguration.CheckBothBeforeAfter && match.Success && !MatchingUtil.ContainsAgoLaterIndexInBeforeString(beforeStr, utilityConfiguration.AgoRegex))
                {
                    match = utilityConfiguration.AgoRegex.Match(beforeStr + " " + afterStr);
                    if (match.Success && !string.IsNullOrEmpty(match.Groups["day"].Value))
                    {
                        swift = swiftDay(match.Groups["day"].Value);
                    }
                }

                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime.AddDays(swift), false);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = Constants.BEFORE_MOD;
            }
            else if (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.ContainsAgoLaterIndexInBeforeString(beforeStr, utilityConfiguration.AgoRegex))
            {
                var match = utilityConfiguration.AgoRegex.Match(beforeStr);
                var swift = 0;

                // Handle cases like "3 days before yesterday"
                if (match.Success && !string.IsNullOrEmpty(match.Groups["day"].Value))
                {
                    swift = swiftDay(match.Groups["day"].Value);
                }

                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime.AddDays(swift), false);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = Constants.BEFORE_MOD;
            }
            else if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.LaterRegex) ||
                     MatchingUtil.ContainsTermIndex(beforeStr, utilityConfiguration.InConnectorRegex) ||
                     (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.ContainsAgoLaterIndexInBeforeString(beforeStr, utilityConfiguration.LaterRegex)))
            {
                var match = utilityConfiguration.LaterRegex.Match(afterStr);
                var swift = 0;

                if (utilityConfiguration.CheckBothBeforeAfter && MatchingUtil.ContainsAgoLaterIndexInBeforeString(beforeStr, utilityConfiguration.LaterRegex) && string.IsNullOrEmpty(match.Groups["day"].Value))
                {
                    match = utilityConfiguration.LaterRegex.Match(beforeStr);
                }

                // Handle cases like "3 days after tomorrow"
                if (match.Success && !string.IsNullOrEmpty(match.Groups["day"].Value))
                {
                    swift = swiftDay(match.Groups["day"].Value);
                }

                var yearMatch = utilityConfiguration.SinceYearSuffixRegex.Match(afterStr);
                if (yearMatch.Success)
                {
                    var yearString = yearMatch.Groups[Constants.YearGroupName].Value;
                    var yearEr     = new ExtractResult {
                        Text = yearString
                    };
                    var year = Convert.ToInt32((double)(numberParser.Parse(yearEr).Value ?? 0));
                    referenceTime = DateObject.MinValue.SafeCreateFromValue(year, 1, 1);
                }

                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime.AddDays(swift), true);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = Constants.AFTER_MOD;
            }
            else if (utilityConfiguration.CheckBothBeforeAfter && (MatchingUtil.ContainsAgoLaterIndexInBeforeString(beforeStr, utilityConfiguration.LaterRegex) ||
                                                                   MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.InConnectorRegex) ||
                                                                   MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.LaterRegex)))
            {
                // Check also beforeStr
                var match = utilityConfiguration.LaterRegex.Match(beforeStr);
                var swift = 0;

                if (MatchingUtil.ContainsAgoLaterIndex(afterStr, utilityConfiguration.LaterRegex) && string.IsNullOrEmpty(match.Groups["day"].Value))
                {
                    match = utilityConfiguration.LaterRegex.Match(beforeStr);
                }

                // Handle cases like "3 days after tomorrow"
                if (match.Success && !string.IsNullOrEmpty(match.Groups["day"].Value))
                {
                    swift = swiftDay(match.Groups["day"].Value);
                }

                var yearMatch = utilityConfiguration.SinceYearSuffixRegex.Match(beforeStr);
                if (yearMatch.Success)
                {
                    var yearString = yearMatch.Groups[Constants.YearGroupName].Value;
                    var yearEr     = new ExtractResult {
                        Text = yearString
                    };
                    var year = Convert.ToInt32((double)(numberParser.Parse(yearEr).Value ?? 0));
                    referenceTime = DateObject.MinValue.SafeCreateFromValue(year, 1, 1);
                }

                resultDateTime = DurationParsingUtil.ShiftDateTime(timex, referenceTime.AddDays(swift), true);

                ((DateTimeResolutionResult)durationParseResult.Value).Mod = Constants.AFTER_MOD;
            }

            if (resultDateTime != referenceTime)
            {
                if (mode.Equals(AgoLaterMode.Date))
                {
                    ret.Timex = $"{DateTimeFormatUtil.LuisDate(resultDateTime)}";
                }
                else if (mode.Equals(AgoLaterMode.DateTime))
                {
                    ret.Timex = $"{DateTimeFormatUtil.LuisDateTime(resultDateTime)}";
                }

                ret.FutureValue         = ret.PastValue = resultDateTime;
                ret.SubDateTimeEntities = new List <object> {
                    durationParseResult
                };
                ret.Success = true;
            }

            return(ret);
        }