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

            object value = null;

            if (er.Type.Equals(ParserName, StringComparison.Ordinal))
            {
                DateTimeResolutionResult innerResult;

                if (TimeZoneUtility.ShouldResolveTimeZone(er, config.Options))
                {
                    var metadata   = er.Data as Dictionary <string, object>;
                    var timezoneEr = metadata[Constants.SYS_DATETIME_TIMEZONE] as ExtractResult;
                    var timezonePr = config.TimeZoneParser.Parse(timezoneEr);

                    innerResult = InternalParse(
                        er.Text.Substring(0, (int)(er.Length - timezoneEr.Length)),
                        referenceTime);

                    if (timezonePr != null && timezonePr.Value != null)
                    {
                        innerResult.TimeZoneResolution = ((DateTimeResolutionResult)timezonePr.Value).TimeZoneResolution;
                    }
                }
                else
                {
                    innerResult = InternalParse(er.Text, referenceTime);
                }

                if (innerResult.Success)
                {
                    innerResult.FutureResolution = new Dictionary <string, string>
                    {
                        {
                            TimeTypeConstants.START_TIME,
                            DateTimeFormatUtil.FormatTime(((Tuple <DateObject, DateObject>)innerResult.FutureValue).Item1)
                        },
                        {
                            TimeTypeConstants.END_TIME,
                            DateTimeFormatUtil.FormatTime(((Tuple <DateObject, DateObject>)innerResult.FutureValue).Item2)
                        },
                    };

                    innerResult.PastResolution = new Dictionary <string, string>
                    {
                        {
                            TimeTypeConstants.START_TIME,
                            DateTimeFormatUtil.FormatTime(((Tuple <DateObject, DateObject>)innerResult.PastValue).Item1)
                        },
                        {
                            TimeTypeConstants.END_TIME,
                            DateTimeFormatUtil.FormatTime(((Tuple <DateObject, DateObject>)innerResult.PastValue).Item2)
                        },
                    };

                    value = innerResult;
                }
            }

            var ret = new DateTimeParseResult
            {
                Text          = er.Text,
                Start         = er.Start,
                Length        = er.Length,
                Type          = er.Type,
                Data          = er.Data,
                Value         = value,
                TimexStr      = value == null ? string.Empty : ((DateTimeResolutionResult)value).Timex,
                ResolutionStr = string.Empty,
            };

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

            // Handle "今から2日曜日" (2 Sundays from now)
            var exactMatch = this.config.SpecialDayWithNumRegex.MatchExact(text, trim: true);

            if (exactMatch.Success)
            {
                var numErs     = this.config.IntegerExtractor.Extract(text);
                var weekdayStr = exactMatch.Groups["weekday"].Value;

                if (!string.IsNullOrEmpty(weekdayStr) && numErs.Count > 0)
                {
                    var num   = Convert.ToInt32((double)(this.config.NumberParser.Parse(numErs[0]).Value ?? 0));
                    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 "明日から3週間" (3 weeks from tomorrow)
            var durationResult = this.config.DurationExtractor.Extract(text, referenceDate);
            var unitMatch      = this.config.DurationRelativeDurationUnitRegex.Match(text);

            if (exactMatch.Success && unitMatch.Success && (durationResult.Count > 0) &&
                string.IsNullOrEmpty(unitMatch.Groups["few"].Value))
            {
                var pr     = this.config.DurationParser.Parse(durationResult[0], referenceDate);
                var dayStr = unitMatch.Groups["later"].Value;
                var future = true;
                int swift  = 0;

                if (pr != null)
                {
                    if (!string.IsNullOrEmpty(dayStr))
                    {
                        swift = this.config.GetSwiftDay(dayStr);
                    }

                    var resultDateTime = DurationParsingUtil.ShiftDateTime(pr.TimexStr, referenceDate.AddDays(swift), future);
                    ret.Timex       = $"{DateTimeFormatUtil.LuisDate(resultDateTime)}";
                    ret.FutureValue = ret.PastValue = resultDateTime;
                    ret.Success     = true;
                    return(ret);
                }
            }

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

            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);
        }
        // Handle cases like "三天前" "Three days ago"
        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 unitMatch = this.config.DurationRelativeDurationUnitRegex.Match(text);

                    // set the inexact number "数" (few) to 3 for now
                    var number = numberStr.Equals(unitMatch.Groups["few"].Value, StringComparison.Ordinal) ? 3 : ConvertCJKToNum(numberStr);

                    if (!numberStr.Equals(unitMatch.Groups["few"].Value, StringComparison.Ordinal))
                    {
                        if (suffix.Equals(unitMatch.Value, StringComparison.Ordinal))
                        {
                            var pr     = this.config.DurationParser.Parse(durationRes[0], referenceDate);
                            var future = suffix.Equals(unitMatch.Groups["later"].Value, StringComparison.Ordinal);
                            int swift  = 0;

                            if (pr != null)
                            {
                                var resultDateTime = DurationParsingUtil.ShiftDateTime(pr.TimexStr, referenceDate.AddDays(swift), future);
                                ret.Timex       = $"{DateTimeFormatUtil.LuisDate(resultDateTime)}";
                                ret.FutureValue = ret.PastValue = resultDateTime;
                                ret.Success     = true;
                                return(ret);
                            }
                        }
                    }

                    if (this.config.UnitMap.ContainsKey(srcUnit))
                    {
                        unitStr   = this.config.UnitMap[srcUnit];
                        ret.Timex = TimexUtility.GenerateDurationTimex(number, unitStr, DurationParsingUtil.IsLessThanDay(unitStr));
                        DateObject date = Constants.InvalidDate;

                        var beforeMatch = this.config.BeforeRegex.Match(suffix);
                        if (beforeMatch.Success && suffix.StartsWith(beforeMatch.Value, StringComparison.Ordinal))
                        {
                            date = DurationParsingUtil.ShiftDateTime(ret.Timex, referenceDate, future: false);
                        }

                        var afterMatch = this.config.AfterRegex.Match(suffix);
                        if (afterMatch.Success && suffix.StartsWith(afterMatch.Value, StringComparison.Ordinal))
                        {
                            date = DurationParsingUtil.ShiftDateTime(ret.Timex, referenceDate, future: true);
                        }

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

            return(ret);
        }
