Example #1
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);
        }
        private DateTimeResolutionResult ParseSimpleCases(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();
            int year = referenceTime.Year, month = referenceTime.Month, day = referenceTime.Day;
            var trimedText = text.Trim().ToLower();

            var match = this.config.PureNumberFromToRegex.Match(trimedText);

            if (!match.Success)
            {
                match = this.config.PureNumberBetweenAndRegex.Match(trimedText);
            }

            if (match.Success && match.Index == 0)
            {
                // this "from .. to .." pattern is valid if followed by a Date OR "pm"
                var isValid = false;

                // get hours
                var hourGroup = match.Groups["hour"];
                var hourStr   = hourGroup.Captures[0].Value;

                if (!this.config.Numbers.TryGetValue(hourStr, out int beginHour))
                {
                    beginHour = int.Parse(hourStr);
                }

                hourStr = hourGroup.Captures[1].Value;

                if (!this.config.Numbers.TryGetValue(hourStr, out int endHour))
                {
                    endHour = int.Parse(hourStr);
                }

                // parse "pm"
                var leftDesc  = match.Groups["leftDesc"].Value;
                var rightDesc = match.Groups["rightDesc"].Value;
                var pmStr     = match.Groups["pm"].Value;
                var amStr     = match.Groups["am"].Value;
                var descStr   = match.Groups["desc"].Value;

                // The "ampm" only occurs in time, we don't have to consider it here
                if (string.IsNullOrEmpty(leftDesc))
                {
                    bool rightAmValid = !string.IsNullOrEmpty(rightDesc) &&
                                        config.UtilityConfiguration.AmDescRegex.Match(rightDesc.ToLower()).Success;
                    bool rightPmValid = !string.IsNullOrEmpty(rightDesc) &&
                                        config.UtilityConfiguration.PmDescRegex.Match(rightDesc.ToLower()).Success;

                    if (!string.IsNullOrEmpty(amStr) || rightAmValid)
                    {
                        if (endHour >= 12)
                        {
                            endHour -= 12;
                        }

                        if (beginHour >= 12 && beginHour - 12 < endHour)
                        {
                            beginHour -= 12;
                        }

                        // Resolve case like "11 to 3am"
                        if (beginHour < 12 && beginHour > endHour)
                        {
                            beginHour += 12;
                        }

                        isValid = true;
                    }
                    else if (!string.IsNullOrEmpty(pmStr) || rightPmValid)
                    {
                        if (endHour < 12)
                        {
                            endHour += 12;
                        }

                        // Resolve case like "11 to 3pm"
                        if (beginHour + 12 < endHour)
                        {
                            beginHour += 12;
                        }

                        isValid = true;
                    }
                }

                if (isValid)
                {
                    var beginStr = "T" + beginHour.ToString("D2");
                    var endStr   = "T" + endHour.ToString("D2");

                    if (endHour >= beginHour)
                    {
                        ret.Timex = $"({beginStr},{endStr},PT{endHour - beginHour}H)";
                    }
                    else
                    {
                        ret.Timex = $"({beginStr},{endStr},PT{endHour - beginHour + 24}H)";
                    }

                    ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(
                        DateObject.MinValue.SafeCreateFromValue(year, month, day, beginHour, 0, 0),
                        DateObject.MinValue.SafeCreateFromValue(year, month, day, endHour, 0, 0));

                    ret.Success = true;

                    return(ret);
                }
            }

            return(ret);
        }
Example #3
0
        // Handle cases like "January first", "twenty-two of August"
        // Handle cases like "20th of next month"
        private DateTimeResolutionResult ParseNumberWithMonth(string text, DateObject referenceDate)
        {
            var ret = new DateTimeResolutionResult();

            var  trimmedText = text.Trim();
            int  month = 0, day = 0, year = referenceDate.Year;
            bool ambiguous = true;

            var er = this.config.OrdinalExtractor.Extract(trimmedText);

            if (er.Count == 0)
            {
                er = this.config.IntegerExtractor.Extract(trimmedText);
            }

            if (er.Count == 0)
            {
                return(ret);
            }

            var num = Convert.ToInt32((double)(this.config.NumberParser.Parse(er[0]).Value ?? 0));

            var match = this.config.MonthRegex.Match(trimmedText);

            if (match.Success)
            {
                month = this.config.MonthOfYear[match.Value.Trim()];
                day   = num;

                var suffix    = trimmedText.Substring(er[0].Start + er[0].Length ?? 0);
                var matchYear = this.config.YearSuffix.Match(suffix);
                if (matchYear.Success)
                {
                    year = ((BaseDateExtractor)this.config.DateExtractor).GetYearFromText(matchYear);
                    if (year != Constants.InvalidYear)
                    {
                        ambiguous = false;
                    }
                }
            }

            // Handling relative month
            if (!match.Success)
            {
                match = this.config.RelativeMonthRegex.Match(trimmedText);
                if (match.Success)
                {
                    var monthStr = match.Groups["order"].Value;
                    var swift    = this.config.GetSwiftMonthOrYear(monthStr);
                    month     = referenceDate.AddMonths(swift).Month;
                    year      = referenceDate.AddMonths(swift).Year;
                    day       = num;
                    ambiguous = false;
                }
            }

            // Handling cases like 'second Sunday'
            if (!match.Success)
            {
                match = this.config.WeekDayRegex.Match(trimmedText);
                if (match.Success)
                {
                    month = referenceDate.Month;

                    // Resolve the date of wanted week day
                    var wantedWeekDay      = this.config.DayOfWeek[match.Groups["weekday"].Value];
                    var firstDate          = DateObject.MinValue.SafeCreateFromValue(referenceDate.Year, referenceDate.Month, 1);
                    var firstWeekDay       = (int)firstDate.DayOfWeek;
                    var firstWantedWeekDay = firstDate.AddDays(wantedWeekDay > firstWeekDay ? wantedWeekDay - firstWeekDay : wantedWeekDay - firstWeekDay + 7);
                    var answerDay          = firstWantedWeekDay.Day + ((num - 1) * 7);
                    day       = answerDay;
                    ambiguous = false;
                }
            }

            if (!match.Success)
            {
                return(ret);
            }

            // For LUIS format value string
            var futureDate = DateObject.MinValue.SafeCreateFromValue(year, month, day);
            var pastDate   = DateObject.MinValue.SafeCreateFromValue(year, month, day);

            if (ambiguous)
            {
                ret.Timex = DateTimeFormatUtil.LuisDate(-1, month, day);
                if (futureDate < referenceDate)
                {
                    futureDate = futureDate.AddYears(+1);
                }

                if (pastDate >= referenceDate)
                {
                    pastDate = pastDate.AddYears(-1);
                }
            }
            else
            {
                ret.Timex = DateTimeFormatUtil.LuisDate(year, month, day);
            }

            ret.FutureValue = futureDate;
            ret.PastValue   = pastDate;
            ret.Success     = true;

            return(ret);
        }
Example #4
0
        private DateTimeResolutionResult MergeTwoTimePoints(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();
            DateTimeParseResult pr1 = null, pr2 = null;
            bool bothHaveDates = false, beginHasDate = false, endHasDate = false;

            var timeExtractResults     = this.Config.TimeExtractor.Extract(text, referenceTime);
            var dateTimeExtractResults = this.Config.DateTimeExtractor.Extract(text, referenceTime);

            if (dateTimeExtractResults.Count == 2)
            {
                pr1           = this.Config.DateTimeParser.Parse(dateTimeExtractResults[0], referenceTime);
                pr2           = this.Config.DateTimeParser.Parse(dateTimeExtractResults[1], referenceTime);
                bothHaveDates = true;
            }
            else if (dateTimeExtractResults.Count == 1 && timeExtractResults.Count == 2)
            {
                if (!dateTimeExtractResults[0].IsOverlap(timeExtractResults[0]))
                {
                    pr1        = this.Config.TimeParser.Parse(timeExtractResults[0], referenceTime);
                    pr2        = this.Config.DateTimeParser.Parse(dateTimeExtractResults[0], referenceTime);
                    endHasDate = true;
                }
                else
                {
                    pr1          = this.Config.DateTimeParser.Parse(dateTimeExtractResults[0], referenceTime);
                    pr2          = this.Config.TimeParser.Parse(timeExtractResults[1], referenceTime);
                    beginHasDate = true;
                }
            }
            else if (dateTimeExtractResults.Count == 1 && timeExtractResults.Count == 1)
            {
                if (timeExtractResults[0].Start < dateTimeExtractResults[0].Start)
                {
                    pr1        = this.Config.TimeParser.Parse(timeExtractResults[0], referenceTime);
                    pr2        = this.Config.DateTimeParser.Parse(dateTimeExtractResults[0], referenceTime);
                    endHasDate = true;
                }
                else if (timeExtractResults[0].Start >= dateTimeExtractResults[0].Start + dateTimeExtractResults[0].Length)
                {
                    pr1          = this.Config.DateTimeParser.Parse(dateTimeExtractResults[0], referenceTime);
                    pr2          = this.Config.TimeParser.Parse(timeExtractResults[0], referenceTime);
                    beginHasDate = true;
                }
                else
                {
                    // If the only TimeExtractResult is part of DateTimeExtractResult, then it should not be handled in this method
                    return(ret);
                }
            }
            else if (timeExtractResults.Count == 2)
            {
                // If both ends are Time. then this is a TimePeriod, not a DateTimePeriod
                return(ret);
            }
            else
            {
                return(ret);
            }

            if (pr1.Value == null || pr2.Value == null)
            {
                return(ret);
            }

            DateObject futureBegin = (DateObject)((DateTimeResolutionResult)pr1.Value).FutureValue,
                       futureEnd   = (DateObject)((DateTimeResolutionResult)pr2.Value).FutureValue;

            DateObject pastBegin = (DateObject)((DateTimeResolutionResult)pr1.Value).PastValue,
                       pastEnd   = (DateObject)((DateTimeResolutionResult)pr2.Value).PastValue;

            if (bothHaveDates)
            {
                if (futureBegin > futureEnd)
                {
                    futureBegin = pastBegin;
                }

                if (pastEnd < pastBegin)
                {
                    pastEnd = futureEnd;
                }
            }

            if (bothHaveDates)
            {
                ret.Timex = $"({pr1.TimexStr},{pr2.TimexStr},PT{Convert.ToInt32((futureEnd - futureBegin).TotalHours)}H)";

                // Do nothing
            }
            else if (beginHasDate)
            {
                futureEnd = DateObject.MinValue.SafeCreateFromValue(
                    futureBegin.Year, futureBegin.Month, futureBegin.Day, futureEnd.Hour, futureEnd.Minute, futureEnd.Second);

                pastEnd = DateObject.MinValue.SafeCreateFromValue(
                    pastBegin.Year, pastBegin.Month, pastBegin.Day, pastEnd.Hour, pastEnd.Minute, pastEnd.Second);

                var dateStr     = pr1.TimexStr.Split('T')[0];
                var durationStr = DateTimeFormatUtil.LuisTimeSpan(futureEnd - futureBegin);
                ret.Timex = $"({pr1.TimexStr},{dateStr + pr2.TimexStr},{durationStr}";
            }
            else if (endHasDate)
            {
                futureBegin = DateObject.MinValue.SafeCreateFromValue(
                    futureEnd.Year, futureEnd.Month, futureEnd.Day, futureBegin.Hour, futureBegin.Minute, futureBegin.Second);

                pastBegin = DateObject.MinValue.SafeCreateFromValue(
                    pastEnd.Year, pastEnd.Month, pastEnd.Day, pastBegin.Hour, pastBegin.Minute, pastBegin.Second);

                var dateStr     = pr2.TimexStr.Split('T')[0];
                var durationStr = DateTimeFormatUtil.LuisTimeSpan(pastEnd - pastBegin);
                ret.Timex = $"({dateStr + pr1.TimexStr},{pr2.TimexStr},PT{Convert.ToInt32((pastEnd - pastBegin).TotalHours)}H)";
            }

            var ampmStr1 = ((DateTimeResolutionResult)pr1.Value).Comment;
            var ampmStr2 = ((DateTimeResolutionResult)pr2.Value).Comment;

            if (!string.IsNullOrEmpty(ampmStr1) && ampmStr1.EndsWith(Constants.Comment_AmPm) &&
                !string.IsNullOrEmpty(ampmStr2) && ampmStr2.EndsWith(Constants.Comment_AmPm))
            {
                ret.Comment = Constants.Comment_AmPm;
            }

            if ((this.Config.Options & DateTimeOptions.EnablePreview) != 0)
            {
                if (((DateTimeResolutionResult)pr1.Value).TimeZoneResolution != null)
                {
                    ret.TimeZoneResolution = ((DateTimeResolutionResult)pr1.Value).TimeZoneResolution;
                }
                else if (((DateTimeResolutionResult)pr2.Value).TimeZoneResolution != null)
                {
                    ret.TimeZoneResolution = ((DateTimeResolutionResult)pr2.Value).TimeZoneResolution;
                }
            }

            ret.FutureValue = new Tuple <DateObject, DateObject>(futureBegin, futureEnd);
            ret.PastValue   = new Tuple <DateObject, DateObject>(pastBegin, pastEnd);
            ret.Success     = true;

            ret.SubDateTimeEntities = new List <object> {
                pr1, pr2
            };

            return(ret);
        }
        private DateTimeResolutionResult MergeTwoTimePoints(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();
            DateTimeParseResult pr1 = null, pr2 = null;
            var validTimeNumber = false;

            var ers = this.config.TimeExtractor.Extract(text, referenceTime);

            if (ers.Count != 2)
            {
                if (ers.Count == 1)
                {
                    var numErs = this.config.IntegerExtractor.Extract(text);

                    foreach (var num in numErs)
                    {
                        int midStrBegin = 0, midStrEnd = 0;
                        // ending number
                        if (num.Start > ers[0].Start + ers[0].Length)
                        {
                            midStrBegin = ers[0].Start + ers[0].Length ?? 0;
                            midStrEnd   = num.Start - midStrBegin ?? 0;
                        }
                        else if (num.Start + num.Length < ers[0].Start)
                        {
                            midStrBegin = num.Start + num.Length ?? 0;
                            midStrEnd   = ers[0].Start - midStrBegin ?? 0;
                        }

                        // check if the middle string between the time point and the valid number is a connect string.
                        var middleStr = text.Substring(midStrBegin, midStrEnd);
                        var tillMatch = this.config.TillRegex.Match(middleStr);
                        if (tillMatch.Success)
                        {
                            num.Data = null;
                            num.Type = Constants.SYS_DATETIME_TIME;
                            ers.Add(num);
                            validTimeNumber = true;
                            break;
                        }
                    }

                    ers.Sort((x, y) => (x.Start - y.Start ?? 0));
                }

                if (!validTimeNumber)
                {
                    return(ret);
                }
            }

            pr1 = this.config.TimeParser.Parse(ers[0], referenceTime);
            pr2 = this.config.TimeParser.Parse(ers[1], referenceTime);

            if (pr1.Value == null || pr2.Value == null)
            {
                return(ret);
            }

            var ampmStr1 = ((DateTimeResolutionResult)pr1.Value).Comment;
            var ampmStr2 = ((DateTimeResolutionResult)pr2.Value).Comment;

            var beginTime = (DateObject)((DateTimeResolutionResult)pr1.Value).FutureValue;
            var endTime   = (DateObject)((DateTimeResolutionResult)pr2.Value).FutureValue;

            if (!string.IsNullOrEmpty(ampmStr2) && ampmStr2.EndsWith(Constants.Comment_AmPm) && endTime <= beginTime && endTime.AddHours(12) > beginTime)
            {
                endTime = endTime.AddHours(12);
                ((DateTimeResolutionResult)pr2.Value).FutureValue = endTime;
                pr2.TimexStr = $"T{endTime.Hour}";
                if (endTime.Minute > 0)
                {
                    pr2.TimexStr = $"{pr2.TimexStr}:{endTime.Minute}";
                }
            }

            if (!string.IsNullOrEmpty(ampmStr1) && ampmStr1.EndsWith(Constants.Comment_AmPm) && endTime > beginTime.AddHours(12))
            {
                beginTime = beginTime.AddHours(12);
                ((DateTimeResolutionResult)pr1.Value).FutureValue = beginTime;
                pr1.TimexStr = $"T{beginTime.Hour}";
                if (beginTime.Minute > 0)
                {
                    pr1.TimexStr = $"{pr1.TimexStr}:{beginTime.Minute}";
                }
            }

            if (endTime < beginTime)
            {
                endTime = endTime.AddDays(1);
            }

            var minutes = (endTime - beginTime).Minutes;
            var hours   = (endTime - beginTime).Hours;

            ret.Timex = $"({pr1.TimexStr},{pr2.TimexStr}," +
                        $"PT{(hours > 0 ? hours + "H" : "")}{(minutes > 0 ? minutes + "M" : "")})";
            ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginTime, endTime);
            ret.Success     = true;


            if (!string.IsNullOrEmpty(ampmStr1) && ampmStr1.EndsWith(Constants.Comment_AmPm) &&
                !string.IsNullOrEmpty(ampmStr2) && ampmStr2.EndsWith(Constants.Comment_AmPm))
            {
                ret.Comment = Constants.Comment_AmPm;
            }

            ret.SubDateTimeEntities = new List <object> {
                pr1, pr2
            };

            return(ret);
        }
Example #6
0
        // Parse "last minute", "next hour"
        private DateTimeResolutionResult ParseRelativeUnit(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();

            var match = Config.RelativeTimeUnitRegex.Match(text);

            if (!match.Success)
            {
                match = this.Config.RestOfDateTimeRegex.Match(text);
            }

            if (match.Success)
            {
                var srcUnit = match.Groups["unit"].Value.ToLower();

                var unitStr = Config.UnitMap[srcUnit];

                int swiftValue  = 1;
                var prefixMatch = Config.PreviousPrefixRegex.Match(text);
                if (prefixMatch.Success)
                {
                    swiftValue = -1;
                }

                DateObject beginTime;
                var        endTime      = beginTime = referenceTime;
                var        sufixPtTimex = string.Empty;

                if (Config.UnitMap.ContainsKey(srcUnit))
                {
                    switch (unitStr)
                    {
                    case "D":
                        endTime      = DateObject.MinValue.SafeCreateFromValue(beginTime.Year, beginTime.Month, beginTime.Day);
                        endTime      = endTime.AddDays(1).AddSeconds(-1);
                        sufixPtTimex = "PT" + (endTime - beginTime).TotalSeconds + "S";
                        break;

                    case "H":
                        beginTime    = swiftValue > 0 ? beginTime : referenceTime.AddHours(swiftValue);
                        endTime      = swiftValue > 0 ? referenceTime.AddHours(swiftValue) : endTime;
                        sufixPtTimex = "PT1H";
                        break;

                    case "M":
                        beginTime    = swiftValue > 0 ? beginTime : referenceTime.AddMinutes(swiftValue);
                        endTime      = swiftValue > 0 ? referenceTime.AddMinutes(swiftValue) : endTime;
                        sufixPtTimex = "PT1M";
                        break;

                    case "S":
                        beginTime    = swiftValue > 0 ? beginTime : referenceTime.AddSeconds(swiftValue);
                        endTime      = swiftValue > 0 ? referenceTime.AddSeconds(swiftValue) : endTime;
                        sufixPtTimex = "PT1S";
                        break;

                    default:
                        return(ret);
                    }

                    ret.Timex =
                        $"({DateTimeFormatUtil.LuisDate(beginTime)}T{DateTimeFormatUtil.LuisTime(beginTime)}," +
                        $"{DateTimeFormatUtil.LuisDate(endTime)}T{DateTimeFormatUtil.LuisTime(endTime)},{sufixPtTimex})";

                    ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginTime, endTime);
                    ret.Success     = true;

                    return(ret);
                }
            }

            return(ret);
        }