Example #4
0
        // 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 == 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   = 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;
                }
            }
            else
            {
                switch (type)
                {
                case Constants.SYS_DATETIME_DATE:
                    singlePointType       = TimeTypeConstants.DATE;
                    singlePointResolution = DateTimeFormatUtil.FormatDate((DateObject)ret.FutureValue);
                    break;

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

                case Constants.SYS_DATETIME_TIME:
                    singlePointType       = TimeTypeConstants.TIME;
                    singlePointResolution = DateTimeFormatUtil.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);
        }
        // handle cases like "5分钟前", "1小时以后"
        private DateTimeResolutionResult ParserDurationWithAgoAndLater(string text, DateObject referenceDate)
        {
            var ret         = new DateTimeResolutionResult();
            var durationRes = this.config.DurationExtractor.Extract(text, referenceDate);

            if (durationRes.Count > 0)
            {
                var match = this.config.DateTimePeriodUnitRegex.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    = ConvertCJKToNum(numberStr);

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

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

                            case Constants.TimexMinute:
                                date = referenceDate.AddMinutes(-number);
                                break;

                            case Constants.TimexSecond:
                                date = referenceDate.AddSeconds(-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.TimexHour:
                                date = referenceDate.AddHours(number);
                                break;

                            case Constants.TimexMinute:
                                date = referenceDate.AddMinutes(number);
                                break;

                            case Constants.TimexSecond:
                                date = referenceDate.AddSeconds(number);
                                break;

                            default:
                                return(ret);
                            }

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

            return(ret);
        }
Example #6
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]);
                }
                else if (config.GetSwiftMonthOrYear(trimmedText) == 2)
                {
                    value = value.AddDays(7);
                }

                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 #7
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);

            // check if the extraction is empty or a relative ordinal (e.g. "next", "previous")
            if (er.Count == 0 || er[0].Metadata.IsOrdinalRelative)
            {
                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);
                GetYearInAffix(suffix, ref year, ref ambiguous, out bool success);

                // Check also in prefix
                if (!success && this.config.CheckBothBeforeAfter)
                {
                    var prefix = trimmedText.Substring(0, er[0].Start ?? 0);
                    GetYearInAffix(prefix, ref year, ref ambiguous, out success);
                }
            }

            // 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 #8