Example #7
0
        private DateTimeResolutionResult MergeDateWithSingleTimePeriod(string text, DateObject referenceTime)
        {
            var ret         = new DateTimeResolutionResult();
            var trimmedText = text.Trim().ToLower();

            var ers = Config.TimePeriodExtractor.Extract(trimmedText, referenceTime);

            if (ers.Count == 0)
            {
                return(ParsePureNumberCases(text, referenceTime));
            }
            else if (ers.Count == 1)
            {
                var timePeriodParseResult      = Config.TimePeriodParser.Parse(ers[0]);
                var timePeriodResolutionResult = (DateTimeResolutionResult)timePeriodParseResult.Value;

                if (timePeriodResolutionResult == null)
                {
                    return(ParsePureNumberCases(text, referenceTime));
                }

                var periodTimex = timePeriodResolutionResult.Timex;

                // If it is a range type timex
                if (TimexUtility.IsRangeTimex(periodTimex))
                {
                    var dateResult = this.Config.DateExtractor.Extract(trimmedText.Replace(ers[0].Text, string.Empty), referenceTime);

                    var dateText = trimmedText.Replace(ers[0].Text, string.Empty).Replace(Config.TokenBeforeDate, string.Empty).Trim();

                    // If only one Date is extracted and the Date text equals to the rest part of source text
                    if (dateResult.Count == 1 && dateText.Equals(dateResult[0].Text))
                    {
                        string     dateTimex;
                        DateObject futureTime;
                        DateObject pastTime;

                        var pr = this.Config.DateParser.Parse(dateResult[0], referenceTime);

                        if (pr.Value != null)
                        {
                            futureTime = (DateObject)((DateTimeResolutionResult)pr.Value).FutureValue;
                            pastTime   = (DateObject)((DateTimeResolutionResult)pr.Value).PastValue;

                            dateTimex = pr.TimexStr;
                        }
                        else
                        {
                            return(ParsePureNumberCases(text, referenceTime));
                        }

                        var rangeTimexComponents = TimexUtility.GetRangeTimexComponents(periodTimex);

                        if (rangeTimexComponents.IsValid)
                        {
                            var beginTimex = TimexUtility.CombineDateAndTimeTimex(dateTimex, rangeTimexComponents.BeginTimex);
                            var endTimex   = TimexUtility.CombineDateAndTimeTimex(dateTimex, rangeTimexComponents.EndTimex);
                            ret.Timex = TimexUtility.GenerateDateTimePeriodTimex(beginTimex, endTimex, rangeTimexComponents.DurationTimex);

                            var timePeriodFutureValue = (Tuple <DateObject, DateObject>)timePeriodResolutionResult.FutureValue;
                            var beginTime             = timePeriodFutureValue.Item1;
                            var endTime = timePeriodFutureValue.Item2;

                            ret.FutureValue = new Tuple <DateObject, DateObject>(
                                DateObject.MinValue.SafeCreateFromValue(
                                    futureTime.Year, futureTime.Month, futureTime.Day, beginTime.Hour, beginTime.Minute, beginTime.Second),
                                DateObject.MinValue.SafeCreateFromValue(
                                    futureTime.Year, futureTime.Month, futureTime.Day, endTime.Hour, endTime.Minute, endTime.Second));

                            ret.PastValue = new Tuple <DateObject, DateObject>(
                                DateObject.MinValue.SafeCreateFromValue(
                                    pastTime.Year, pastTime.Month, pastTime.Day, beginTime.Hour, beginTime.Minute, beginTime.Second),
                                DateObject.MinValue.SafeCreateFromValue(
                                    pastTime.Year, pastTime.Month, pastTime.Day, endTime.Hour, endTime.Minute, endTime.Second));

                            if (!string.IsNullOrEmpty(timePeriodResolutionResult.Comment) &&
                                timePeriodResolutionResult.Comment.Equals(Constants.Comment_AmPm))
                            {
                                // AmPm comment is used for later SetParserResult to judge whether this parse result should have two parsing results
                                // Cases like "from 10:30 to 11 on 1/1/2015" should have AmPm comment, as it can be parsed to "10:30am to 11am" and also be parsed to "10:30pm to 11pm"
                                // Cases like "from 10:30 to 3 on 1/1/2015" should not have AmPm comment
                                if (beginTime.Hour < Constants.HalfDayHourCount && endTime.Hour < Constants.HalfDayHourCount)
                                {
                                    ret.Comment = Constants.Comment_AmPm;
                                }
                            }

                            ret.Success             = true;
                            ret.SubDateTimeEntities = new List <object> {
                                pr, timePeriodParseResult
                            };

                            return(ret);
                        }
                    }

                    return(ParsePureNumberCases(text, referenceTime));
                }
            }

            return(ret);
        }
        // Cases like "from 3 to 5pm" or "between 4 and 6am", time point is pure number without colon
        private DateTimeResolutionResult ParsePureNumCases(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();
            int year = referenceTime.Year, month = referenceTime.Month, day = referenceTime.Day;
            var trimmedText = text.Trim().ToLower();

            var match = this.config.PureNumberFromToRegex.MatchBegin(trimmedText, trim: true);

            if (!match.Success)
            {
                match = this.config.PureNumberBetweenAndRegex.MatchBegin(trimmedText, trim: true);
            }

            if (match.Success)
            {
                // this "from .. to .." pattern is valid if followed by a Date OR "pm"
                var isValid = false;

                // get hours
                var hourGroup      = match.Groups[Constants.HourGroupName];
                var hourStr        = hourGroup.Captures[0].Value;
                var afterHourIndex = hourGroup.Captures[0].Index + hourGroup.Captures[0].Length;

                // hard to integrate this part into the regex
                if (afterHourIndex == trimmedText.Length || !trimmedText.Substring(afterHourIndex).Trim().StartsWith(":"))
                {
                    if (!this.config.Numbers.TryGetValue(hourStr, out int beginHour))
                    {
                        beginHour = int.Parse(hourStr);
                    }

                    hourStr        = hourGroup.Captures[1].Value;
                    afterHourIndex = hourGroup.Captures[1].Index + hourGroup.Captures[1].Length;

                    if (afterHourIndex == trimmedText.Length || !trimmedText.Substring(afterHourIndex).Trim().StartsWith(":"))
                    {
                        if (!this.config.Numbers.TryGetValue(hourStr, out int endHour))
                        {
                            endHour = int.Parse(hourStr);
                        }

                        // parse "pm"
                        var leftDesc   = match.Groups["leftDesc"].Value;
                        var rightDesc  = match.Groups["rightDesc"].Value;
                        var matchPmStr = match.Groups[Constants.PmGroupName].Value;
                        var matchAmStr = match.Groups[Constants.AmGroupName].Value;
                        var descStr    = match.Groups[Constants.DescGroupName].Value;

                        // The "ampm" only occurs in time, we don't have to consider it here
                        if (string.IsNullOrEmpty(leftDesc))
                        {
                            var rightAmValid = !string.IsNullOrEmpty(rightDesc) &&
                                               config.UtilityConfiguration.AmDescRegex.Match(rightDesc.ToLower()).Success;
                            var rightPmValid = !string.IsNullOrEmpty(rightDesc) &&
                                               config.UtilityConfiguration.PmDescRegex.Match(rightDesc.ToLower()).Success;

                            if (!string.IsNullOrEmpty(matchAmStr) || rightAmValid)
                            {
                                if (endHour >= Constants.HalfDayHourCount)
                                {
                                    endHour -= Constants.HalfDayHourCount;
                                }

                                if (beginHour >= Constants.HalfDayHourCount && beginHour - Constants.HalfDayHourCount < endHour)
                                {
                                    beginHour -= Constants.HalfDayHourCount;
                                }

                                // Resolve case like "11 to 3am"
                                if (beginHour < Constants.HalfDayHourCount && beginHour > endHour)
                                {
                                    beginHour += Constants.HalfDayHourCount;
                                }

                                isValid = true;
                            }
                            else if (!string.IsNullOrEmpty(matchPmStr) || rightPmValid)
                            {
                                if (endHour < Constants.HalfDayHourCount)
                                {
                                    endHour += Constants.HalfDayHourCount;
                                }

                                // Resolve case like "11 to 3pm"
                                if (beginHour + Constants.HalfDayHourCount < endHour)
                                {
                                    beginHour += Constants.HalfDayHourCount;
                                }

                                isValid = true;
                            }
                        }

                        if (isValid)
                        {
                            var beginStr = "T" + beginHour.ToString("D2");
                            var endStr   = "T" + endHour.ToString("D2");

                            if (endHour >= beginHour)
                            {
                                ret.Timex = $"({beginStr},{endStr},PT{endHour - beginHour}H)";
                            }
                            else
                            {
                                ret.Timex = $"({beginStr},{endStr},PT{endHour - beginHour + 24}H)";
                            }

                            // Try to get the timezone resolution
                            var timeErs = config.TimeExtractor.Extract(trimmedText);
                            foreach (var er in timeErs)
                            {
                                var pr = config.TimeParser.Parse(er, referenceTime);
                                if (((DateTimeResolutionResult)pr.Value).TimeZoneResolution != null)
                                {
                                    ret.TimeZoneResolution = ((DateTimeResolutionResult)pr.Value).TimeZoneResolution;
                                    break;
                                }
                            }

                            ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(
                                DateObject.MinValue.SafeCreateFromValue(year, month, day, beginHour, 0, 0),
                                DateObject.MinValue.SafeCreateFromValue(year, month, day, endHour, 0, 0));

                            ret.Success = true;
                        }
                    }
                }
            }

            return(ret);
        }
        // Cases like "from 3:30 to 5" or "between 3:30am to 6pm", at least one of the time point contains colon
        private DateTimeResolutionResult ParseSpecificTimeCases(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();
            int year = referenceTime.Year, month = referenceTime.Month, day = referenceTime.Day;

            // Handle cases like "from 4:30 to 5"
            var match = config.SpecificTimeFromToRegex.MatchExact(text, trim: true);

            if (!match.Success)
            {
                // Handle cases like "between 5:10 and 7"
                match = config.SpecificTimeBetweenAndRegex.MatchExact(text, trim: true);
            }

            if (match.Success)
            {
                // Cases like "half past seven" are not handled here
                if (match.Groups[Constants.PrefixGroupName].Success)
                {
                    return(ret);
                }

                // Cases like "4" is different with "4:00" as the Timex is different "T04H" vs "T04H00M"
                int beginHour;
                int beginMinute = Constants.InvalidMinute;
                int beginSecond = Constants.InvalidSecond;
                int endHour;
                int endMinute = Constants.InvalidMinute;
                int endSecond = Constants.InvalidSecond;

                // Get time1 and time2
                var hourGroup = match.Groups[Constants.HourGroupName];

                var hourStr = hourGroup.Captures[0].Value;

                if (config.Numbers.ContainsKey(hourStr))
                {
                    beginHour = config.Numbers[hourStr];
                }
                else
                {
                    beginHour = int.Parse(hourStr);
                }

                hourStr = hourGroup.Captures[1].Value;

                if (config.Numbers.ContainsKey(hourStr))
                {
                    endHour = config.Numbers[hourStr];
                }
                else
                {
                    endHour = int.Parse(hourStr);
                }

                var time1StartIndex = match.Groups["time1"].Index;
                var time1EndIndex   = time1StartIndex + match.Groups["time1"].Length;
                var time2StartIndex = match.Groups["time2"].Index;
                var time2EndIndex   = time2StartIndex + match.Groups["time2"].Length;

                // Get beginMinute (if exists) and endMinute (if exists)
                for (int i = 0; i < match.Groups[Constants.MinuteGroupName].Captures.Count; i++)
                {
                    var minuteCapture = match.Groups[Constants.MinuteGroupName].Captures[i];
                    if (minuteCapture.Index >= time1StartIndex && minuteCapture.Index + minuteCapture.Length <= time1EndIndex)
                    {
                        beginMinute = int.Parse(minuteCapture.Value);
                    }
                    else if (minuteCapture.Index >= time2StartIndex && minuteCapture.Index + minuteCapture.Length <= time2EndIndex)
                    {
                        endMinute = int.Parse(minuteCapture.Value);
                    }
                }

                // Get beginSecond (if exists) and endSecond (if exists)
                for (int i = 0; i < match.Groups[Constants.SecondGroupName].Captures.Count; i++)
                {
                    var secondCapture = match.Groups[Constants.SecondGroupName].Captures[i];
                    if (secondCapture.Index >= time1StartIndex && secondCapture.Index + secondCapture.Length <= time1EndIndex)
                    {
                        beginSecond = int.Parse(secondCapture.Value);
                    }
                    else if (secondCapture.Index >= time2StartIndex && secondCapture.Index + secondCapture.Length <= time2EndIndex)
                    {
                        endSecond = int.Parse(secondCapture.Value);
                    }
                }

                // Desc here means descriptions like "am / pm / o'clock"
                // Get leftDesc (if exists) and rightDesc (if exists)
                var leftDesc  = match.Groups["leftDesc"].Value;
                var rightDesc = match.Groups["rightDesc"].Value;

                for (int i = 0; i < match.Groups[Constants.DescGroupName].Captures.Count; i++)
                {
                    var descCapture = match.Groups[Constants.DescGroupName].Captures[i];
                    if (descCapture.Index >= time1StartIndex && descCapture.Index + descCapture.Length <= time1EndIndex && string.IsNullOrEmpty(leftDesc))
                    {
                        leftDesc = descCapture.Value;
                    }
                    else if (descCapture.Index >= time2StartIndex && descCapture.Index + descCapture.Length <= time2EndIndex && string.IsNullOrEmpty(rightDesc))
                    {
                        rightDesc = descCapture.Value;
                    }
                }

                var beginDateTime = DateObject.MinValue.SafeCreateFromValue(year, month, day, beginHour, beginMinute >= 0 ? beginMinute : 0, beginSecond >= 0 ? beginSecond : 0);
                var endDateTime   = DateObject.MinValue.SafeCreateFromValue(year, month, day, endHour, endMinute >= 0 ? endMinute : 0, endSecond >= 0 ? endSecond : 0);

                var hasLeftAm  = !string.IsNullOrEmpty(leftDesc) && leftDesc.ToLower().StartsWith("a");
                var hasLeftPm  = !string.IsNullOrEmpty(leftDesc) && leftDesc.ToLower().StartsWith("p");
                var hasRightAm = !string.IsNullOrEmpty(rightDesc) && rightDesc.ToLower().StartsWith("a");
                var hasRightPm = !string.IsNullOrEmpty(rightDesc) && rightDesc.ToLower().StartsWith("p");
                var hasLeft    = hasLeftAm || hasLeftPm;
                var hasRight   = hasRightAm || hasRightPm;

                // Both time point has description like 'am' or 'pm'
                if (hasLeft && hasRight)
                {
                    if (hasLeftAm)
                    {
                        if (beginHour >= Constants.HalfDayHourCount)
                        {
                            beginDateTime = beginDateTime.AddHours(-Constants.HalfDayHourCount);
                        }
                    }
                    else
                    {
                        if (beginHour < Constants.HalfDayHourCount)
                        {
                            beginDateTime = beginDateTime.AddHours(Constants.HalfDayHourCount);
                        }
                    }

                    if (hasRightAm)
                    {
                        if (endHour > Constants.HalfDayHourCount)
                        {
                            endDateTime = endDateTime.AddHours(-Constants.HalfDayHourCount);
                        }
                    }
                    else
                    {
                        if (endHour < Constants.HalfDayHourCount)
                        {
                            endDateTime = endDateTime.AddHours(Constants.HalfDayHourCount);
                        }
                    }
                }
                else if (hasLeft || hasRight)
                {
                    // one of the time point has description like 'am' or 'pm'
                    if (hasLeftAm)
                    {
                        if (beginHour >= Constants.HalfDayHourCount)
                        {
                            beginDateTime = beginDateTime.AddHours(-Constants.HalfDayHourCount);
                        }

                        if (endHour < Constants.HalfDayHourCount)
                        {
                            if (endDateTime < beginDateTime)
                            {
                                endDateTime = endDateTime.AddHours(Constants.HalfDayHourCount);
                            }
                        }
                    }
                    else if (hasLeftPm)
                    {
                        if (beginHour < Constants.HalfDayHourCount)
                        {
                            beginDateTime = beginDateTime.AddHours(Constants.HalfDayHourCount);
                        }

                        if (endHour < Constants.HalfDayHourCount)
                        {
                            if (endDateTime < beginDateTime)
                            {
                                var span = beginDateTime - endDateTime;
                                endDateTime = endDateTime.AddHours(span.TotalHours >= Constants.HalfDayHourCount ?
                                                                   24 :
                                                                   Constants.HalfDayHourCount);
                            }
                        }
                    }

                    if (hasRightAm)
                    {
                        if (endHour >= Constants.HalfDayHourCount)
                        {
                            endDateTime = endDateTime.AddHours(-Constants.HalfDayHourCount);
                        }

                        if (beginHour < Constants.HalfDayHourCount)
                        {
                            if (endDateTime < beginDateTime)
                            {
                                beginDateTime = beginDateTime.AddHours(-Constants.HalfDayHourCount);
                            }
                        }
                    }
                    else if (hasRightPm)
                    {
                        if (endHour < Constants.HalfDayHourCount)
                        {
                            endDateTime = endDateTime.AddHours(Constants.HalfDayHourCount);
                        }

                        if (beginHour < Constants.HalfDayHourCount)
                        {
                            if (endDateTime < beginDateTime)
                            {
                                beginDateTime = beginDateTime.AddHours(-Constants.HalfDayHourCount);
                            }
                            else
                            {
                                var span = endDateTime - beginDateTime;
                                if (span.TotalHours > Constants.HalfDayHourCount)
                                {
                                    beginDateTime = beginDateTime.AddHours(Constants.HalfDayHourCount);
                                }
                            }
                        }
                    }
                }

                // No 'am' or 'pm' indicator
                else if (beginHour <= Constants.HalfDayHourCount && endHour <= Constants.HalfDayHourCount)
                {
                    if (beginHour > endHour)
                    {
                        if (beginHour == Constants.HalfDayHourCount)
                        {
                            beginDateTime = beginDateTime.AddHours(-Constants.HalfDayHourCount);
                        }
                        else
                        {
                            endDateTime = endDateTime.AddHours(Constants.HalfDayHourCount);
                        }
                    }

                    ret.Comment = Constants.Comment_AmPm;
                }

                if (endDateTime < beginDateTime)
                {
                    endDateTime = endDateTime.AddHours(24);
                }

                var beginStr = DateTimeFormatUtil.ShortTime(beginDateTime.Hour, beginMinute, beginSecond);
                var endStr   = DateTimeFormatUtil.ShortTime(endDateTime.Hour, endMinute, endSecond);

                ret.Success = true;

                ret.Timex = $"({beginStr},{endStr},{DateTimeFormatUtil.LuisTimeSpan(endDateTime - beginDateTime)})";

                ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(
                    beginDateTime,
                    endDateTime);

                ret.SubDateTimeEntities = new List <object>();

                // In SplitDateAndTime mode, time points will be get from these SubDateTimeEntities
                // Cases like "from 4 to 5pm", "4" should not be treated as SubDateTimeEntity
                if (hasLeft || beginMinute != Constants.InvalidMinute || beginSecond != Constants.InvalidSecond)
                {
                    var er = new ExtractResult()
                    {
                        Start  = time1StartIndex,
                        Length = time1EndIndex - time1StartIndex,
                        Text   = text.Substring(time1StartIndex, time1EndIndex - time1StartIndex),
                        Type   = $"{Constants.SYS_DATETIME_TIME}",
                    };

                    var pr = this.config.TimeParser.Parse(er, referenceTime);
                    ret.SubDateTimeEntities.Add(pr);
                }

                // Cases like "from 4am to 5", "5" should not be treated as SubDateTimeEntity
                if (hasRight || endMinute != Constants.InvalidMinute || endSecond != Constants.InvalidSecond)
                {
                    var er = new ExtractResult
                    {
                        Start  = time2StartIndex,
                        Length = time2EndIndex - time2StartIndex,
                        Text   = text.Substring(time2StartIndex, time2EndIndex - time2StartIndex),
                        Type   = $"{Constants.SYS_DATETIME_TIME}",
                    };

                    var pr = this.config.TimeParser.Parse(er, referenceTime);
                    ret.SubDateTimeEntities.Add(pr);
                }

                ret.Success = true;
            }

            return(ret);
        }
        // Merge a Date entity and a Time entity
        private DateTimeResolutionResult MergeDateAndTime(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();

            var er1 = this.config.DateExtractor.Extract(text, referenceTime);

            if (er1.Count == 0)
            {
                er1 = this.config.DateExtractor.Extract(this.config.TokenBeforeDate + text, referenceTime);
                if (er1.Count == 1)
                {
                    er1[0].Start -= this.config.TokenBeforeDate.Length;
                }
                else
                {
                    return(ret);
                }
            }
            else
            {
                // This is to understand if there is an ambiguous token in the text. For some languages (e.g. spanish),
                // the same word could mean different things (e.g a time in the day or an specific day).
                if (this.config.ContainsAmbiguousToken(text, er1[0].Text))
                {
                    return(ret);
                }
            }

            var er2 = this.config.TimeExtractor.Extract(text, referenceTime);

            if (er2.Count == 0)
            {
                // Here we filter out "morning, afternoon, night..." time entities
                er2 = this.config.TimeExtractor.Extract(this.config.TokenBeforeTime + text, referenceTime);
                if (er2.Count == 1)
                {
                    er2[0].Start -= this.config.TokenBeforeTime.Length;
                }
                else if (er2.Count == 0)
                {
                    // check whether there is a number being used as a time point
                    bool hasTimeNumber = false;
                    var  numErs        = this.config.IntegerExtractor.Extract(text);
                    if (numErs.Count > 0 && er1.Count == 1)
                    {
                        foreach (var num in numErs)
                        {
                            var middleBegin = er1[0].Start + er1[0].Length ?? 0;
                            var middleEnd   = num.Start ?? 0;
                            if (middleBegin > middleEnd)
                            {
                                continue;
                            }

                            var middleStr = text.Substring(middleBegin, middleEnd - middleBegin).Trim();
                            var match     = this.config.DateNumberConnectorRegex.Match(middleStr);
                            if (string.IsNullOrEmpty(middleStr) || match.Success)
                            {
                                num.Type = Constants.SYS_DATETIME_TIME;
                                er2.Add(num);
                                hasTimeNumber = true;
                            }
                        }
                    }

                    if (!hasTimeNumber)
                    {
                        return(ret);
                    }
                }
            }

            // Handle cases like "Oct. 5 in the afternoon at 7:00";
            // in this case "5 in the afternoon" will be extracted as a Time entity
            var correctTimeIdx = 0;

            while (correctTimeIdx < er2.Count && er2[correctTimeIdx].IsOverlap(er1[0]))
            {
                correctTimeIdx++;
            }

            if (correctTimeIdx >= er2.Count)
            {
                return(ret);
            }

            var pr1 = this.config.DateParser.Parse(er1[0], referenceTime.Date);
            var pr2 = this.config.TimeParser.Parse(er2[correctTimeIdx], referenceTime);

            if (pr1.Value == null || pr2.Value == null)
            {
                return(ret);
            }

            var futureDate = (DateObject)((DateTimeResolutionResult)pr1.Value).FutureValue;
            var pastDate   = (DateObject)((DateTimeResolutionResult)pr1.Value).PastValue;
            var time       = (DateObject)((DateTimeResolutionResult)pr2.Value).FutureValue;

            var hour = time.Hour;
            var min  = time.Minute;
            var sec  = time.Second;

            // Handle morning, afternoon
            if (this.config.PMTimeRegex.IsMatch(text) && WithinAfternoonHours(hour))
            {
                hour += Constants.HalfDayHourCount;
            }
            else if (this.config.AMTimeRegex.IsMatch(text) && WithinMorningHoursAndNoon(hour, min, sec))
            {
                hour -= Constants.HalfDayHourCount;
            }

            var timeStr = pr2.TimexStr;

            if (timeStr.EndsWith(Constants.Comment_AmPm, StringComparison.Ordinal))
            {
                timeStr = timeStr.Substring(0, timeStr.Length - 4);
            }

            timeStr   = "T" + hour.ToString("D2") + timeStr.Substring(3);
            ret.Timex = pr1.TimexStr + timeStr;

            var val = (DateTimeResolutionResult)pr2.Value;

            if (hour <= Constants.HalfDayHourCount && !this.config.PMTimeRegex.IsMatch(text) && !this.config.AMTimeRegex.IsMatch(text) &&
                !string.IsNullOrEmpty(val.Comment))
            {
                ret.Comment = Constants.Comment_AmPm;
            }

            ret.FutureValue = DateObject.MinValue.SafeCreateFromValue(futureDate.Year, futureDate.Month, futureDate.Day, hour, min, sec);
            ret.PastValue   = DateObject.MinValue.SafeCreateFromValue(pastDate.Year, pastDate.Month, pastDate.Day, hour, min, sec);

            // Handle case like "Wed Oct 26 15:50:06 2016" which year and month separated by time.
            var timeSuffix = text.Substring(er2[0].Start + er2[0].Length ?? 0);
            var matchYear  = this.config.YearRegex.Match(timeSuffix);

            if (matchYear.Success && ((DateObject)((DateTimeResolutionResult)pr1.Value).FutureValue).Year != ((DateObject)((DateTimeResolutionResult)pr1.Value).PastValue).Year)
            {
                var year       = ((BaseDateExtractor)this.config.DateExtractor).GetYearFromText(matchYear);
                var dateSuffix = text.Substring(er1[0].Start + er1[0].Length ?? 0);
                var checkYear  = this.config.DateExtractor.GetYearFromText(this.config.YearRegex.Match(dateSuffix));

                if (year >= Constants.MinYearNum && year <= Constants.MaxYearNum && year == checkYear)
                {
                    ret.FutureValue = DateObject.MinValue.SafeCreateFromValue(year, futureDate.Month, futureDate.Day, hour, min, sec);
                    ret.PastValue   = DateObject.MinValue.SafeCreateFromValue(year, pastDate.Month, pastDate.Day, hour, min, sec);
                    ret.Timex       = year + pr1.TimexStr.Substring(4) + timeStr;
                }
            }

            ret.Success = true;

            // Change the value of time object
            pr2.TimexStr = timeStr;
            if (!string.IsNullOrEmpty(ret.Comment))
            {
                ((DateTimeResolutionResult)pr2.Value).Comment = ret.Comment.Equals(Constants.Comment_AmPm, StringComparison.Ordinal) ?
                                                                Constants.Comment_AmPm : string.Empty;
            }

            // Add the date and time object in case we want to split them
            ret.SubDateTimeEntities = new List <object> {
                pr1, pr2
            };

            // Add timezone
            ret.TimeZoneResolution = ((DateTimeResolutionResult)pr2.Value).TimeZoneResolution;

            return(ret);
        }
        private DateTimeResolutionResult ParseTimeOfToday(string text, DateObject referenceTime)
        {
            var ret         = new DateTimeResolutionResult();
            var trimmedText = text.Trim();

            int    hour = 0, min = 0, sec = 0;
            string timeStr;

            var wholeMatch = this.config.SimpleTimeOfTodayAfterRegex.MatchExact(trimmedText, trim: true);

            if (!wholeMatch.Success)
            {
                wholeMatch = this.config.SimpleTimeOfTodayBeforeRegex.MatchExact(trimmedText, trim: true);
            }

            if (wholeMatch.Success)
            {
                var hourStr = wholeMatch.Groups[Constants.HourGroupName].Value;
                if (string.IsNullOrEmpty(hourStr))
                {
                    hourStr = wholeMatch.Groups["hournum"].Value;
                    hour    = this.config.Numbers[hourStr];
                }
                else
                {
                    hour = int.Parse(hourStr);
                }

                timeStr = "T" + hour.ToString("D2");
            }
            else
            {
                var ers = this.config.TimeExtractor.Extract(trimmedText, referenceTime);
                if (ers.Count != 1)
                {
                    ers = this.config.TimeExtractor.Extract(this.config.TokenBeforeTime + trimmedText, referenceTime);
                    if (ers.Count == 1)
                    {
                        ers[0].Start -= this.config.TokenBeforeTime.Length;
                    }
                    else
                    {
                        return(ret);
                    }
                }

                var pr = this.config.TimeParser.Parse(ers[0], referenceTime);
                if (pr.Value == null)
                {
                    return(ret);
                }

                var time = (DateObject)((DateTimeResolutionResult)pr.Value).FutureValue;

                hour    = time.Hour;
                min     = time.Minute;
                sec     = time.Second;
                timeStr = pr.TimexStr;
            }

            var match = this.config.SpecificTimeOfDayRegex.Match(trimmedText);

            if (match.Success)
            {
                var matchStr = match.Value;

                // Handle "last", "next"
                var swift = this.config.GetSwiftDay(matchStr);

                var date = referenceTime.AddDays(swift).Date;

                // Handle "morning", "afternoon"
                hour = this.config.GetHour(matchStr, hour);

                // In this situation, timeStr cannot end up with "ampm", because we always have a "morning" or "night"
                if (timeStr.EndsWith(Constants.Comment_AmPm, StringComparison.Ordinal))
                {
                    timeStr = timeStr.Substring(0, timeStr.Length - 4);
                }

                timeStr = "T" + hour.ToString("D2") + timeStr.Substring(3);

                ret.Timex       = DateTimeFormatUtil.FormatDate(date) + timeStr;
                ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(date.Year, date.Month, date.Day, hour, min, sec);
                ret.Success     = true;
                return(ret);
            }

            return(ret);
        }
Example #12
0
        private DateTimeResolutionResult ParseWeekdayOfMonth(string text, DateObject referenceDate)
        {
            var ret = new DateTimeResolutionResult();

            var trimmedText = text.Trim();
            var match       = this.config.WeekDayOfMonthRegex.Match(trimmedText);

            if (!match.Success)
            {
                return(ret);
            }

            var cardinalStr = match.Groups["cardinal"].Value;
            var weekdayStr  = match.Groups["weekday"].Value;
            var monthStr    = match.Groups["month"].Value;
            var noYear      = false;
            int year;

            var cardinal = this.config.IsCardinalLast(cardinalStr) ? 5 : this.config.CardinalMap[cardinalStr];

            var weekday = this.config.DayOfWeek[weekdayStr];
            int month;

            if (string.IsNullOrEmpty(monthStr))
            {
                var swift = this.config.GetSwiftMonthOrYear(trimmedText);

                month = referenceDate.AddMonths(swift).Month;
                year  = referenceDate.AddMonths(swift).Year;
            }
            else
            {
                month  = this.config.MonthOfYear[monthStr];
                year   = referenceDate.Year;
                noYear = true;
            }

            var value = ComputeDate(cardinal, weekday, month, year);

            if (value.Month != month)
            {
                cardinal -= 1;
                value     = value.AddDays(-7);
            }

            var futureDate = value;
            var pastDate   = value;

            if (noYear && futureDate < referenceDate)
            {
                futureDate = ComputeDate(cardinal, weekday, month, year + 1);
                if (futureDate.Month != month)
                {
                    futureDate = futureDate.AddDays(-7);
                }
            }

            if (noYear && pastDate >= referenceDate)
            {
                pastDate = ComputeDate(cardinal, weekday, month, year - 1);
                if (pastDate.Month != month)
                {
                    pastDate = pastDate.AddDays(-7);
                }
            }

            // Here is a very special case, timeX follow future date
            ret.Timex       = $@"XXXX-{month.ToString("D2")}-WXX-{weekday}-#{cardinal}";
            ret.FutureValue = futureDate;
            ret.PastValue   = pastDate;
            ret.Success     = true;

            return(ret);
        }
Example #13
0
        private DateTimeResolutionResult Match2Time(Match match, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();
            int hour = 0,
                min = 0,
                second = 0,
                day = referenceTime.Day,
                month = referenceTime.Month,
                year = referenceTime.Year;
            bool hasMin = false, hasSec = false, hasAm = false, hasPm = false;

            var engTimeStr = match.Groups["engtime"].Value;

            if (!string.IsNullOrEmpty(engTimeStr))
            {
                // get hour
                var hourStr = match.Groups["hournum"].Value.ToLower();
                hour = this.config.Numbers[hourStr];

                // get minute
                var minStr  = match.Groups["minnum"].Value;
                var tensStr = match.Groups["tens"].Value;

                if (!string.IsNullOrEmpty(minStr))
                {
                    min = this.config.Numbers[minStr];
                    if (!string.IsNullOrEmpty(tensStr))
                    {
                        min += this.config.Numbers[tensStr];
                    }
                    hasMin = true;
                }
            }
            else
            {
                // get hour
                var hourStr = match.Groups["hour"].Value;
                if (string.IsNullOrEmpty(hourStr))
                {
                    hourStr = match.Groups["hournum"].Value.ToLower();
                    if (!this.config.Numbers.TryGetValue(hourStr, out hour))
                    {
                        return(ret);
                    }
                }
                else
                {
                    hour = int.Parse(hourStr);
                }

                // get minute
                var minStr = match.Groups["min"].Value.ToLower();
                if (string.IsNullOrEmpty(minStr))
                {
                    minStr = match.Groups["minnum"].Value;
                    if (!string.IsNullOrEmpty(minStr))
                    {
                        min    = this.config.Numbers[minStr];
                        hasMin = true;
                    }

                    var tensStr = match.Groups["tens"].Value;
                    if (!string.IsNullOrEmpty(tensStr))
                    {
                        min   += this.config.Numbers[tensStr];
                        hasMin = true;
                    }
                }
                else
                {
                    min    = int.Parse(minStr);
                    hasMin = true;
                }

                // get second
                var secStr = match.Groups["sec"].Value.ToLower();
                if (!string.IsNullOrEmpty(secStr))
                {
                    second = int.Parse(secStr);
                    hasSec = true;
                }
            }

            //adjust by desc string
            var descStr = match.Groups["desc"].Value.ToLower();

            if (!string.IsNullOrEmpty(descStr))
            {
                //ampm is a special case in which at 6ampm = at 6
                if (config.UtilityConfiguration.AmDescRegex.Match(descStr.ToLower()).Success ||
                    config.UtilityConfiguration.AmPmDescRegex.Match(descStr.ToLower()).Success)
                {
                    if (hour >= 12)
                    {
                        hour -= 12;
                    }
                    if (!config.UtilityConfiguration.AmPmDescRegex.Match(descStr.ToLower()).Success)
                    {
                        hasAm = true;
                    }
                }
                else if (config.UtilityConfiguration.PmDescRegex.Match(descStr.ToLower()).Success)
                {
                    if (hour < 12)
                    {
                        hour += 12;
                    }
                    hasPm = true;
                }
            }

            // adjust min by prefix
            var timePrefix = match.Groups["prefix"].Value.ToLower();

            if (!string.IsNullOrEmpty(timePrefix))
            {
                this.config.AdjustByPrefix(timePrefix, ref hour, ref min, ref hasMin);
            }

            // adjust hour by suffix
            var timeSuffix = match.Groups["suffix"].Value.ToLower();

            if (!string.IsNullOrEmpty(timeSuffix))
            {
                this.config.AdjustBySuffix(timeSuffix, ref hour, ref min, ref hasMin, ref hasAm, ref hasPm);
            }

            if (hour == 24)
            {
                hour = 0;
            }

            ret.Timex = "T" + hour.ToString("D2");
            if (hasMin)
            {
                ret.Timex += ":" + min.ToString("D2");
            }

            if (hasSec)
            {
                ret.Timex += ":" + second.ToString("D2");
            }

            if (hour <= 12 && !hasPm && !hasAm)
            {
                ret.Comment = "ampm";
            }

            ret.FutureValue = ret.PastValue = new DateObject(year, month, day, hour, min, second);
            ret.Success     = true;

            return(ret);
        }
        private DateTimeResolutionResult Match2Date(Match match, DateObject referenceDate)
        {
            var ret        = new DateTimeResolutionResult();
            var holidayStr = match.Groups["holiday"].Value;

            var year    = referenceDate.Year;
            var hasYear = false;
            var yearNum = match.Groups["year"].Value;
            var yearCJK = match.Groups[Constants.YearCJKGroupName].Value;
            var yearRel = match.Groups["yearrel"].Value;

            if (!string.IsNullOrEmpty(yearNum))
            {
                hasYear = true;
                yearNum = this.config.SanitizeYearToken(yearNum);

                year = int.Parse(yearNum, CultureInfo.InvariantCulture);
            }
            else if (!string.IsNullOrEmpty(yearCJK))
            {
                hasYear = true;
                yearCJK = this.config.SanitizeYearToken(yearCJK);

                year = ConvertToInteger(yearCJK);
            }
            else if (!string.IsNullOrEmpty(yearRel))
            {
                hasYear = true;
                int swift = this.config.GetSwiftYear(yearRel);
                if (swift >= -1)
                {
                    year += swift;
                }
            }

            if (year < 100 && year >= 90)
            {
                year += Constants.BASE_YEAR_PAST_CENTURY;
            }
            else if (year < 20)
            {
                year += Constants.BASE_YEAR_CURRENT_CENTURY;
            }

            if (!string.IsNullOrEmpty(holidayStr))
            {
                DateObject value;
                string     timexStr;
                if (this.config.FixedHolidaysDict.ContainsKey(holidayStr))
                {
                    value    = this.config.FixedHolidaysDict[holidayStr](year);
                    timexStr = $"-{value.Month:D2}-{value.Day:D2}";
                }
                else
                {
                    if (this.config.HolidayFuncDict.ContainsKey(holidayStr))
                    {
                        value    = this.config.HolidayFuncDict[holidayStr](year);
                        timexStr = this.config.NoFixedTimex[holidayStr];
                    }
                    else
                    {
                        return(ret);
                    }
                }

                if (hasYear)
                {
                    ret.Timex       = year.ToString("D4", CultureInfo.InvariantCulture) + timexStr;
                    ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(year, value.Month, value.Day);
                    ret.Success     = true;
                    return(ret);
                }

                ret.Timex       = "XXXX" + timexStr;
                ret.FutureValue = GetFutureValue(value, referenceDate, holidayStr);
                ret.PastValue   = GetPastValue(value, referenceDate, holidayStr);
                ret.Success     = true;
                return(ret);
            }

            return(ret);
        }
        // merge the entity with its related contexts and then parse the combine text
        private static DateTimeResolutionResult GetResolution(ExtractResult er, DateTimeParseResult pr, DateTimeResolutionResult ret)
        {
            var parentText = (string)((Dictionary <string, object>)er.Data)[ExtendedModelResult.ParentTextKey];
            var type       = pr.Type;

            var singlePointResolution      = string.Empty;
            var pastStartPointResolution   = string.Empty;
            var pastEndPointResolution     = string.Empty;
            var futureStartPointResolution = string.Empty;
            var futureEndPointResolution   = string.Empty;
            var singlePointType            = string.Empty;
            var startPointType             = string.Empty;
            var endPointType = string.Empty;

            if (type is Constants.SYS_DATETIME_DATEPERIOD or Constants.SYS_DATETIME_TIMEPERIOD or Constants.SYS_DATETIME_DATETIMEPERIOD)
            {
                switch (type)
                {
                case Constants.SYS_DATETIME_DATEPERIOD:
                    startPointType             = TimeTypeConstants.START_DATE;
                    endPointType               = TimeTypeConstants.END_DATE;
                    pastStartPointResolution   = DateTimeFormatUtil.FormatDate(((Tuple <DateObject, DateObject>)ret.PastValue).Item1);
                    pastEndPointResolution     = DateTimeFormatUtil.FormatDate(((Tuple <DateObject, DateObject>)ret.PastValue).Item2);
                    futureStartPointResolution = DateTimeFormatUtil.FormatDate(((Tuple <DateObject, DateObject>)ret.FutureValue).Item1);
                    futureEndPointResolution   = DateTimeFormatUtil.FormatDate(((Tuple <DateObject, DateObject>)ret.FutureValue).Item2);
                    break;

                case Constants.SYS_DATETIME_DATETIMEPERIOD:
                    startPointType = TimeTypeConstants.START_DATETIME;
                    endPointType   = TimeTypeConstants.END_DATETIME;

                    if (ret.PastValue is Tuple <DateObject, DateObject> tuple)
                    {
                        pastStartPointResolution   = DateTimeFormatUtil.FormatDateTime(tuple.Item1);
                        pastEndPointResolution     = DateTimeFormatUtil.FormatDateTime(tuple.Item2);
                        futureStartPointResolution =
                            DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)ret.FutureValue).Item1);
                        futureEndPointResolution =
                            DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)ret.FutureValue).Item2);
                    }
                    else if (ret.PastValue is DateObject datetime)
                    {
                        pastStartPointResolution   = DateTimeFormatUtil.FormatDateTime(datetime);
                        futureStartPointResolution = DateTimeFormatUtil.FormatDateTime((DateObject)ret.FutureValue);
                    }

                    break;

                case Constants.SYS_DATETIME_TIMEPERIOD:
                    startPointType             = TimeTypeConstants.START_TIME;
                    endPointType               = TimeTypeConstants.END_TIME;
                    pastStartPointResolution   = DateTimeFormatUtil.FormatTime(((Tuple <DateObject, DateObject>)ret.PastValue).Item1);
                    pastEndPointResolution     = DateTimeFormatUtil.FormatTime(((Tuple <DateObject, DateObject>)ret.PastValue).Item2);
                    futureStartPointResolution = DateTimeFormatUtil.FormatTime(((Tuple <DateObject, DateObject>)ret.FutureValue).Item1);
                    futureEndPointResolution   = DateTimeFormatUtil.FormatTime(((Tuple <DateObject, DateObject>)ret.FutureValue).Item2);
                    break;
                }
            }
        // match several other cases
        // including '今倩', '后倩', '十三ζ—₯'
        protected DateTimeResolutionResult ParseImplicitDate(string text, DateObject referenceDate)
        {
            var ret = new DateTimeResolutionResult();

            // handle "十二ζ—₯" "ζ˜ŽεΉ΄θΏ™δΈͺζœˆδΈ‰ζ—₯" "ζœ¬ζœˆεδΈ€ζ—₯"
            var match = this.config.SpecialDate.MatchExact(text, trim: true);

            if (match.Success)
            {
                var yearStr  = match.Groups["thisyear"].Value;
                var monthStr = match.Groups["thismonth"].Value;
                var dayStr   = match.Groups["day"].Value;

                int month = referenceDate.Month, year = referenceDate.Year;
                var day = this.config.DayOfMonth[dayStr];

                bool hasYear = false, hasMonth = false;

                if (!string.IsNullOrEmpty(monthStr))
                {
                    hasMonth = true;
                    if (this.config.NextRe.Match(monthStr).Success)
                    {
                        month++;
                        if (month == Constants.MaxMonth + 1)
                        {
                            month = Constants.MinMonth;
                            year++;
                        }
                    }
                    else if (this.config.LastRe.Match(monthStr).Success)
                    {
                        month--;
                        if (month == Constants.MinMonth - 1)
                        {
                            month = Constants.MaxMonth;
                            year--;
                        }
                    }

                    if (!string.IsNullOrEmpty(yearStr))
                    {
                        hasYear = true;
                        if (this.config.NextRe.Match(yearStr).Success)
                        {
                            ++year;
                        }
                        else if (this.config.LastRe.Match(yearStr).Success)
                        {
                            --year;
                        }
                    }
                }

                ret.Timex = DateTimeFormatUtil.LuisDate(hasYear ? year : -1, hasMonth ? month : -1, day);

                DateObject futureDate, pastDate;

                if (day > GetMonthMaxDay(year, month))
                {
                    var futureMonth = month + 1;
                    var pastMonth   = month - 1;
                    var futureYear  = year;
                    var pastYear    = year;

                    if (futureMonth == Constants.MaxMonth + 1)
                    {
                        futureMonth = Constants.MinMonth;
                        futureYear  = year++;
                    }

                    if (pastMonth == Constants.MinMonth - 1)
                    {
                        pastMonth = Constants.MaxMonth;
                        pastYear  = year--;
                    }

                    var isFutureValid = DateObjectExtension.IsValidDate(futureYear, futureMonth, day);
                    var isPastValid   = DateObjectExtension.IsValidDate(pastYear, pastMonth, day);

                    if (isFutureValid && isPastValid)
                    {
                        futureDate = DateObject.MinValue.SafeCreateFromValue(futureYear, futureMonth, day);
                        pastDate   = DateObject.MinValue.SafeCreateFromValue(pastYear, pastMonth, day);
                    }
                    else if (isFutureValid && !isPastValid)
                    {
                        futureDate = pastDate = DateObject.MinValue.SafeCreateFromValue(futureYear, futureMonth, day);
                    }
                    else if (!isFutureValid && !isPastValid)
                    {
                        futureDate = pastDate = DateObject.MinValue.SafeCreateFromValue(pastYear, pastMonth, day);
                    }
                    else
                    {
                        // Fall back to normal cases, might lead to resolution failure
                        // TODO: Ideally, this failure should be filtered out in extract phase
                        futureDate = pastDate = DateObject.MinValue.SafeCreateFromValue(year, month, day);
                    }
                }
                else
                {
                    futureDate = DateObject.MinValue.SafeCreateFromValue(year, month, day);
                    pastDate   = DateObject.MinValue.SafeCreateFromValue(year, month, day);

                    if (!hasMonth)
                    {
                        if (futureDate < referenceDate)
                        {
                            if (IsValidDate(year, month + 1, day))
                            {
                                futureDate = futureDate.AddMonths(1);
                            }
                        }

                        if (pastDate >= referenceDate)
                        {
                            if (IsValidDate(year, month - 1, day))
                            {
                                pastDate = pastDate.AddMonths(-1);
                            }
                            else if (DateContext.IsFeb29th(year, month - 1, day))
                            {
                                pastDate = pastDate.AddMonths(-2);
                            }
                        }
                    }
                    else if (!hasYear)
                    {
                        if (futureDate < referenceDate)
                        {
                            if (IsValidDate(year + 1, month, day))
                            {
                                futureDate = futureDate.AddYears(1);
                            }
                        }

                        if (pastDate >= referenceDate)
                        {
                            if (IsValidDate(year - 1, month, day))
                            {
                                pastDate = pastDate.AddYears(-1);
                            }
                        }
                    }
                }

                ret.FutureValue = futureDate;
                ret.PastValue   = pastDate;
                ret.Success     = true;

                return(ret);
            }

            // handle cases like "昨ζ—₯", "明ζ—₯", "倧后倩"
            match = this.config.SpecialDayRegex.MatchExact(text, trim: true);

            if (match.Success)
            {
                var value = referenceDate.AddDays(this.config.GetSwiftDay(match.Value));
                ret.Timex       = DateTimeFormatUtil.LuisDate(value);
                ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(value.Year, value.Month, value.Day);
                ret.Success     = true;

                return(ret);
            }

            if (!ret.Success)
            {
                ret = MatchThisWeekday(text, referenceDate);
            }

            if (!ret.Success)
            {
                ret = MatchNextWeekday(text, referenceDate);
            }

            if (!ret.Success)
            {
                ret = MatchLastWeekday(text, referenceDate);
            }

            if (!ret.Success)
            {
                ret = MatchWeekdayAlone(text, referenceDate);
            }

            return(ret);
        }