0
        internal void ResolveAmpm(Dictionary <string, object> resolutionDic,
                                  string keyName)
        {
            if (resolutionDic.ContainsKey(keyName))
            {
                var resolution = (Dictionary <string, string>)resolutionDic[keyName];
                if (!resolutionDic.ContainsKey(DateTimeResolutionKey.Timex))
                {
                    return;
                }

                var timex = (string)resolutionDic[DateTimeResolutionKey.Timex];
                resolutionDic.Remove(keyName);

                resolutionDic.Add(keyName + "Am", resolution);

                var resolutionPm = new Dictionary <string, string>();
                switch ((string)resolutionDic[ResolutionKey.Type])
                {
                case Constants.SYS_DATETIME_TIME:
                    resolutionPm[ResolutionKey.Value]         = DateTimeFormatUtil.ToPm(resolution[ResolutionKey.Value]);
                    resolutionPm[DateTimeResolutionKey.Timex] = DateTimeFormatUtil.ToPm(timex);
                    break;

                case Constants.SYS_DATETIME_DATETIME:
                    var splited = resolution[ResolutionKey.Value].Split(' ');
                    resolutionPm[ResolutionKey.Value]         = splited[0] + " " + DateTimeFormatUtil.ToPm(splited[1]);
                    resolutionPm[DateTimeResolutionKey.Timex] = DateTimeFormatUtil.AllStringToPm(timex);
                    break;

                case Constants.SYS_DATETIME_TIMEPERIOD:
                    if (resolution.ContainsKey(DateTimeResolutionKey.START))
                    {
                        resolutionPm[DateTimeResolutionKey.START] = DateTimeFormatUtil.ToPm(resolution[DateTimeResolutionKey.START]);
                    }

                    if (resolution.ContainsKey(DateTimeResolutionKey.END))
                    {
                        resolutionPm[DateTimeResolutionKey.END] = DateTimeFormatUtil.ToPm(resolution[DateTimeResolutionKey.END]);
                    }

                    resolutionPm[DateTimeResolutionKey.Timex] = DateTimeFormatUtil.AllStringToPm(timex);
                    break;

                case Constants.SYS_DATETIME_DATETIMEPERIOD:
                    splited = resolution[DateTimeResolutionKey.START].Split(' ');
                    if (resolution.ContainsKey(DateTimeResolutionKey.START))
                    {
                        resolutionPm[DateTimeResolutionKey.START] = splited[0] + " " + DateTimeFormatUtil.ToPm(splited[1]);
                    }

                    splited = resolution[DateTimeResolutionKey.END].Split(' ');

                    if (resolution.ContainsKey(DateTimeResolutionKey.END))
                    {
                        resolutionPm[DateTimeResolutionKey.END] = splited[0] + " " + DateTimeFormatUtil.ToPm(splited[1]);
                    }

                    resolutionPm[DateTimeResolutionKey.Timex] = DateTimeFormatUtil.AllStringToPm(timex);
                    break;
                }

                resolutionDic.Add(keyName + "Pm", resolutionPm);
            }
        }
        public DateTimeParseResult Parse(ExtractResult er, DateObject refTime)
        {
            var referenceTime = refTime;

            object value = null;

            if (er.Type.Equals(ParserName))
            {
                var innerResult = MergeDateWithSingleTimePeriod(er.Text, referenceTime);

                if (!innerResult.Success)
                {
                    innerResult = MergeTwoTimePoints(er.Text, referenceTime);
                }

                if (!innerResult.Success)
                {
                    innerResult = ParseSpecificTimeOfDay(er.Text, referenceTime);
                }

                if (!innerResult.Success)
                {
                    innerResult = ParseDuration(er.Text, referenceTime);
                }

                if (!innerResult.Success)
                {
                    innerResult = ParseRelativeUnit(er.Text, referenceTime);
                }

                if (!innerResult.Success)
                {
                    innerResult = ParseDateWithPeriodPrefix(er.Text, referenceTime);
                }

                if (!innerResult.Success)
                {
                    // Cases like "today after 2:00pm", "1/1/2015 before 2:00 in the afternoon"
                    innerResult = ParseDateWithTimePeriodSuffix(er.Text, referenceTime);
                }

                if (innerResult.Success)
                {
                    if (!IsBeforeOrAfterMod(innerResult.Mod))
                    {
                        innerResult.FutureResolution = new Dictionary <string, string>
                        {
                            {
                                TimeTypeConstants.START_DATETIME,
                                DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)innerResult.FutureValue).Item1)
                            },
                            {
                                TimeTypeConstants.END_DATETIME,
                                DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)innerResult.FutureValue).Item2)
                            },
                        };

                        innerResult.PastResolution = new Dictionary <string, string>
                        {
                            {
                                TimeTypeConstants.START_DATETIME,
                                DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)innerResult.PastValue).Item1)
                            },
                            {
                                TimeTypeConstants.END_DATETIME,
                                DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)innerResult.PastValue).Item2)
                            },
                        };
                    }
                    else
                    {
                        if (innerResult.Mod == Constants.AFTER_MOD)
                        {
                            // Cases like "1/1/2015 after 2:00" there is no EndTime
                            innerResult.FutureResolution = new Dictionary <string, string>
                            {
                                {
                                    TimeTypeConstants.START_DATETIME,
                                    DateTimeFormatUtil.FormatDateTime((DateObject)innerResult.FutureValue)
                                },
                            };

                            innerResult.PastResolution = new Dictionary <string, string>
                            {
                                {
                                    TimeTypeConstants.START_DATETIME,
                                    DateTimeFormatUtil.FormatDateTime((DateObject)innerResult.PastValue)
                                },
                            };
                        }
                        else
                        {
                            // Cases like "1/1/2015 before 5:00 in the afternoon" there is no StartTime
                            innerResult.FutureResolution = new Dictionary <string, string>
                            {
                                {
                                    TimeTypeConstants.END_DATETIME,
                                    DateTimeFormatUtil.FormatDateTime((DateObject)innerResult.FutureValue)
                                },
                            };

                            innerResult.PastResolution = new Dictionary <string, string>
                            {
                                {
                                    TimeTypeConstants.END_DATETIME,
                                    DateTimeFormatUtil.FormatDateTime((DateObject)innerResult.PastValue)
                                },
                            };
                        }
                    }

                    value = innerResult;
                }
            }

            var ret = new DateTimeParseResult
            {
                Text          = er.Text,
                Start         = er.Start,
                Length        = er.Length,
                Type          = er.Type,
                Data          = er.Data,
                Value         = value,
                TimexStr      = value == null ? string.Empty : ((DateTimeResolutionResult)value).Timex,
                ResolutionStr = string.Empty,
            };

            return(ret);
        }
Example #10
0
        private DateTimeResolutionResult ParseTimeOfToday(string text, DateObject referenceTime)
        {
            var ret         = new DateTimeResolutionResult();
            var trimmedText = text.ToLowerInvariant().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.ToLower();
                    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.ToLowerInvariant();

                // 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))
                {
                    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 #11
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().ToLower();
            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.GetSwiftMonth(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 #12
0
        public static string GenerateYearTimex(int year, string specialYearPrefixes = null)
        {
            var yearTimex = DateTimeFormatUtil.LuisDate(year);

            return(specialYearPrefixes == null ? yearTimex : specialYearPrefixes + yearTimex);
        }
Example #13
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.ToLower();
                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"
            match = this.config.SpecialDayRegex.Match(trimmedText);
            if (match.Success && match.Index == 0 && match.Length == trimmedText.Length)
            {
                var swift = this.config.GetSwiftDay(match.Value);

                var value = referenceDate.AddDays(swift);

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

                return(ret);
            }

            // handle "two days from tomorrow"
            match = this.config.SpecialDayWithNumRegex.Match(trimmedText);
            if (match.Success && match.Index == 0 && match.Length == trimmedText.Length)
            {
                var swift     = this.config.GetSwiftDay(match.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 = value;
                ret.Success     = true;

                return(ret);
            }

            // handle "two sundays from now"
            match = this.config.RelativeWeekDayRegex.Match(trimmedText);
            if (match.Success && match.Index == 0 && match.Length == trimmedText.Length)
            {
                var numErs     = this.config.IntegerExtractor.Extract(trimmedText);
                var num        = Convert.ToInt32((double)(this.config.NumberParser.Parse(numErs[0]).Value ?? 0));
                var weekdayStr = match.Groups["weekday"].Value.ToLower();
                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 = value;
                ret.Success     = true;

                return(ret);
            }

            // handle "next Sunday"
            match = this.config.NextRegex.Match(trimmedText);
            if (match.Success && match.Index == 0 && match.Length == trimmedText.Length)
            {
                var weekdayStr = match.Groups["weekday"].Value.ToLower();
                var value      = referenceDate.Next((DayOfWeek)this.config.DayOfWeek[weekdayStr]);

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

                return(ret);
            }

            // handle "this Friday"
            match = this.config.ThisRegex.Match(trimmedText);
            if (match.Success && match.Index == 0 && match.Length == trimmedText.Length)
            {
                var weekdayStr = match.Groups["weekday"].Value.ToLower();
                var value      = referenceDate.This((DayOfWeek)this.config.DayOfWeek[weekdayStr]);

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

                return(ret);
            }

            // handle "last Friday", "last mon"
            match = this.config.LastRegex.Match(trimmedText);
            if (match.Success && match.Index == 0 && match.Length == trimmedText.Length)
            {
                var weekdayStr = match.Groups["weekday"].Value.ToLower();
                var value      = referenceDate.Last((DayOfWeek)this.config.DayOfWeek[weekdayStr]);

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

                return(ret);
            }

            // handle "Friday"
            match = this.config.WeekDayRegex.Match(trimmedText);
            if (match.Success && match.Index == 0 && match.Length == trimmedText.Length)
            {
                var weekdayStr = match.Groups["weekday"].Value.ToLower();
                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 = futureDate;
                ret.PastValue   = pastDate;
                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.ToLower();

                // 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 erTmp = 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(erTmp).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);
            }

            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, CultureInfo.InvariantCulture);
                }

                hourStr = hourGroup.Captures[1].Value;

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

                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, CultureInfo.InvariantCulture);
                    }
                    else if (minuteCapture.Index >= time2StartIndex && minuteCapture.Index + minuteCapture.Length <= time2EndIndex)
                    {
                        endMinute = int.Parse(minuteCapture.Value, CultureInfo.InvariantCulture);
                    }
                }

                // 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, CultureInfo.InvariantCulture);
                    }
                    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.StartsWith("a", StringComparison.Ordinal);
                var hasLeftPm  = !string.IsNullOrEmpty(leftDesc) && leftDesc.StartsWith("p", StringComparison.Ordinal);
                var hasRightAm = !string.IsNullOrEmpty(rightDesc) && rightDesc.StartsWith("a", StringComparison.Ordinal);
                var hasRightPm = !string.IsNullOrEmpty(rightDesc) && rightDesc.StartsWith("p", StringComparison.Ordinal);
                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);
        }
        // Parse combined patterns Duration + Date, e.g. '3 days before Monday', '4 weeks after January 15th'
        private DateTimeResolutionResult ParseDurationWithDate(string text, DateObject referenceDate)
        {
            var ret         = new DateTimeResolutionResult();
            var durationRes = config.DurationExtractor.Extract(text, referenceDate);

            foreach (var duration in durationRes)
            {
                var matches = config.UnitRegex.Matches(duration.Text);
                if (matches.Count > 0)
                {
                    var afterStr = text.Substring((int)duration.Start + (int)duration.Length);

                    // Check if the Duration entity is followed by "before|from|after"
                    var connector = config.BeforeAfterRegex.MatchBegin(afterStr, trim: true);
                    if (connector.Success)
                    {
                        // Parse Duration
                        var pr = config.DurationParser.Parse(duration, referenceDate);

                        // Parse Date
                        if (pr.Value != null)
                        {
                            var dateString  = afterStr.Substring(connector.Index + connector.Length).Trim();
                            var innerResult = ParseBasicRegexMatch(dateString, referenceDate);
                            if (!innerResult.Success)
                            {
                                innerResult = ParseImplicitDate(dateString, referenceDate);
                            }

                            if (!innerResult.Success)
                            {
                                innerResult = ParseWeekdayOfMonth(dateString, referenceDate);
                            }

                            if (!innerResult.Success)
                            {
                                innerResult = ParseNumberWithMonth(dateString, referenceDate);
                            }

                            if (!innerResult.Success)
                            {
                                innerResult = ParseSingleNumber(dateString, referenceDate);
                            }

                            if (!innerResult.Success)
                            {
                                var holidayEr = new ExtractResult
                                {
                                    Start    = 0,
                                    Length   = dateString.Length,
                                    Text     = dateString,
                                    Type     = Constants.SYS_DATETIME_DATE,
                                    Data     = null,
                                    Metadata = new Metadata {
                                        IsHoliday = true
                                    },
                                };
                                innerResult = (DateTimeResolutionResult)config.HolidayParser.Parse(holidayEr, referenceDate).Value;
                            }

                            // Combine parsed results Duration + Date
                            if (innerResult.Success)
                            {
                                var        isFuture       = connector.Groups["after"].Success ? true : false;
                                DateObject date           = (DateObject)innerResult.FutureValue;
                                var        resultDateTime = DurationParsingUtil.ShiftDateTime(pr.TimexStr, date, future: isFuture);
                                ret.Timex = $"{DateTimeFormatUtil.LuisDate(resultDateTime)}";

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

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

                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);
        }
        // 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();
            int month = 0, day = 0, year = 0;

            var monthStr         = match.Groups["month"].Value;
            var dayStr           = match.Groups["day"].Value;
            var weekdayStr       = match.Groups["weekday"].Value;
            var yearStr          = match.Groups["year"].Value;
            var writtenYear      = match.Groups["fullyear"].Value;
            var ambiguousCentury = false;

            if (this.config.MonthOfYear.ContainsKey(monthStr) && this.config.DayOfMonth.ContainsKey(dayStr))
            {
                month = this.config.MonthOfYear[monthStr];
                day   = this.config.DayOfMonth[dayStr];

                if (!string.IsNullOrEmpty(writtenYear))
                {
                    year = this.config.DateExtractor.GetYearFromText(match);
                }
                else if (!string.IsNullOrEmpty(yearStr))
                {
                    year = int.Parse(yearStr, CultureInfo.InvariantCulture);
                    if (year < 100 && year >= Constants.MinTwoDigitYearPastNum)
                    {
                        year += Constants.BASE_YEAR_PAST_CENTURY;
                    }
                    else if (year >= 0 && year < Constants.MaxTwoDigitYearFutureNum)
                    {
                        year += Constants.BASE_YEAR_CURRENT_CENTURY;
                    }
                    else if (year >= Constants.MaxTwoDigitYearFutureNum && year < Constants.MinTwoDigitYearPastNum)
                    {
                        // Two-digit years in the range [30, 40) are ambiguos
                        ambiguousCentury = true;
                    }
                }
            }

            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 futurePastDates = DateContext.GenerateDates(noYear, referenceDate, year, month, day);

            ret.FutureValue = futurePastDates.future;
            ret.PastValue   = futurePastDates.past;
            ret.Success     = true;

            // Ambiguous two-digit years are assigned values in both centuries (e.g. 35 -> 1935, 2035)
            if (ambiguousCentury)
            {
                ret.PastValue   = futurePastDates.past.AddYears(Constants.BASE_YEAR_PAST_CENTURY);
                ret.FutureValue = futurePastDates.future.AddYears(Constants.BASE_YEAR_CURRENT_CENTURY);
                ret.Timex       = TimexUtility.ModifyAmbiguousCenturyTimex(ret.Timex);
            }

            return(ret);
        }
Example #18
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();
            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 special case: night ends with 23:59 due to C# issues.
                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.Equals(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);
        }
Example #19
0
        public DateTimeParseResult Parse(ExtractResult er, DateObject reference)
        {
            var referenceDate = reference;

            object value = null;

            if (er.Type.Equals(ParserName, StringComparison.Ordinal))
            {
                var innerResult = ParseBasicRegexMatch(er.Text, referenceDate);
                if (!innerResult.Success)
                {
                    innerResult = ParseImplicitDate(er.Text, referenceDate);
                }

                if (!innerResult.Success)
                {
                    innerResult = ParseWeekdayOfMonth(er.Text, referenceDate);
                }

                if (!innerResult.Success)
                {
                    innerResult = ParseDurationWithAgoAndLater(er.Text, referenceDate);
                }

                // NumberWithMonth must be the second last one, because it only need to find a number and a month to get a "success"
                if (!innerResult.Success)
                {
                    innerResult = ParseNumberWithMonth(er.Text, referenceDate);
                }

                // SingleNumber last one
                if (!innerResult.Success)
                {
                    innerResult = ParseSingleNumber(er.Text, referenceDate);
                }

                if (innerResult.Success)
                {
                    innerResult.FutureResolution = new Dictionary <string, string>
                    {
                        { TimeTypeConstants.DATE, DateTimeFormatUtil.FormatDate((DateObject)innerResult.FutureValue) },
                    };

                    innerResult.PastResolution = new Dictionary <string, string>
                    {
                        { TimeTypeConstants.DATE, DateTimeFormatUtil.FormatDate((DateObject)innerResult.PastValue) },
                    };

                    value = innerResult;
                }
            }

            var ret = new DateTimeParseResult
            {
                Text          = er.Text,
                Start         = er.Start,
                Length        = er.Length,
                Type          = er.Type,
                Data          = er.Data,
                Value         = value,
                TimexStr      = value == null ? string.Empty : ((DateTimeResolutionResult)value).Timex,
                ResolutionStr = string.Empty,
            };

            return(ret);
        }
Example #20
0
        public DateTimeParseResult Parse(ExtractResult er, DateObject refTime)
        {
            var referenceTime = refTime;

            object value = null;

            if (er.Type.Equals(ParserName))
            {
                var innerResult = InternalParse(er.Text, referenceTime);

                // Handling timeZone
                if (innerResult.Success && TimeZoneUtility.ShouldResolveTimeZone(er, this.Config.Options))
                {
                    var metadata   = er.Data as Dictionary <string, object>;
                    var timezoneEr = metadata[Constants.SYS_DATETIME_TIMEZONE] as ExtractResult;
                    var timezonePr = this.Config.TimeZoneParser.Parse(timezoneEr);
                    if (timezonePr != null && timezonePr.Value != null)
                    {
                        innerResult.TimeZoneResolution = ((DateTimeResolutionResult)timezonePr.Value).TimeZoneResolution;
                    }
                }

                if (innerResult.Success)
                {
                    if (!IsBeforeOrAfterMod(innerResult.Mod))
                    {
                        innerResult.FutureResolution = new Dictionary <string, string>
                        {
                            {
                                TimeTypeConstants.START_DATETIME,
                                DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)innerResult.FutureValue).Item1)
                            },
                            {
                                TimeTypeConstants.END_DATETIME,
                                DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)innerResult.FutureValue).Item2)
                            },
                        };

                        innerResult.PastResolution = new Dictionary <string, string>
                        {
                            {
                                TimeTypeConstants.START_DATETIME,
                                DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)innerResult.PastValue).Item1)
                            },
                            {
                                TimeTypeConstants.END_DATETIME,
                                DateTimeFormatUtil.FormatDateTime(((Tuple <DateObject, DateObject>)innerResult.PastValue).Item2)
                            },
                        };
                    }
                    else
                    {
                        if (innerResult.Mod == Constants.AFTER_MOD)
                        {
                            // Cases like "1/1/2015 after 2:00" there is no EndTime
                            innerResult.FutureResolution = new Dictionary <string, string>
                            {
                                {
                                    TimeTypeConstants.START_DATETIME,
                                    DateTimeFormatUtil.FormatDateTime((DateObject)innerResult.FutureValue)
                                },
                            };

                            innerResult.PastResolution = new Dictionary <string, string>
                            {
                                {
                                    TimeTypeConstants.START_DATETIME,
                                    DateTimeFormatUtil.FormatDateTime((DateObject)innerResult.PastValue)
                                },
                            };
                        }
                        else
                        {
                            // Cases like "1/1/2015 before 5:00 in the afternoon" there is no StartTime
                            innerResult.FutureResolution = new Dictionary <string, string>
                            {
                                {
                                    TimeTypeConstants.END_DATETIME,
                                    DateTimeFormatUtil.FormatDateTime((DateObject)innerResult.FutureValue)
                                },
                            };

                            innerResult.PastResolution = new Dictionary <string, string>
                            {
                                {
                                    TimeTypeConstants.END_DATETIME,
                                    DateTimeFormatUtil.FormatDateTime((DateObject)innerResult.PastValue)
                                },
                            };
                        }
                    }

                    value = innerResult;
                }
            }

            var ret = new DateTimeParseResult
            {
                Text          = er.Text,
                Start         = er.Start,
                Length        = er.Length,
                Type          = er.Type,
                Data          = er.Data,
                Value         = value,
                TimexStr      = value == null ? string.Empty : ((DateTimeResolutionResult)value).Timex,
                ResolutionStr = string.Empty,
            };

            return(ret);
        }