Example #17
0
        private DateTimeResolutionResult ParseMergedDuration(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();
            var durationExtractor = this.config.DurationExtractor;

            // DurationExtractor without parameter will not extract merged duration
            var ers = durationExtractor.Extract(text, referenceTime);

            // only handle merged duration cases like "1 month 21 days"
            if (ers.Count <= 1)
            {
                ret.Success = false;
                return(ret);
            }

            var start = ers[0].Start ?? 0;

            if (start != 0)
            {
                var beforeStr = text.Substring(0, start - 1);
                if (!string.IsNullOrWhiteSpace(beforeStr))
                {
                    return(ret);
                }
            }

            var end = ers[ers.Count - 1].Start + ers[ers.Count - 1].Length ?? 0;

            if (end != text.Length)
            {
                var afterStr = text.Substring(end);
                if (!string.IsNullOrWhiteSpace(afterStr))
                {
                    return(ret);
                }
            }

            var prs       = new List <DateTimeParseResult>();
            var timexDict = new Dictionary <string, string>();

            // insert timex into a dictionary
            foreach (var er in ers)
            {
                var unitRegex = this.config.DurationUnitRegex;
                var unitMatch = unitRegex.Match(er.Text);
                if (unitMatch.Success)
                {
                    var pr = (DateTimeParseResult)Parse(er);
                    if (pr.Value != null)
                    {
                        timexDict.Add(this.config.UnitMap[unitMatch.Groups["unit"].Value], pr.TimexStr);
                        prs.Add(pr);
                    }
                }
            }

            // sort the timex using the granularity of the duration, "P1M23D" for "1 month 23 days" and "23 days 1 month"
            if (prs.Count == ers.Count)
            {
                ret.Timex = TimexUtility.GenerateCompoundDurationTimex(timexDict, this.config.UnitValueMap);

                double value = 0;
                foreach (var pr in prs)
                {
                    value += double.Parse(((DateTimeResolutionResult)pr.Value).FutureValue.ToString(), CultureInfo.InvariantCulture);
                }

                ret.FutureValue = ret.PastValue = value;
            }

            ret.Success = true;
            return(ret);
        }
        protected virtual DateTimeResolutionResult ParseWeekdayOfMonth(string text, DateObject referenceDate)
        {
            var ret = new DateTimeResolutionResult();

            var trimmedText = text.Trim();
            var match       = this.config.WeekDayOfMonthRegex.Match(trimmedText);

            if (!match.Success)
            {
                return(ret);
            }

            var cardinalStr = match.Groups["cardinal"].Value;
            var weekdayStr  = match.Groups["weekday"].Value;
            var monthStr    = match.Groups["month"].Value;
            var noYear      = false;
            int year;

            int cardinal;

            if (cardinalStr.Equals(this.config.LastWeekDayToken, StringComparison.Ordinal))
            {
                cardinal = 5;
            }
            else
            {
                cardinal = this.config.CardinalMap[cardinalStr];
            }

            var weekday = this.config.DayOfWeek[weekdayStr];
            int month;

            if (string.IsNullOrEmpty(monthStr))
            {
                var swift = 0;
                if (trimmedText.StartsWith(this.config.NextMonthToken, StringComparison.Ordinal))
                {
                    swift = 1;
                }
                else if (trimmedText.StartsWith(this.config.LastMonthToken, StringComparison.Ordinal))
                {
                    swift = -1;
                }

                month = referenceDate.AddMonths(swift).Month;
                year  = referenceDate.AddMonths(swift).Year;
            }
            else
            {
                month  = this.config.MonthOfYear[monthStr];
                year   = referenceDate.Year;
                noYear = true;
            }

            var value = ComputeDate(cardinal, weekday, month, year);

            if (value.Month != month)
            {
                cardinal -= 1;
                value     = value.AddDays(-7);
            }

            var futureDate = value;
            var pastDate   = value;

            if (noYear && futureDate < referenceDate)
            {
                futureDate = ComputeDate(cardinal, weekday, month, year + 1);
                if (futureDate.Month != month)
                {
                    futureDate = futureDate.AddDays(-7);
                }
            }

            if (noYear && pastDate >= referenceDate)
            {
                pastDate = ComputeDate(cardinal, weekday, month, year - 1);
                if (pastDate.Month != month)
                {
                    pastDate = pastDate.AddDays(-7);
                }
            }

            // here is a very special case, timeX follows future date
            ret.Timex       = $@"XXXX-{month:D2}-WXX-{weekday}-#{cardinal}";
            ret.FutureValue = futureDate;
            ret.PastValue   = pastDate;
            ret.Success     = true;

            return(ret);
        }
Example #19
0
        // Parse specific TimeOfDay like "this night", "early morning", "late evening"
        protected virtual DateTimeResolutionResult ParseSpecificTimeOfDay(string text, DateObject referenceTime)
        {
            var ret         = new DateTimeResolutionResult();
            var trimmedText = text.Trim().ToLowerInvariant();
            var timeText    = trimmedText;

            var match = this.Config.PeriodTimeOfDayWithDateRegex.Match(trimmedText);

            // Extract early/late prefix from text if any
            bool hasEarly = false, hasLate = false;

            if (match.Success)
            {
                timeText = match.Groups[Constants.TimeOfDayGroupName].Value;

                if (!string.IsNullOrEmpty(match.Groups["early"].Value))
                {
                    hasEarly    = true;
                    ret.Comment = Constants.Comment_Early;
                }

                if (!hasEarly && !string.IsNullOrEmpty(match.Groups["late"].Value))
                {
                    hasLate     = true;
                    ret.Comment = Constants.Comment_Late;
                }
            }
            else
            {
                match = this.Config.AmDescRegex.Match(trimmedText);
                if (!match.Success)
                {
                    match = this.Config.PmDescRegex.Match(trimmedText);
                }

                if (match.Success)
                {
                    timeText = match.Value;
                }
            }

            // Handle time of day

            // Late/early only works with time of day
            // Only standard time of day (morning, afternoon, evening and night) will not directly return
            if (!this.Config.GetMatchedTimeRange(timeText, out string timeStr, out int beginHour, out int endHour, out int endMin))
            {
                return(ret);
            }

            // Modify time period if "early" or "late" exists
            // Since 'time of day' is defined as four hour periods
            // the first 2 hours represent early, the later 2 hours represent late
            if (hasEarly)
            {
                endHour = beginHour + 2;

                // Handling speical case: night ends with 23:59
                if (endMin == 59)
                {
                    endMin = 0;
                }
            }
            else if (hasLate)
            {
                beginHour = beginHour + 2;
            }

            if (Config.SpecificTimeOfDayRegex.IsExactMatch(trimmedText, trim: true))
            {
                var swift = this.Config.GetSwiftPrefix(trimmedText);

                var date = referenceTime.AddDays(swift).Date;
                int day = date.Day, month = date.Month, year = date.Year;

                ret.Timex = DateTimeFormatUtil.FormatDate(date) + timeStr;

                ret.FutureValue   =
                    ret.PastValue =
                        new Tuple <DateObject, DateObject>(
                            DateObject.MinValue.SafeCreateFromValue(year, month, day, beginHour, 0, 0),
                            DateObject.MinValue.SafeCreateFromValue(year, month, day, endHour, endMin, endMin));

                ret.Success = true;
                return(ret);
            }

            // Handle Date followed by morning, afternoon and morning, afternoon followed by Date
            match = this.Config.PeriodTimeOfDayWithDateRegex.Match(trimmedText);

            if (!match.Success)
            {
                match = this.Config.AmDescRegex.Match(trimmedText);
                if (!match.Success)
                {
                    match = this.Config.PmDescRegex.Match(trimmedText);
                }
            }

            if (match.Success)
            {
                var beforeStr = trimmedText.Substring(0, match.Index).Trim();
                var afterStr  = trimmedText.Substring(match.Index + match.Length).Trim();

                // Eliminate time period, if any
                var timePeriodErs = this.Config.TimePeriodExtractor.Extract(beforeStr);
                if (timePeriodErs.Count > 0)
                {
                    beforeStr = beforeStr.Remove(timePeriodErs[0].Start ?? 0, timePeriodErs[0].Length ?? 0).Trim();
                }
                else
                {
                    timePeriodErs = this.Config.TimePeriodExtractor.Extract(afterStr);
                    if (timePeriodErs.Count > 0)
                    {
                        afterStr = afterStr.Remove(timePeriodErs[0].Start ?? 0, timePeriodErs[0].Length ?? 0).Trim();
                    }
                }

                var ers = this.Config.DateExtractor.Extract(beforeStr + ' ' + afterStr, referenceTime);

                if (ers.Count == 0 || ers[0].Length < beforeStr.Length)
                {
                    var valid = false;

                    if (ers.Count > 0 && ers[0].Start == 0)
                    {
                        var midStr = beforeStr.Substring(ers[0].Start + ers[0].Length ?? 0);
                        if (string.IsNullOrWhiteSpace(midStr.Replace(',', ' ')))
                        {
                            valid = true;
                        }
                    }

                    if (!valid)
                    {
                        ers = this.Config.DateExtractor.Extract(afterStr, referenceTime);

                        if (ers.Count == 0 || ers[0].Length != afterStr.Length)
                        {
                            if (ers.Count > 0 && ers[0].Start + ers[0].Length == afterStr.Length)
                            {
                                var midStr = afterStr.Substring(0, ers[0].Start ?? 0);
                                if (string.IsNullOrWhiteSpace(midStr.Replace(',', ' ')))
                                {
                                    valid = true;
                                }
                            }
                        }
                        else
                        {
                            valid = true;
                        }
                    }

                    if (!valid)
                    {
                        return(ret);
                    }
                }

                var hasSpecificTimePeriod = false;
                if (timePeriodErs.Count > 0)
                {
                    var timePr = this.Config.TimePeriodParser.Parse(timePeriodErs[0], referenceTime);
                    if (timePr != null)
                    {
                        var periodFuture = (Tuple <DateObject, DateObject>)((DateTimeResolutionResult)timePr.Value).FutureValue;
                        var periodPast   = (Tuple <DateObject, DateObject>)((DateTimeResolutionResult)timePr.Value).PastValue;

                        if (periodFuture == periodPast)
                        {
                            beginHour = periodFuture.Item1.Hour;
                            endHour   = periodFuture.Item2.Hour;
                        }
                        else
                        {
                            if (periodFuture.Item1.Hour >= beginHour || periodFuture.Item2.Hour <= endHour)
                            {
                                beginHour = periodFuture.Item1.Hour;
                                endHour   = periodFuture.Item2.Hour;
                            }
                            else
                            {
                                beginHour = periodPast.Item1.Hour;
                                endHour   = periodPast.Item2.Hour;
                            }
                        }

                        hasSpecificTimePeriod = true;
                    }
                }

                var pr         = this.Config.DateParser.Parse(ers[0], referenceTime);
                var futureDate = (DateObject)((DateTimeResolutionResult)pr.Value).FutureValue;
                var pastDate   = (DateObject)((DateTimeResolutionResult)pr.Value).PastValue;

                if (!hasSpecificTimePeriod)
                {
                    ret.Timex = pr.TimexStr + timeStr;
                }
                else
                {
                    ret.Timex = string.Format("({0}T{1},{0}T{2},PT{3}H)", pr.TimexStr, beginHour, endHour, endHour - beginHour);
                }

                ret.FutureValue =
                    new Tuple <DateObject, DateObject>(
                        DateObject.MinValue.SafeCreateFromValue(futureDate.Year, futureDate.Month, futureDate.Day, beginHour, 0, 0),
                        DateObject.MinValue.SafeCreateFromValue(futureDate.Year, futureDate.Month, futureDate.Day, endHour, endMin, endMin));

                ret.PastValue =
                    new Tuple <DateObject, DateObject>(
                        DateObject.MinValue.SafeCreateFromValue(pastDate.Year, pastDate.Month, pastDate.Day, beginHour, 0, 0),
                        DateObject.MinValue.SafeCreateFromValue(pastDate.Year, pastDate.Month, pastDate.Day, endHour, endMin, endMin));

                ret.Success = true;

                return(ret);
            }

            return(ret);
        }
        // Handle cases like "三倩前"
        private DateTimeResolutionResult ParserDurationWithAgoAndLater(string text, DateObject referenceDate)
        {
            var ret     = new DateTimeResolutionResult();
            var numStr  = string.Empty;
            var unitStr = string.Empty;

            var durationRes = this.config.DurationExtractor.Extract(text, referenceDate);

            if (durationRes.Count > 0)
            {
                var match = this.config.UnitRegex.Match(text);
                if (match.Success)
                {
                    var suffix  = text.Substring((int)durationRes[0].Start + (int)durationRes[0].Length).Trim();
                    var srcUnit = match.Groups["unit"].Value;

                    var numberStr = text.Substring((int)durationRes[0].Start, match.Index - (int)durationRes[0].Start).Trim();
                    var number    = ConvertChineseToNum(numberStr);

                    if (this.config.UnitMap.ContainsKey(srcUnit))
                    {
                        unitStr = this.config.UnitMap[srcUnit];

                        var beforeMatch = this.config.BeforeRegex.Match(suffix);
                        if (beforeMatch.Success && suffix.StartsWith(beforeMatch.Value, StringComparison.Ordinal))
                        {
                            DateObject date;
                            switch (unitStr)
                            {
                            case Constants.TimexDay:
                                date = referenceDate.AddDays(-number);
                                break;

                            case Constants.TimexWeek:
                                date = referenceDate.AddDays(-7 * number);
                                break;

                            case Constants.TimexMonthFull:
                                date = referenceDate.AddMonths(-number);
                                break;

                            case Constants.TimexYear:
                                date = referenceDate.AddYears(-number);
                                break;

                            default:
                                return(ret);
                            }

                            ret.Timex       = $"{DateTimeFormatUtil.LuisDate(date)}";
                            ret.FutureValue = ret.PastValue = date;
                            ret.Success     = true;
                            return(ret);
                        }

                        var afterMatch = this.config.AfterRegex.Match(suffix);
                        if (afterMatch.Success && suffix.StartsWith(afterMatch.Value, StringComparison.Ordinal))
                        {
                            DateObject date;
                            switch (unitStr)
                            {
                            case Constants.TimexDay:
                                date = referenceDate.AddDays(number);
                                break;

                            case Constants.TimexWeek:
                                date = referenceDate.AddDays(7 * number);
                                break;

                            case Constants.TimexMonthFull:
                                date = referenceDate.AddMonths(number);
                                break;

                            case Constants.TimexYear:
                                date = referenceDate.AddYears(number);
                                break;

                            default:
                                return(ret);
                            }

                            ret.Timex       = $"{DateTimeFormatUtil.LuisDate(date)}";
                            ret.FutureValue = ret.PastValue = date;
                            ret.Success     = true;
                            return(ret);
                        }
                    }
                }
            }

            return(ret);
        }
Example #21
0
        // Handle cases like "Monday 7-9", where "7-9" can't be extracted by the TimePeriodExtractor
        private DateTimeResolutionResult ParsePureNumberCases(string text, DateObject referenceTime)
        {
            var ret         = new DateTimeResolutionResult();
            var trimmedText = text.Trim().ToLower();

            var match = this.Config.PureNumberFromToRegex.Match(trimmedText);

            if (!match.Success)
            {
                match = this.Config.PureNumberBetweenAndRegex.Match(trimmedText);
            }

            if (match.Success && (match.Index == 0 || match.Index + match.Length == trimmedText.Length))
            {
                int beginHour, endHour;
                ret.Comment = ParseTimePeriod(match, out beginHour, out endHour);

                var dateStr = string.Empty;

                // Parse following date
                var dateExtractResult = this.Config.DateExtractor.Extract(trimmedText.Replace(match.Value, string.Empty), referenceTime);

                DateObject futureDate, pastDate;
                if (dateExtractResult.Count > 0)
                {
                    var pr = this.Config.DateParser.Parse(dateExtractResult[0], referenceTime);
                    if (pr.Value != null)
                    {
                        futureDate = (DateObject)((DateTimeResolutionResult)pr.Value).FutureValue;
                        pastDate   = (DateObject)((DateTimeResolutionResult)pr.Value).PastValue;

                        dateStr = pr.TimexStr;
                    }
                    else
                    {
                        return(ret);
                    }
                }
                else
                {
                    return(ret);
                }

                var pastHours     = endHour - beginHour;
                var beginTimex    = TimexUtility.CombineDateAndTimeTimex(dateStr, DateTimeFormatUtil.ShortTime(beginHour));
                var endTimex      = TimexUtility.CombineDateAndTimeTimex(dateStr, DateTimeFormatUtil.ShortTime(endHour));
                var durationTimex = TimexUtility.GenerateDurationTimex(endHour - beginHour, Constants.TimexHour, isLessThanDay: true);

                ret.Timex = TimexUtility.GenerateDateTimePeriodTimex(beginTimex, endTimex, durationTimex);

                ret.FutureValue = new Tuple <DateObject, DateObject>(
                    DateObject.MinValue.SafeCreateFromValue(futureDate.Year, futureDate.Month, futureDate.Day, beginHour, 0, 0),
                    DateObject.MinValue.SafeCreateFromValue(futureDate.Year, futureDate.Month, futureDate.Day, endHour, 0, 0));

                ret.PastValue = new Tuple <DateObject, DateObject>(
                    DateObject.MinValue.SafeCreateFromValue(pastDate.Year, pastDate.Month, pastDate.Day, beginHour, 0, 0),
                    DateObject.MinValue.SafeCreateFromValue(pastDate.Year, pastDate.Month, pastDate.Day, endHour, 0, 0));

                ret.Success = true;
            }

            return(ret);
        }
Example #22
0
        private DateTimeResolutionResult Match2Date(Match match, DateObject referenceDate)
        {
            var ret        = new DateTimeResolutionResult();
            var holidayStr = this.config.SanitizeHolidayToken(match.Groups["holiday"].Value.ToLowerInvariant());

            // get year (if exist)
            var yearStr  = match.Groups["year"].Value.ToLower();
            var orderStr = match.Groups["order"].Value.ToLower();
            int year;
            var hasYear = false;

            if (!string.IsNullOrEmpty(yearStr))
            {
                year    = int.Parse(yearStr);
                hasYear = true;
            }
            else if (!string.IsNullOrEmpty(orderStr))
            {
                var swift = this.config.GetSwiftYear(orderStr);
                if (swift < -1)
                {
                    return(ret);
                }
                year    = referenceDate.Year + swift;
                hasYear = true;
            }
            else
            {
                year = referenceDate.Year;
            }

            string holidayKey = string.Empty;

            foreach (var holidayPair in this.config.HolidayNames)
            {
                if (holidayPair.Value.Contains(holidayStr))
                {
                    holidayKey = holidayPair.Key;
                    break;
                }
            }

            var timexStr = string.Empty;

            if (!string.IsNullOrEmpty(holidayKey))
            {
                var value = referenceDate;
                Func <int, DateObject> function;
                if (this.config.HolidayFuncDictionary.TryGetValue(holidayKey, out function))
                {
                    value = function(year);
                    this.config.VariableHolidaysTimexDictionary.TryGetValue(holidayKey, out timexStr);
                    if (string.IsNullOrEmpty(timexStr))
                    {
                        timexStr = $"-{value.Month:D2}-{value.Day:D2}";
                    }
                }

                if (function == null)
                {
                    return(ret);
                }

                if (hasYear)
                {
                    ret.Timex       = year.ToString("D4") + timexStr;
                    ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(year, value.Month, value.Day);
                    ret.Success     = true;
                    return(ret);
                }

                ret.Timex       = "XXXX" + timexStr;
                ret.FutureValue = GetFutureValue(value, referenceDate, holidayKey);
                ret.PastValue   = GetPastValue(value, referenceDate, holidayKey);
                ret.Success     = true;

                return(ret);
            }

            return(ret);
        }
Example #23
0
        // TODO: this can be abstracted with the similar method in BaseDatePeriodParser
        // Parse "in 20 minutes"
        private DateTimeResolutionResult ParseDuration(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();

            // For the rest of datetime, it will be handled in next function
            if (Config.RestOfDateTimeRegex.IsMatch(text))
            {
                return(ret);
            }

            var ers = Config.DurationExtractor.Extract(text, referenceTime);

            if (ers.Count == 1)
            {
                var pr = Config.DurationParser.Parse(ers[0]);

                var beforeStr = text.Substring(0, pr.Start ?? 0).Trim().ToLowerInvariant();
                var afterStr  = text.Substring((pr.Start ?? 0) + (pr.Length ?? 0)).Trim().ToLowerInvariant();

                var numbersInSuffix   = Config.CardinalExtractor.Extract(beforeStr);
                var numbersInDuration = Config.CardinalExtractor.Extract(ers[0].Text);

                // Handle cases like "2 upcoming days", "5 previous years"
                if (numbersInSuffix.Any() && !numbersInDuration.Any())
                {
                    var numberEr           = numbersInSuffix.First();
                    var numberText         = numberEr.Text;
                    var durationText       = ers[0].Text;
                    var combinedText       = $"{numberText} {durationText}";
                    var combinedDurationEr = Config.DurationExtractor.Extract(combinedText, referenceTime);

                    if (combinedDurationEr.Any())
                    {
                        pr = Config.DurationParser.Parse(combinedDurationEr.First());
                        var startIndex = numberEr.Start.Value + numberEr.Length.Value;
                        beforeStr = beforeStr.Substring(startIndex).Trim();
                    }
                }

                if (pr.Value != null)
                {
                    var swiftSeconds   = 0;
                    var mod            = string.Empty;
                    var durationResult = (DateTimeResolutionResult)pr.Value;
                    if (durationResult.PastValue is double && durationResult.FutureValue is double)
                    {
                        swiftSeconds = (int)((double)durationResult.FutureValue);
                    }

                    DateObject beginTime;
                    var        endTime = beginTime = referenceTime;

                    if (Config.PreviousPrefixRegex.IsExactMatch(beforeStr, trim: true))
                    {
                        mod       = Constants.BEFORE_MOD;
                        beginTime = referenceTime.AddSeconds(-swiftSeconds);
                    }

                    // Handle the "within (the) (next) xx seconds/minutes/hours" case
                    // Should also handle the multiple duration case like P1DT8H
                    // Set the beginTime equal to reference time for now
                    if (Config.WithinNextPrefixRegex.IsExactMatch(beforeStr, trim: true))
                    {
                        endTime = beginTime.AddSeconds(swiftSeconds);
                    }

                    if (Config.FutureRegex.IsExactMatch(beforeStr, trim: true))
                    {
                        mod     = Constants.AFTER_MOD;
                        endTime = beginTime.AddSeconds(swiftSeconds);
                    }

                    if (Config.PreviousPrefixRegex.IsExactMatch(afterStr, trim: true))
                    {
                        mod       = Constants.BEFORE_MOD;
                        beginTime = referenceTime.AddSeconds(-swiftSeconds);
                    }

                    if (Config.FutureRegex.IsExactMatch(afterStr, trim: true))
                    {
                        mod     = Constants.AFTER_MOD;
                        endTime = beginTime.AddSeconds(swiftSeconds);
                    }

                    if (Config.FutureSuffixRegex.IsExactMatch(afterStr, trim: true))
                    {
                        mod     = Constants.AFTER_MOD;
                        endTime = beginTime.AddSeconds(swiftSeconds);
                    }

                    ret.Timex =
                        $"({DateTimeFormatUtil.LuisDate(beginTime)}T{DateTimeFormatUtil.LuisTime(beginTime)}," +
                        $"{DateTimeFormatUtil.LuisDate(endTime)}T{DateTimeFormatUtil.LuisTime(endTime)}," +
                        $"{durationResult.Timex})";

                    ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginTime, endTime);
                    ret.Success     = true;

                    if (!string.IsNullOrEmpty(mod))
                    {
                        ((DateTimeResolutionResult)pr.Value).Mod = mod;
                    }

                    ret.SubDateTimeEntities = new List <object> {
                        pr
                    };

                    return(ret);
                }
            }

            return(ret);
        }
        private static DateTimeResolutionResult GetResolution(ExtractResult er, DateTimeParseResult pr, DateTimeResolutionResult ret)
        {
            var parentText = (string)((Dictionary <string, object>)er.Data)[ExtendedModelResult.ParentTextKey];
            var type       = pr.Type;

            var singlePointResolution      = "";
            var pastStartPointResolution   = "";
            var pastEndPointResolution     = "";
            var futureStartPointResolution = "";
            var futureEndPointResolution   = "";
            var singlePointType            = "";
            var startPointType             = "";
            var endPointType = "";

            if (type == Constants.SYS_DATETIME_DATEPERIOD || type == Constants.SYS_DATETIME_TIMEPERIOD ||
                type == Constants.SYS_DATETIME_DATETIMEPERIOD)
            {
                switch (type)
                {
                case Constants.SYS_DATETIME_DATEPERIOD:
                    startPointType             = TimeTypeConstants.START_DATE;
                    endPointType               = TimeTypeConstants.END_DATE;
                    pastStartPointResolution   = FormatUtil.FormatDate(((Tuple <DateObject, DateObject>)ret.PastValue).Item1);
                    pastEndPointResolution     = FormatUtil.FormatDate(((Tuple <DateObject, DateObject>)ret.PastValue).Item2);
                    futureStartPointResolution = FormatUtil.FormatDate(((Tuple <DateObject, DateObject>)ret.FutureValue).Item1);
                    futureEndPointResolution   = FormatUtil.FormatDate(((Tuple <DateObject, DateObject>)ret.FutureValue).Item2);
                    break;

                case Constants.SYS_DATETIME_DATETIMEPERIOD:
                    startPointType = TimeTypeConstants.START_DATETIME;
                    endPointType   = TimeTypeConstants.END_DATETIME;

                    if (ret.PastValue is Tuple <DateObject, DateObject> tuple)
                    {
                        pastStartPointResolution   = FormatUtil.FormatDateTime(tuple.Item1);
                        pastEndPointResolution     = FormatUtil.FormatDateTime(tuple.Item2);
                        futureStartPointResolution =
                            FormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)ret.FutureValue).Item1);
                        futureEndPointResolution =
                            FormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)ret.FutureValue).Item2);
                    }
                    else if (ret.PastValue is DateObject datetime)
                    {
                        pastStartPointResolution   = FormatUtil.FormatDateTime(datetime);
                        futureStartPointResolution = FormatUtil.FormatDateTime((DateObject)ret.FutureValue);
                    }

                    break;

                case Constants.SYS_DATETIME_TIMEPERIOD:
                    startPointType             = TimeTypeConstants.START_TIME;
                    endPointType               = TimeTypeConstants.END_TIME;
                    pastStartPointResolution   = FormatUtil.FormatTime(((Tuple <DateObject, DateObject>)ret.PastValue).Item1);
                    pastEndPointResolution     = FormatUtil.FormatTime(((Tuple <DateObject, DateObject>)ret.PastValue).Item2);
                    futureStartPointResolution = FormatUtil.FormatTime(((Tuple <DateObject, DateObject>)ret.FutureValue).Item1);
                    futureEndPointResolution   = FormatUtil.FormatTime(((Tuple <DateObject, DateObject>)ret.FutureValue).Item2);
                    break;
                }
            }
            else
            {
                switch (type)
                {
                case Constants.SYS_DATETIME_DATE:
                    singlePointType       = TimeTypeConstants.DATE;
                    singlePointResolution = FormatUtil.FormatDate((DateObject)ret.FutureValue);
                    break;

                case Constants.SYS_DATETIME_DATETIME:
                    singlePointType       = TimeTypeConstants.DATETIME;
                    singlePointResolution = FormatUtil.FormatDateTime((DateObject)ret.FutureValue);
                    break;

                case Constants.SYS_DATETIME_TIME:
                    singlePointType       = TimeTypeConstants.TIME;
                    singlePointResolution = FormatUtil.FormatTime((DateObject)ret.FutureValue);
                    break;
                }
            }

            ret.FutureResolution = new Dictionary <string, string>();
            ret.PastResolution   = new Dictionary <string, string>();

            if (!string.IsNullOrEmpty(futureStartPointResolution))
            {
                ret.FutureResolution.Add(startPointType, futureStartPointResolution);
            }

            if (!string.IsNullOrEmpty(futureEndPointResolution))
            {
                ret.FutureResolution.Add(endPointType, futureEndPointResolution);
            }

            if (!string.IsNullOrEmpty(pastStartPointResolution))
            {
                ret.PastResolution.Add(startPointType, pastStartPointResolution);
            }

            if (!string.IsNullOrEmpty(pastEndPointResolution))
            {
                ret.PastResolution.Add(endPointType, pastEndPointResolution);
            }

            if (!string.IsNullOrEmpty(singlePointResolution))
            {
                ret.FutureResolution.Add(singlePointType, singlePointResolution);
                ret.PastResolution.Add(singlePointType, singlePointResolution);
            }

            if (!string.IsNullOrEmpty(parentText))
            {
                ret.FutureResolution.Add(ExtendedModelResult.ParentTextKey, parentText);
                ret.PastResolution.Add(ExtendedModelResult.ParentTextKey, parentText);
            }

            if (((DateTimeResolutionResult)pr.Value).Mod != null)
            {
                ret.Mod = ((DateTimeResolutionResult)pr.Value).Mod;
            }

            if (((DateTimeResolutionResult)pr.Value).TimeZoneResolution != null)
            {
                ret.TimeZoneResolution = ((DateTimeResolutionResult)pr.Value).TimeZoneResolution;
            }

            return(ret);
        }
        // parse "morning", "afternoon", "night"
        private DateTimeResolutionResult ParseNight(string text, DateObject referenceTime)
        {
            int day   = referenceTime.Day,
                month = referenceTime.Month,
                year  = referenceTime.Year;
            var ret   = new DateTimeResolutionResult();

            // extract early/late prefix from text
            var  match = this.config.TimeOfDayRegex.Match(text);
            bool hasEarly = false, hasLate = false;

            if (match.Success)
            {
                if (!string.IsNullOrEmpty(match.Groups["early"].Value))
                {
                    var early = match.Groups["early"].Value;
                    text        = text.Replace(early, "");
                    hasEarly    = true;
                    ret.Comment = Constants.Comment_Early;
                }

                if (!hasEarly && !string.IsNullOrEmpty(match.Groups["late"].Value))
                {
                    var late = match.Groups["late"].Value;
                    text        = text.Replace(late, "");
                    hasLate     = true;
                    ret.Comment = Constants.Comment_Late;
                }
            }

            if (!this.config.GetMatchedTimexRange(text, out string timex, out int beginHour, out int endHour, out int endMinSeg))
            {
                return(new DateTimeResolutionResult());
            }

            // modify time period if "early" or "late" is existed
            if (hasEarly)
            {
                endHour = beginHour + 2;
                // handling case: night end with 23:59
                if (endMinSeg == 59)
                {
                    endMinSeg = 0;
                }
            }
            else if (hasLate)
            {
                beginHour = beginHour + 2;
            }

            ret.Timex = timex;

            ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(
                DateObject.MinValue.SafeCreateFromValue(year, month, day, beginHour, 0, 0),
                DateObject.MinValue.SafeCreateFromValue(year, month, day, endHour, endMinSeg, endMinSeg)
                );

            ret.Success = true;

            return(ret);
        }
        // merge the entity with its related contexts and then parse the combine text
        private DateTimeResolutionResult ParseDateTimeAndTimeAlt(ExtractResult er, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();

            // Original type of the extracted entity
            var subType    = ((Dictionary <string, object>)er.Data)[Constants.SubType].ToString();
            var dateTimeEr = new ExtractResult();

            // e.g. {next week Mon} or {Tue}, formmer--"next week Mon" doesn't contain "context" key
            var           hasContext = false;
            ExtractResult contextEr  = null;

            if (((Dictionary <string, object>)er.Data).ContainsKey(Constants.Context))
            {
                contextEr = (ExtractResult)((Dictionary <string, object>)er.Data)[Constants.Context];
                if (contextEr.Type.Equals(Constants.ContextType_RelativeSuffix))
                {
                    dateTimeEr.Text = $"{er.Text} {contextEr.Text}";
                }
                else
                {
                    dateTimeEr.Text = $"{contextEr.Text} {er.Text}";
                }

                hasContext = true;
            }
            else
            {
                dateTimeEr.Text = er.Text;
            }

            var dateTimePr = new DateTimeParseResult();

            if (subType == Constants.SYS_DATETIME_DATE)
            {
                dateTimeEr.Type = Constants.SYS_DATETIME_DATE;
                dateTimePr      = this.config.DateParser.Parse(dateTimeEr, referenceTime);
            }
            else if (subType == Constants.SYS_DATETIME_TIME)
            {
                if (!hasContext)
                {
                    dateTimeEr.Type = Constants.SYS_DATETIME_TIME;
                    dateTimePr      = this.config.TimeParser.Parse(dateTimeEr, referenceTime);
                }
                else if (contextEr.Type == Constants.SYS_DATETIME_DATE || contextEr.Type == Constants.ContextType_RelativePrefix)
                {
                    // For cases:
                    //      Monday 9 am or 11 am
                    //      next 9 am or 11 am
                    dateTimeEr.Type = Constants.SYS_DATETIME_DATETIME;
                    dateTimePr      = this.config.DateTimeParser.Parse(dateTimeEr, referenceTime);
                }
                else if (contextEr.Type == Constants.ContextType_AmPm)
                {
                    // For cases: in the afternoon 3 o'clock or 5 o'clock
                    dateTimeEr.Type = Constants.SYS_DATETIME_TIME;
                    dateTimePr      = this.config.TimeParser.Parse(dateTimeEr, referenceTime);
                }
            }
            else if (subType == Constants.SYS_DATETIME_DATETIME)
            {
                // "next week Mon 9 am or Tue 1 pm"
                dateTimeEr.Type = Constants.SYS_DATETIME_DATETIME;
                dateTimePr      = this.config.DateTimeParser.Parse(dateTimeEr, referenceTime);
            }
            else if (subType == Constants.SYS_DATETIME_TIMEPERIOD)
            {
                if (!hasContext)
                {
                    dateTimeEr.Type = Constants.SYS_DATETIME_TIMEPERIOD;
                    dateTimePr      = this.config.TimePeriodParser.Parse(dateTimeEr, referenceTime);
                }
                else if (contextEr.Type == Constants.SYS_DATETIME_DATE || contextEr.Type == Constants.ContextType_RelativePrefix)
                {
                    dateTimeEr.Type = Constants.SYS_DATETIME_DATETIMEPERIOD;
                    dateTimePr      = this.config.DateTimePeriodParser.Parse(dateTimeEr, referenceTime);
                }
            }
            else if (subType == Constants.SYS_DATETIME_DATETIMEPERIOD)
            {
                dateTimeEr.Type = Constants.SYS_DATETIME_DATETIMEPERIOD;
                dateTimePr      = this.config.DateTimePeriodParser.Parse(dateTimeEr, referenceTime);
            }
            else if (subType == Constants.SYS_DATETIME_DATEPERIOD)
            {
                dateTimeEr.Type = Constants.SYS_DATETIME_DATEPERIOD;
                dateTimePr      = this.config.DatePeriodParser.Parse(dateTimeEr, referenceTime);
            }

            if (dateTimePr.Value != null)
            {
                ret.FutureValue = ((DateTimeResolutionResult)dateTimePr.Value).FutureValue;
                ret.PastValue   = ((DateTimeResolutionResult)dateTimePr.Value).PastValue;
                ret.Timex       = dateTimePr.TimexStr;

                // Create resolution
                ret = GetResolution(er, dateTimePr, ret);

                ret.Success = true;
            }

            return(ret);
        }