Example #21
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();
            int month = 0, day = 0, year = 0;

            var monthStr    = match.Groups["month"].Value;
            var dayStr      = match.Groups["day"].Value;
            var weekdayStr  = match.Groups["weekday"].Value;
            var yearStr     = match.Groups["year"].Value;
            var writtenYear = match.Groups["fullyear"].Value;

            if (this.config.MonthOfYear.ContainsKey(monthStr) && this.config.DayOfMonth.ContainsKey(dayStr))
            {
                month = this.config.MonthOfYear[monthStr];
                day   = this.config.DayOfMonth[dayStr];

                if (!string.IsNullOrEmpty(writtenYear))
                {
                    year = this.config.DateExtractor.GetYearFromText(match);
                }
                else if (!string.IsNullOrEmpty(yearStr))
                {
                    year = int.Parse(yearStr, CultureInfo.InvariantCulture);
                    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 #22
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();

            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);
        }
        // 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 #24
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);
        }
Example #25
0
        internal static void ResolveAmpm(Dictionary <string, object> resolutionDic, string keyName)
        {
            if (resolutionDic.ContainsKey(keyName))
            {
                var resolution   = (Dictionary <string, string>)resolutionDic[keyName];
                var resolutionPm = new Dictionary <string, string>();

                if (!resolutionDic.ContainsKey(DateTimeResolutionKey.Timex))
                {
                    return;
                }

                var timex = (string)resolutionDic[DateTimeResolutionKey.Timex];

                resolutionDic.Remove(keyName);
                resolutionDic.Add(keyName + "Am", resolution);

                switch ((string)resolutionDic[ResolutionKey.Type])
                {
                case Constants.SYS_DATETIME_TIME:
                    resolutionPm[ResolutionKey.Value]         = DateTimeFormatUtil.ToPm(resolution[ResolutionKey.Value]);
                    resolutionPm[DateTimeResolutionKey.Timex] = DateTimeFormatUtil.ToPm(timex);
                    break;

                case Constants.SYS_DATETIME_DATETIME:
                    var split = resolution[ResolutionKey.Value].Split(' ');
                    resolutionPm[ResolutionKey.Value]         = split[0] + " " + DateTimeFormatUtil.ToPm(split[1]);
                    resolutionPm[DateTimeResolutionKey.Timex] = DateTimeFormatUtil.AllStringToPm(timex);
                    break;

                case Constants.SYS_DATETIME_TIMEPERIOD:
                    if (resolution.ContainsKey(DateTimeResolutionKey.Start))
                    {
                        resolutionPm[DateTimeResolutionKey.Start] = DateTimeFormatUtil.ToPm(resolution[DateTimeResolutionKey.Start]);
                    }

                    if (resolution.ContainsKey(DateTimeResolutionKey.End))
                    {
                        resolutionPm[DateTimeResolutionKey.End] = DateTimeFormatUtil.ToPm(resolution[DateTimeResolutionKey.End]);
                    }

                    resolutionPm[DateTimeResolutionKey.Timex] = DateTimeFormatUtil.AllStringToPm(timex);
                    break;

                case Constants.SYS_DATETIME_DATETIMEPERIOD:
                    if (resolution.ContainsKey(DateTimeResolutionKey.Start))
                    {
                        var start = Convert.ToDateTime(resolution[DateTimeResolutionKey.Start], CultureInfo.InvariantCulture);
                        start = start.Hour == Constants.HalfDayHourCount ?
                                start.AddHours(-Constants.HalfDayHourCount) : start.AddHours(Constants.HalfDayHourCount);

                        resolutionPm[DateTimeResolutionKey.Start] = DateTimeFormatUtil.FormatDateTime(start);
                    }

                    if (resolution.ContainsKey(DateTimeResolutionKey.End))
                    {
                        var end = Convert.ToDateTime(resolution[DateTimeResolutionKey.End], CultureInfo.InvariantCulture);
                        end = end.Hour == Constants.HalfDayHourCount ?
                              end.AddHours(-Constants.HalfDayHourCount) : end.AddHours(Constants.HalfDayHourCount);

                        resolutionPm[DateTimeResolutionKey.End] = DateTimeFormatUtil.FormatDateTime(end);
                    }

                    resolutionPm[DateTimeResolutionKey.Timex] = DateTimeFormatUtil.AllStringToPm(timex);
                    break;
                }

                resolutionDic.Add(keyName + "Pm", resolutionPm);
            }
        }