Example #27
0
        // Match several other cases
        // Including 'today', 'the day after tomorrow', 'on 13'
        private DateTimeResolutionResult ParseImplicitDate(string text, DateObject referenceDate)
        {
            var trimmedText = text.Trim();

            var ret = new DateTimeResolutionResult();

            // Handle "on 12"
            var match = this.config.OnRegex.Match(this.config.DateTokenPrefix + trimmedText);

            if (match.Success && match.Index == 3 && match.Length == trimmedText.Length)
            {
                int month = referenceDate.Month, year = referenceDate.Year;
                var dayStr = match.Groups["day"].Value;
                var day    = this.config.DayOfMonth[dayStr];

                ret.Timex = DateTimeFormatUtil.LuisDate(-1, -1, day);

                DateObject futureDate, pastDate;
                var        tryStr = DateTimeFormatUtil.LuisDate(year, month, day);
                if (DateObject.TryParse(tryStr, out DateObject _))
                {
                    futureDate = DateObject.MinValue.SafeCreateFromValue(year, month, day);
                    pastDate   = DateObject.MinValue.SafeCreateFromValue(year, month, day);

                    if (futureDate < referenceDate)
                    {
                        futureDate = futureDate.AddMonths(+1);
                    }

                    if (pastDate >= referenceDate)
                    {
                        pastDate = pastDate.AddMonths(-1);
                    }
                }
                else
                {
                    futureDate = DateObject.MinValue.SafeCreateFromValue(year, month + 1, day);
                    pastDate   = DateObject.MinValue.SafeCreateFromValue(year, month - 1, day);
                }

                ret.FutureValue = futureDate;
                ret.PastValue   = pastDate;
                ret.Success     = true;

                return(ret);
            }

            // Handle "today", "the day before yesterday"
            var exactMatch = this.config.SpecialDayRegex.MatchExact(trimmedText, trim: true);

            if (exactMatch.Success)
            {
                var swift = GetSwiftDay(exactMatch.Value);

                var value = referenceDate.Date.AddDays(swift);

                ret.Timex       = DateTimeFormatUtil.LuisDate(value);
                ret.FutureValue = ret.PastValue = value;
                ret.Success     = true;

                return(ret);
            }

            // Handle "two days from tomorrow"
            exactMatch = this.config.SpecialDayWithNumRegex.MatchExact(trimmedText, trim: true);

            if (exactMatch.Success)
            {
                var swift     = GetSwiftDay(exactMatch.Groups["day"].Value);
                var numErs    = this.config.IntegerExtractor.Extract(trimmedText);
                var numOfDays = Convert.ToInt32((double)(this.config.NumberParser.Parse(numErs[0]).Value ?? 0));

                var value = referenceDate.AddDays(numOfDays + swift);

                ret.Timex       = DateTimeFormatUtil.LuisDate(value);
                ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(value.Year, value.Month, value.Day);
                ret.Success     = true;

                return(ret);
            }

            // Handle "two sundays from now"
            exactMatch = this.config.RelativeWeekDayRegex.MatchExact(trimmedText, trim: true);

            if (exactMatch.Success)
            {
                var numErs     = this.config.IntegerExtractor.Extract(trimmedText);
                var num        = Convert.ToInt32((double)(this.config.NumberParser.Parse(numErs[0]).Value ?? 0));
                var weekdayStr = exactMatch.Groups["weekday"].Value;
                var value      = referenceDate;

                // Check whether the determined day of this week has passed.
                if (value.DayOfWeek > (DayOfWeek)this.config.DayOfWeek[weekdayStr])
                {
                    num--;
                }

                while (num-- > 0)
                {
                    value = value.Next((DayOfWeek)this.config.DayOfWeek[weekdayStr]);
                }

                ret.Timex       = DateTimeFormatUtil.LuisDate(value);
                ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(value.Year, value.Month, value.Day);
                ret.Success     = true;

                return(ret);
            }

            // Handle "next Sunday", "upcoming Sunday"
            // We define "upcoming Sunday" as the nearest Sunday to come (not include today)
            // We define "next Sunday" as Sunday of next week
            exactMatch = this.config.NextRegex.MatchExact(trimmedText, trim: true);
            if (exactMatch.Success)
            {
                var weekdayStr = exactMatch.Groups["weekday"].Value;
                var value      = referenceDate.Next((DayOfWeek)this.config.DayOfWeek[weekdayStr]);

                if (this.config.UpcomingPrefixRegex.MatchBegin(trimmedText, trim: true).Success)
                {
                    value = referenceDate.Upcoming((DayOfWeek)this.config.DayOfWeek[weekdayStr]);
                }

                ret.Timex       = DateTimeFormatUtil.LuisDate(value);
                ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(value.Year, value.Month, value.Day);
                ret.Success     = true;

                return(ret);
            }

            // Handle "this Friday"
            exactMatch = this.config.ThisRegex.MatchExact(trimmedText, trim: true);

            if (exactMatch.Success)
            {
                var weekdayStr = exactMatch.Groups["weekday"].Value;
                var value      = referenceDate.This((DayOfWeek)this.config.DayOfWeek[weekdayStr]);

                ret.Timex       = DateTimeFormatUtil.LuisDate(value);
                ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(value.Year, value.Month, value.Day);
                ret.Success     = true;

                return(ret);
            }

            // Handle "last Friday", "last mon"
            // We define "past Sunday" as the nearest Sunday that has already passed (not include today)
            // We define "previous Sunday" as Sunday of previous week
            exactMatch = this.config.LastRegex.MatchExact(trimmedText, trim: true);

            if (exactMatch.Success)
            {
                var weekdayStr = exactMatch.Groups["weekday"].Value;
                var value      = referenceDate.Last((DayOfWeek)this.config.DayOfWeek[weekdayStr]);

                if (this.config.PastPrefixRegex.MatchBegin(trimmedText, trim: true).Success)
                {
                    value = referenceDate.Past((DayOfWeek)this.config.DayOfWeek[weekdayStr]);
                }

                ret.Timex       = DateTimeFormatUtil.LuisDate(value);
                ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(value.Year, value.Month, value.Day);
                ret.Success     = true;

                return(ret);
            }

            // Handle "Friday"
            exactMatch = this.config.WeekDayRegex.MatchExact(trimmedText, trim: true);

            if (exactMatch.Success)
            {
                var weekdayStr = exactMatch.Groups["weekday"].Value;
                var weekDay    = this.config.DayOfWeek[weekdayStr];
                var value      = referenceDate.This((DayOfWeek)this.config.DayOfWeek[weekdayStr]);

                if (weekDay == 0)
                {
                    weekDay = 7;
                }

                if (weekDay < (int)referenceDate.DayOfWeek)
                {
                    value = referenceDate.Next((DayOfWeek)weekDay);
                }

                ret.Timex = "XXXX-WXX-" + weekDay;
                var futureDate = value;
                var pastDate   = value;
                if (futureDate < referenceDate)
                {
                    futureDate = futureDate.AddDays(7);
                }

                if (pastDate >= referenceDate)
                {
                    pastDate = pastDate.AddDays(-7);
                }

                ret.FutureValue = DateObject.MinValue.SafeCreateFromValue(futureDate.Year, futureDate.Month, futureDate.Day);
                ret.PastValue   = DateObject.MinValue.SafeCreateFromValue(pastDate.Year, pastDate.Month, pastDate.Day);
                ret.Success     = true;

                return(ret);
            }

            // Handle "for the 27th."
            match = this.config.ForTheRegex.Match(text);

            if (match.Success)
            {
                int day = 0, month = referenceDate.Month, year = referenceDate.Year;
                var dayStr = match.Groups["DayOfMonth"].Value;

                // Create a extract result which content ordinal string of text
                ExtractResult er = new ExtractResult
                {
                    Text   = dayStr,
                    Start  = match.Groups["DayOfMonth"].Index,
                    Length = match.Groups["DayOfMonth"].Length,
                };

                day = Convert.ToInt32((double)(this.config.NumberParser.Parse(er).Value ?? 0));

                ret.Timex = DateTimeFormatUtil.LuisDate(-1, -1, day);

                DateObject futureDate;
                var        tryStr = DateTimeFormatUtil.LuisDate(year, month, day);
                if (DateObject.TryParse(tryStr, out DateObject _))
                {
                    futureDate = DateObject.MinValue.SafeCreateFromValue(year, month, day);
                }
                else
                {
                    futureDate = DateObject.MinValue.SafeCreateFromValue(year, month + 1, day);
                }

                ret.FutureValue = futureDate;
                ret.PastValue   = ret.FutureValue;
                ret.Success     = true;

                return(ret);
            }

            // Handling cases like 'Thursday the 21st', which both 'Thursday' and '21st' refer to a same date
            match = this.config.WeekDayAndDayOfMothRegex.Match(text);
            if (match.Success)
            {
                int month = referenceDate.Month, year = referenceDate.Year;

                // create a extract result which content ordinal string of text
                ExtractResult extractResultTmp = new ExtractResult
                {
                    Text   = match.Groups["DayOfMonth"].Value,
                    Start  = match.Groups["DayOfMonth"].Index,
                    Length = match.Groups["DayOfMonth"].Length,
                };

                // parse the day in text into number
                var day = Convert.ToInt32((double)(this.config.NumberParser.Parse(extractResultTmp).Value ?? 0));

                // The validity of the phrase is guaranteed in the Date Extractor
                ret.Timex       = DateTimeFormatUtil.LuisDate(year, month, day);
                ret.FutureValue = new DateObject(year, month, day);
                ret.PastValue   = new DateObject(year, month, day);
                ret.Success     = true;

                return(ret);
            }

            // 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.
            match = this.config.WeekDayAndDayRegex.Match(text);
            if (match.Success)
            {
                int month = referenceDate.Month, year = referenceDate.Year;

                // Create a extract result which content ordinal string of text
                ExtractResult ertmp = new ExtractResult
                {
                    Text   = match.Groups["day"].Value,
                    Start  = match.Groups["day"].Index,
                    Length = match.Groups["day"].Length,
                };

                // Parse the day in text into number
                var day = Convert.ToInt32((double)(this.config.NumberParser.Parse(ertmp).Value ?? 0));

                // Firstly, find a latest date with the "day" as pivotDate.
                // Secondly, if the pivotDate equals the referenced date, in other word, the day of the referenced date is exactly the "day".
                // In this way, check if the pivotDate is the weekday. If so, then the futureDate and the previousDate are the same date (referenced date).
                // Otherwise, increase the pivotDate month by month to find the latest futureDate and decrease the pivotDate month
                // by month to the latest previousDate.
                // Notice: if the "day" is larger than 28, some months should be ignored in the increase or decrease procedure.
                var pivotDate   = new DateObject(year, month, 1);
                var daysInMonth = DateObject.DaysInMonth(year, month);
                if (daysInMonth >= day)
                {
                    pivotDate = DateObject.MinValue.SafeCreateFromValue(year, month, day);
                }
                else
                {
                    // Add 1 month is enough, since 1, 3, 5, 7, 8, 10, 12 months has 31 days
                    pivotDate = pivotDate.AddMonths(1);
                    pivotDate = DateObject.MinValue.SafeCreateFromValue(pivotDate.Year, pivotDate.Month, day);
                }

                var numWeekDayInt       = (int)pivotDate.DayOfWeek;
                var extractedWeekDayStr = match.Groups["weekday"].Value;
                var weekDay             = this.config.DayOfWeek[extractedWeekDayStr];
                if (!pivotDate.Equals(DateObject.MinValue))
                {
                    if (day == referenceDate.Day && numWeekDayInt == weekDay)
                    {
                        // The referenceDate is the weekday and with the "day".
                        ret.FutureValue = new DateObject(year, month, day);
                        ret.PastValue   = new DateObject(year, month, day);
                        ret.Timex       = DateTimeFormatUtil.LuisDate(year, month, day);
                    }
                    else
                    {
                        var futureDate = pivotDate;
                        var pastDate   = pivotDate;

                        while ((int)futureDate.DayOfWeek != weekDay || futureDate.Day != day || futureDate < referenceDate)
                        {
                            // Increase the futureDate month by month to find the expected date (the "day" is the weekday) and
                            // make sure the futureDate not less than the referenceDate.
                            futureDate = futureDate.AddMonths(1);
                            var tmp = DateObject.DaysInMonth(futureDate.Year, futureDate.Month);
                            if (tmp >= day)
                            {
                                // For months like January 31, after add 1 month, February 31 won't be returned, so the day should be revised ASAP.
                                futureDate = futureDate.SafeCreateFromValue(futureDate.Year, futureDate.Month, day);
                            }
                        }

                        ret.FutureValue = futureDate;

                        while ((int)pastDate.DayOfWeek != weekDay || pastDate.Day != day || pastDate > referenceDate)
                        {
                            // Decrease the pastDate month by month to find the expected date (the "day" is the weekday) and
                            // make sure the pastDate not larger than the referenceDate.
                            pastDate = pastDate.AddMonths(-1);
                            var tmp = DateObject.DaysInMonth(pastDate.Year, pastDate.Month);
                            if (tmp >= day)
                            {
                                // For months like March 31, after minus 1 month, February 31 won't be returned, so the day should be revised ASAP.
                                pastDate = pastDate.SafeCreateFromValue(pastDate.Year, pastDate.Month, day);
                            }
                        }

                        ret.PastValue = pastDate;

                        if (weekDay == 0)
                        {
                            weekDay = 7;
                        }

                        ret.Timex = "XXXX-WXX-" + weekDay;
                    }
                }

                ret.Success = true;

                return(ret);
            }

            return(ret);
        }
Example #28
0
        private DateTimeResolutionResult Match2Time(Match match, DateObject referenceTime)
        {
            var  ret = new DateTimeResolutionResult();
            bool hasMin = false, hasSec = false, hasAm = false, hasPm = false, hasMid = false;
            int  hour   = 0,
                 min    = 0,
                 second = 0,
                 day    = referenceTime.Day,
                 month  = referenceTime.Month,
                 year   = referenceTime.Year;

            var writtenTimeStr = match.Groups["writtentime"].Value;

            if (!string.IsNullOrEmpty(writtenTimeStr))
            {
                // get hour
                var hourStr = match.Groups["hournum"].Value.ToLower();
                hour = this.config.Numbers[hourStr];

                // get minute
                var minStr  = match.Groups["minnum"].Value;
                var tensStr = match.Groups["tens"].Value;

                if (!string.IsNullOrEmpty(minStr))
                {
                    min = this.config.Numbers[minStr];
                    if (!string.IsNullOrEmpty(tensStr))
                    {
                        min += this.config.Numbers[tensStr];
                    }

                    hasMin = true;
                }
            }
            else if (!string.IsNullOrEmpty(match.Groups["mid"].Value))
            {
                hasMid = true;
                if (!string.IsNullOrEmpty(match.Groups["midnight"].Value))
                {
                    hour   = 0;
                    min    = 0;
                    second = 0;
                }
                else if (!string.IsNullOrEmpty(match.Groups["midmorning"].Value))
                {
                    hour   = 10;
                    min    = 0;
                    second = 0;
                }
                else if (!string.IsNullOrEmpty(match.Groups["midafternoon"].Value))
                {
                    hour   = 14;
                    min    = 0;
                    second = 0;
                }
                else if (!string.IsNullOrEmpty(match.Groups["midday"].Value))
                {
                    hour   = Constants.HalfDayHourCount;
                    min    = 0;
                    second = 0;
                }
            }
            else
            {
                // get hour
                var hourStr = match.Groups[Constants.HourGroupName].Value;
                if (string.IsNullOrEmpty(hourStr))
                {
                    hourStr = match.Groups["hournum"].Value.ToLower();
                    if (!this.config.Numbers.TryGetValue(hourStr, out hour))
                    {
                        return(ret);
                    }
                }
                else
                {
                    if (!int.TryParse(hourStr, out hour))
                    {
                        if (!this.config.Numbers.TryGetValue(hourStr.ToLower(), out hour))
                        {
                            return(ret);
                        }
                    }
                }

                // get minute
                var minStr = match.Groups[Constants.MinuteGroupName].Value.ToLower();
                if (string.IsNullOrEmpty(minStr))
                {
                    minStr = match.Groups["minnum"].Value;
                    if (!string.IsNullOrEmpty(minStr))
                    {
                        min    = this.config.Numbers[minStr];
                        hasMin = true;
                    }

                    var tensStr = match.Groups["tens"].Value;
                    if (!string.IsNullOrEmpty(tensStr))
                    {
                        min   += this.config.Numbers[tensStr];
                        hasMin = true;
                    }
                }
                else
                {
                    min    = int.Parse(minStr);
                    hasMin = true;
                }

                // get second
                var secStr = match.Groups[Constants.SecondGroupName].Value.ToLower();
                if (!string.IsNullOrEmpty(secStr))
                {
                    second = int.Parse(secStr);
                    hasSec = true;
                }
            }

            // Adjust by desc string
            var descStr = match.Groups[Constants.DescGroupName].Value.ToLower();

            // ampm is a special case in which at 6ampm = at 6
            if (config.UtilityConfiguration.AmDescRegex.Match(descStr).Success ||
                config.UtilityConfiguration.AmPmDescRegex.Match(descStr).Success ||
                match.Groups[Constants.ImplicitAmGroupName].Success)
            {
                if (hour >= Constants.HalfDayHourCount)
                {
                    hour -= Constants.HalfDayHourCount;
                }

                if (!config.UtilityConfiguration.AmPmDescRegex.Match(descStr).Success)
                {
                    hasAm = true;
                }
            }
            else if (config.UtilityConfiguration.PmDescRegex.Match(descStr).Success ||
                     match.Groups[Constants.ImplicitPmGroupName].Success)
            {
                if (hour < Constants.HalfDayHourCount)
                {
                    hour += Constants.HalfDayHourCount;
                }

                hasPm = true;
            }

            // adjust min by prefix
            var timePrefix = match.Groups[Constants.PrefixGroupName].Value.ToLower();

            if (!string.IsNullOrEmpty(timePrefix))
            {
                this.config.AdjustByPrefix(timePrefix, ref hour, ref min, ref hasMin);
            }

            // adjust hour by suffix
            var timeSuffix = match.Groups[Constants.SuffixGroupName].Value.ToLower();

            if (!string.IsNullOrEmpty(timeSuffix))
            {
                this.config.AdjustBySuffix(timeSuffix, ref hour, ref min, ref hasMin, ref hasAm, ref hasPm);
            }

            if (hour == 24)
            {
                hour = 0;
            }

            ret.Timex = "T" + hour.ToString("D2");
            if (hasMin)
            {
                ret.Timex += ":" + min.ToString("D2");
            }

            if (hasSec)
            {
                ret.Timex += ":" + second.ToString("D2");
            }

            if (hour <= Constants.HalfDayHourCount && !hasPm && !hasAm && !hasMid)
            {
                ret.Comment = Constants.Comment_AmPm;
            }

            ret.FutureValue = ret.PastValue = DateObject.MinValue.SafeCreateFromValue(year, month, day, hour, min, second);
            ret.Success     = true;

            return(ret);
        }
Example #29
0
        // Parse a regex match which includes 'day', 'month' and 'year' (optional) group
        private DateTimeResolutionResult Match2Date(Match match, DateObject referenceDate, string relativeStr)
        {
            var ret = new DateTimeResolutionResult();

            var monthStr = match.Groups["month"].Value;
            var dayStr = match.Groups["day"].Value;
            var yearStr = match.Groups["year"].Value;
            var weekdayStr = match.Groups["weekday"].Value;
            int month = 0, day = 0, year = 0;

            if (this.config.MonthOfYear.ContainsKey(monthStr) && this.config.DayOfMonth.ContainsKey(dayStr))
            {
                month = this.config.MonthOfYear[monthStr];
                day   = this.config.DayOfMonth[dayStr];
                if (!string.IsNullOrEmpty(yearStr))
                {
                    year = int.Parse(yearStr);
                    if (year < 100 && year >= Constants.MinTwoDigitYearPastNum)
                    {
                        year += 1900;
                    }
                    else if (year >= 0 && year < Constants.MaxTwoDigitYearFutureNum)
                    {
                        year += 2000;
                    }
                }
            }

            var noYear = false;

            if (year == 0)
            {
                year = referenceDate.Year;
                if (!string.IsNullOrEmpty(relativeStr))
                {
                    var swift = this.config.GetSwiftMonthOrYear(relativeStr);

                    // @TODO Improve handling of next/last in particular cases "next friday 5/12" when the next friday is not 5/12.
                    if (!string.IsNullOrEmpty(weekdayStr))
                    {
                        swift = 0;
                    }

                    year += swift;
                }
                else
                {
                    noYear = true;
                }

                ret.Timex = DateTimeFormatUtil.LuisDate(-1, month, day);
            }
            else
            {
                ret.Timex = DateTimeFormatUtil.LuisDate(year, month, day);
            }

            var futureDate = DateObject.MinValue.SafeCreateFromValue(year, month, day);
            var pastDate   = DateObject.MinValue.SafeCreateFromValue(year, month, day);

            if (noYear && futureDate < referenceDate && !futureDate.IsDefaultValue())
            {
                futureDate = futureDate.AddYears(+1);
            }

            if (noYear && pastDate >= referenceDate && !pastDate.IsDefaultValue())
            {
                pastDate = pastDate.AddYears(-1);
            }

            ret.FutureValue = futureDate;
            ret.PastValue   = pastDate;
            ret.Success     = true;

            return(ret);
        }
Example #30
0
        // parse "in 20 minutes"
        private DateTimeResolutionResult ParseNumberWithUnit(string text, DateObject referenceTime)
        {
            var    ret = new DateTimeResolutionResult();
            string unitStr;

            // if there are spaces between number and unit
            var ers = this.config.CardinalExtractor.Extract(text);

            if (ers.Count == 1)
            {
                var pr      = this.config.CardinalParser.Parse(ers[0]);
                var srcUnit = text.Substring(ers[0].Start + ers[0].Length ?? 0).Trim();

                if (srcUnit.StartsWith("δΈͺ", StringComparison.Ordinal))
                {
                    srcUnit = srcUnit.Substring(1);
                }

                var beforeStr = text.Substring(0, ers[0].Start ?? 0);
                if (this.config.UnitMap.ContainsKey(srcUnit))
                {
                    var numStr = pr.ResolutionStr;
                    unitStr = this.config.UnitMap[srcUnit];
                    var prefixMatch = this.config.PastRegex.MatchExact(beforeStr, trim: true);

                    if (prefixMatch.Success)
                    {
                        DateObject beginDate, endDate;
                        switch (unitStr)
                        {
                        case "H":
                            beginDate = referenceTime.AddHours(-(double)pr.Value);
                            endDate   = referenceTime;
                            break;

                        case "M":
                            beginDate = referenceTime.AddMinutes(-(double)pr.Value);
                            endDate   = referenceTime;
                            break;

                        case "S":
                            beginDate = referenceTime.AddSeconds(-(double)pr.Value);
                            endDate   = referenceTime;
                            break;

                        default:
                            return(ret);
                        }

                        ret.Timex =
                            $"({DateTimeFormatUtil.LuisDate(beginDate)}T{DateTimeFormatUtil.LuisTime(beginDate)},{DateTimeFormatUtil.LuisDate(endDate)}T{DateTimeFormatUtil.LuisTime(endDate)},PT{numStr}{unitStr[0]})";
                        ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginDate, endDate);
                        ret.Success     = true;
                        return(ret);
                    }

                    prefixMatch = this.config.FutureRegex.MatchExact(beforeStr, trim: true);

                    if (prefixMatch.Success)
                    {
                        DateObject beginDate, endDate;
                        switch (unitStr)
                        {
                        case "H":
                            beginDate = referenceTime;
                            endDate   = referenceTime.AddHours((double)pr.Value);
                            break;

                        case "M":
                            beginDate = referenceTime;
                            endDate   = referenceTime.AddMinutes((double)pr.Value);
                            break;

                        case "S":
                            beginDate = referenceTime;
                            endDate   = referenceTime.AddSeconds((double)pr.Value);
                            break;

                        default:
                            return(ret);
                        }

                        ret.Timex =
                            $"({DateTimeFormatUtil.LuisDate(beginDate)}T{DateTimeFormatUtil.LuisTime(beginDate)},{DateTimeFormatUtil.LuisDate(endDate)}T{DateTimeFormatUtil.LuisTime(endDate)},PT{numStr}{unitStr[0]})";
                        ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginDate, endDate);
                        ret.Success     = true;
                        return(ret);
                    }
                }
            }

            // handle "last hour"
            var match = this.config.UnitRegex.Match(text);

            if (match.Success)
            {
                var srcUnit   = match.Groups["unit"].Value;
                var beforeStr = text.Substring(0, match.Index).Trim();
                if (this.config.UnitMap.ContainsKey(srcUnit))
                {
                    unitStr = this.config.UnitMap[srcUnit];

                    if (this.config.PastRegex.IsExactMatch(beforeStr, trim: true))
                    {
                        DateObject beginDate, endDate;
                        switch (unitStr)
                        {
                        case "H":
                            beginDate = referenceTime.AddHours(-1);
                            endDate   = referenceTime;
                            break;

                        case "M":
                            beginDate = referenceTime.AddMinutes(-1);
                            endDate   = referenceTime;
                            break;

                        case "S":
                            beginDate = referenceTime.AddSeconds(-1);
                            endDate   = referenceTime;
                            break;

                        default:
                            return(ret);
                        }

                        ret.Timex =
                            $"({DateTimeFormatUtil.LuisDate(beginDate)}T{DateTimeFormatUtil.LuisTime(beginDate)},{DateTimeFormatUtil.LuisDate(endDate)}T{DateTimeFormatUtil.LuisTime(endDate)},PT1{unitStr[0]})";
                        ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginDate, endDate);
                        ret.Success     = true;
                        return(ret);
                    }

                    if (this.config.FutureRegex.IsExactMatch(beforeStr, trim: true))
                    {
                        DateObject beginDate, endDate;
                        switch (unitStr)
                        {
                        case "H":
                            beginDate = referenceTime;
                            endDate   = referenceTime.AddHours(1);
                            break;

                        case "M":
                            beginDate = referenceTime;
                            endDate   = referenceTime.AddMinutes(1);
                            break;

                        case "S":
                            beginDate = referenceTime;
                            endDate   = referenceTime.AddSeconds(1);
                            break;

                        default:
                            return(ret);
                        }

                        ret.Timex =
                            $"({DateTimeFormatUtil.LuisDate(beginDate)}T{DateTimeFormatUtil.LuisTime(beginDate)},{DateTimeFormatUtil.LuisDate(endDate)}T{DateTimeFormatUtil.LuisTime(endDate)},PT1{unitStr[0]})";
                        ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginDate, endDate);
                        ret.Success     = true;
                        return(ret);
                    }
                }
            }

            return(ret);
        }