Example #26
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();
                var afterStr  = text.Substring((pr.Start ?? 0) + (pr.Length ?? 0)).Trim();

                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);
        }
        protected DateTimeResolutionResult MatchWeekdayAndDay(string text, DateObject reference)
        {
            var ret = new DateTimeResolutionResult();

            // 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.
            var match = this.config.WeekDayAndDayRegex.Match(text);

            if (match.Success)
            {
                int month = reference.Month, year = reference.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 = ConvertCJKToNum(match.Groups["day"].Value);

                // 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 (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 < reference)
                        {
                            // 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 > reference)
                        {
                            // 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 = DateTimeFormatUtil.LuisDate(pastDate);
                    }
                }

                ret.Success = true;

                return(ret);
            }

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

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

            int    swift = 0;
            bool   isMatch = false, isLater = false;
            string dayStr = null;

            // Item2 is a label identifying the regex defined in Item1
            var agoLaterRegexTuples = new List <(Regex, string)>
            {
                (utilityConfiguration.AgoRegex, Constants.AGO_LABEL),
                (utilityConfiguration.LaterRegex, Constants.LATER_LABEL),
            };

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

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

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

                if (isMatch)
                {
                    break;
                }
            }

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

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

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

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

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

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

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

            return(ret);
        }
Example #29
0
        public SortedDictionary <string, object> DateTimeResolution(DateTimeParseResult slot)
        {
            if (slot == null)
            {
                return(null);
            }

            var resolutions = new List <Dictionary <string, string> >();
            var res         = new Dictionary <string, object>();

            var type  = slot.Type;
            var timex = slot.TimexStr;

            var val = (DateTimeResolutionResult)slot.Value;

            if (val == null)
            {
                return(null);
            }

            var    isLunar = val.IsLunar;
            var    mod     = val.Mod;
            string list    = null;

            // Resolve dates list for date periods
            if (slot.Type.Equals(Constants.SYS_DATETIME_DATEPERIOD) && val.List != null)
            {
                list = string.Join(",", val.List.Select(o => DateTimeFormatUtil.LuisDate((DateObject)o)).ToArray());
            }

            // With modifier, output Type might not be the same with type in resolution result
            // For example, if the resolution type is "date", with modifier the output type should be "daterange"
            var typeOutput = DetermineDateTimeType(slot.Type, hasMod: !string.IsNullOrEmpty(mod));
            var comment    = val.Comment;

            // The following should be added to res first, since ResolveAmPm requires these fields.
            AddResolutionFields(res, DateTimeResolutionKey.Timex, timex);
            AddResolutionFields(res, Constants.Comment, comment);
            AddResolutionFields(res, DateTimeResolutionKey.Mod, mod);
            AddResolutionFields(res, ResolutionKey.Type, typeOutput);
            AddResolutionFields(res, DateTimeResolutionKey.IsLunar, isLunar ? isLunar.ToString() : string.Empty);

            var hasTimeZone = false;

            // For standalone timezone entity recognition, we generate TimeZoneResolution for each entity we extracted.
            // We also merge time entity with timezone entity and add the information in TimeZoneResolution to every DateTime resolutions.
            if (val.TimeZoneResolution != null)
            {
                if (slot.Type.Equals(Constants.SYS_DATETIME_TIMEZONE))
                {
                    // single timezone
                    AddResolutionFields(res, Constants.ResolveTimeZone, new Dictionary <string, string>
                    {
                        { ResolutionKey.Value, val.TimeZoneResolution.Value },
                        { Constants.UtcOffsetMinsKey, val.TimeZoneResolution.UtcOffsetMins.ToString() },
                    });
                }
                else
                {
                    // timezone as clarification of datetime
                    hasTimeZone = true;
                    AddResolutionFields(res, Constants.TimeZone, val.TimeZoneResolution.Value);
                    AddResolutionFields(res, Constants.TimeZoneText, val.TimeZoneResolution.TimeZoneText);
                    AddResolutionFields(res, Constants.UtcOffsetMinsKey, val.TimeZoneResolution.UtcOffsetMins.ToString());
                }
            }

            var pastResolutionStr   = ((DateTimeResolutionResult)slot.Value).PastResolution;
            var futureResolutionStr = ((DateTimeResolutionResult)slot.Value).FutureResolution;

            if (typeOutput == Constants.SYS_DATETIME_DATETIMEALT && pastResolutionStr.Count > 0)
            {
                typeOutput = DetermineResolutionDateTimeType(pastResolutionStr);
            }

            var resolutionPast   = GenerateResolution(type, pastResolutionStr, mod);
            var resolutionFuture = GenerateResolution(type, futureResolutionStr, mod);

            // If past and future are same, keep only one
            if (resolutionFuture.OrderBy(t => t.Key).Select(t => t.Value)
                .SequenceEqual(resolutionPast.OrderBy(t => t.Key).Select(t => t.Value)))
            {
                if (resolutionPast.Count > 0)
                {
                    AddResolutionFields(res, Constants.Resolve, resolutionPast);
                }
            }
            else
            {
                if (resolutionPast.Count > 0)
                {
                    AddResolutionFields(res, Constants.ResolveToPast, resolutionPast);
                }

                if (resolutionFuture.Count > 0)
                {
                    AddResolutionFields(res, Constants.ResolveToFuture, resolutionFuture);
                }
            }

            // If 'ampm', double our resolution accordingly
            if (!string.IsNullOrEmpty(comment) && comment.Equals(Constants.Comment_AmPm))
            {
                if (res.ContainsKey(Constants.Resolve))
                {
                    ResolveAmpm(res, Constants.Resolve);
                }
                else
                {
                    ResolveAmpm(res, Constants.ResolveToPast);
                    ResolveAmpm(res, Constants.ResolveToFuture);
                }
            }

            // If WeekOf and in CalendarMode, modify the past part of our resolution
            if ((Config.Options & DateTimeOptions.CalendarMode) != 0 &&
                !string.IsNullOrEmpty(comment) && comment.Equals(Constants.Comment_WeekOf))
            {
                ResolveWeekOf(res, Constants.ResolveToPast);
            }

            foreach (var p in res)
            {
                if (p.Value is Dictionary <string, string> dictionary)
                {
                    var value = new Dictionary <string, string>();

                    AddResolutionFields(value, DateTimeResolutionKey.Timex, timex);
                    AddResolutionFields(value, DateTimeResolutionKey.Mod, mod);
                    AddResolutionFields(value, ResolutionKey.Type, typeOutput);
                    AddResolutionFields(value, DateTimeResolutionKey.IsLunar, isLunar ? isLunar.ToString() : string.Empty);
                    AddResolutionFields(value, DateTimeResolutionKey.List, list);

                    if (hasTimeZone)
                    {
                        AddResolutionFields(value, Constants.TimeZone, val.TimeZoneResolution.Value);
                        AddResolutionFields(value, Constants.TimeZoneText, val.TimeZoneResolution.TimeZoneText);
                        AddResolutionFields(value, Constants.UtcOffsetMinsKey, val.TimeZoneResolution.UtcOffsetMins.ToString());
                    }

                    foreach (var q in dictionary)
                    {
                        if (value.ContainsKey(q.Key))
                        {
                            value[q.Key] = q.Value;
                        }
                        else
                        {
                            value.Add(q.Key, q.Value);
                        }
                    }

                    resolutions.Add(value);
                }
            }

            if (resolutionPast.Count == 0 && resolutionFuture.Count == 0 && val.TimeZoneResolution == null)
            {
                var notResolved = new Dictionary <string, string>
                {
                    {
                        DateTimeResolutionKey.Timex, timex
                    },
                    {
                        ResolutionKey.Type, typeOutput
                    },
                    {
                        ResolutionKey.Value, "not resolved"
                    },
                };

                resolutions.Add(notResolved);
            }

            return(new SortedDictionary <string, object> {
                { ResolutionKey.ValueSet, resolutions }
            });
        }
        // Cases like "between 0730 to 0930", only this case is handled in this method
        private DateTimeResolutionResult ParsePureDigitNumCases(string text, DateObject referenceTime)
        {
            var ret = new DateTimeResolutionResult();
            int year = referenceTime.Year, month = referenceTime.Month, day = referenceTime.Day;
            var trimmedText = text.Trim();

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

            if (match.Success)
            {
                // get hours
                var hourGroup   = match.Groups[Constants.HourGroupName];
                var minuteGroup = match.Groups[Constants.MinuteGroupName];

                if (hourGroup.Captures.Count == 2 && minuteGroup.Captures.Count == 2)
                {
                    var beginHourEndIndex     = hourGroup.Captures[0].Index + hourGroup.Captures[0].Length;
                    var beginMinuteStartIndex = minuteGroup.Captures[0].Index;
                    var endHourEndIndex       = hourGroup.Captures[1].Index + hourGroup.Captures[1].Length;
                    var endMinuteStartIndex   = minuteGroup.Captures[1].Index;

                    // falls into the case "between 0730 to 0930"
                    if (beginHourEndIndex == beginMinuteStartIndex && endHourEndIndex == endMinuteStartIndex)
                    {
                        var startHourStr   = hourGroup.Captures[0].Value;
                        var startMinuteStr = minuteGroup.Captures[0].Value;
                        var endHourStr     = hourGroup.Captures[1].Value;
                        var endMinuteStr   = minuteGroup.Captures[1].Value;

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

                        if (!this.config.Numbers.TryGetValue(startMinuteStr, out int beginMinute))
                        {
                            beginMinute = int.Parse(startMinuteStr, CultureInfo.InvariantCulture);
                        }

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

                        if (!this.config.Numbers.TryGetValue(endMinuteStr, out int endMinute))
                        {
                            endMinute = int.Parse(endMinuteStr, CultureInfo.InvariantCulture);
                        }

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

                        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);
                        var endStr   = DateTimeFormatUtil.ShortTime(endDateTime.Hour, endMinute);

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

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

                        ret.Success = true;
                    }
                }
            }

            return(ret);
        }