// Judge if a date is valid private static bool IsValidDate(int year, int month, int day) { if (month < Constants.MinMonth) { year--; month = Constants.MaxMonth; } if (month > Constants.MaxMonth) { year++; month = Constants.MinMonth; } return(DateObjectExtension.IsValidDate(year, month, day)); }
// Check every integers and ordinal number for date private List <Token> NumberWithMonth(string text, DateObject reference) { var ret = new List <Token>(); var er = this.Config.OrdinalExtractor.Extract(text); er.AddRange(this.Config.IntegerExtractor.Extract(text)); foreach (var result in er) { int.TryParse((this.Config.NumberParser.Parse(result).Value ?? 0).ToString(), out int num); if (num < 1 || num > 31) { continue; } if (result.Start >= 0) { // Handling cases like '(Monday,) Jan twenty two' var frontStr = text.Substring(0, result.Start ?? 0); var match = this.Config.MonthEnd.Match(frontStr); if (match.Success) { var startIndex = match.Index; var endIndex = match.Index + match.Length + (result.Length ?? 0); ExtendWithWeekdayAndYear( ref startIndex, ref endIndex, Config.MonthOfYear.GetValueOrDefault(match.Groups["month"].Value, reference.Month), num, text, reference); ret.Add(new Token(startIndex, endIndex)); continue; } // Handling cases like 'for the 25th' var matches = this.Config.ForTheRegex.Matches(text); bool isFound = false; foreach (Match matchCase in matches) { if (matchCase.Success) { var ordinalNum = matchCase.Groups["DayOfMonth"].Value; if (ordinalNum == result.Text) { var endLength = 0; if (matchCase.Groups["end"].Value.Length > 0) { endLength = matchCase.Groups["end"].Value.Length; } ret.Add(new Token(matchCase.Index, matchCase.Index + matchCase.Length - endLength)); isFound = true; } } } if (isFound) { continue; } // Handling cases like 'Thursday the 21st', which both 'Thursday' and '21st' refer to a same date matches = this.Config.WeekDayAndDayOfMonthRegex.Matches(text); foreach (Match matchCase in matches) { if (matchCase.Success) { var ordinalNum = matchCase.Groups["DayOfMonth"].Value; if (ordinalNum == result.Text) { // Get week of day for the ordinal number which is regarded as a date of reference month var date = DateObject.MinValue.SafeCreateFromValue(reference.Year, reference.Month, num); var numWeekDayInt = (int)date.DayOfWeek; // Get week day from text directly, compare it with the weekday generated above // to see whether they refer to the same week day var extractedWeekDayStr = matchCase.Groups["weekday"].Value; // calculate matchLength considering that matchCase can preceed or follow result var matchLength = matchCase.Index < result.Start ? result.Start + result.Length - matchCase.Index : matchCase.Index + matchCase.Length - result.Start; if (!date.Equals(DateObject.MinValue) && numWeekDayInt == Config.DayOfWeek[extractedWeekDayStr] && matchCase.Length == matchLength) { if (matchCase.Index < result.Start) { ret.Add(new Token(matchCase.Index, result.Start + result.Length ?? 0)); } else { ret.Add(new Token((int)result.Start, matchCase.Index + matchCase.Length)); } isFound = true; } } } } if (isFound) { continue; } // Handling cases like 'Monday 21', which both 'Monday' and '21' refer to the same date // The year of expected date can be different to the year of referenceDate. matches = this.Config.WeekDayAndDayRegex.Matches(text); foreach (Match matchCase in matches) { if (matchCase.Success) { var matchLength = result.Start + result.Length - matchCase.Index; if (matchLength == matchCase.Length) { // check if day number is compatible with reference month if (DateObjectExtension.IsValidDate(reference.Year, reference.Month, num) || !this.Config.CheckBothBeforeAfter) { ret.Add(new Token(matchCase.Index, result.Start + result.Length ?? 0)); isFound = true; } } } } if (isFound) { continue; } // Handling cases like '20th of next month' var suffixStr = text.Substring(result.Start + result.Length ?? 0); var beginMatch = this.Config.RelativeMonthRegex.MatchBegin(suffixStr.Trim(), trim: true); if (beginMatch.Success && beginMatch.Index == 0) { var spaceLen = suffixStr.Length - suffixStr.Trim().Length; var resStart = result.Start; var resEnd = resStart + result.Length + spaceLen + beginMatch.Length; // Check if prefix contains 'the', include it if any var prefix = text.Substring(0, resStart ?? 0); var prefixMatch = this.Config.PrefixArticleRegex.Match(prefix); if (prefixMatch.Success) { resStart = prefixMatch.Index; } ret.Add(new Token(resStart ?? 0, resEnd ?? 0)); } // Handling cases like 'second Sunday' suffixStr = text.Substring(result.Start + result.Length ?? 0); beginMatch = this.Config.WeekDayRegex.MatchBegin(suffixStr.Trim(), trim: true); if (beginMatch.Success && num >= 1 && num <= 5 && result.Type.Equals(Number.Constants.SYS_NUM_ORDINAL, StringComparison.Ordinal)) { var weekDayStr = beginMatch.Groups["weekday"].Value; if (this.Config.DayOfWeek.ContainsKey(weekDayStr)) { var spaceLen = suffixStr.Length - suffixStr.Trim().Length; ret.Add(new Token(result.Start ?? 0, result.Start + result.Length + spaceLen + beginMatch.Length ?? 0)); } } } // For cases like "I'll go back twenty second of June" if (result.Start + result.Length < text.Length) { var afterStr = text.Substring(result.Start + result.Length ?? 0); var match = this.Config.OfMonth.Match(afterStr); if (match.Success) { var startIndex = result.Start ?? 0; var endIndex = (result.Start + result.Length ?? 0) + match.Length; ExtendWithWeekdayAndYear(ref startIndex, ref endIndex, Config.MonthOfYear.GetValueOrDefault(match.Groups["month"].Value, reference.Month), num, text, reference); ret.Add(new Token(startIndex, endIndex)); } } } return(ret); }
// 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); var isWithin = this.config.DurationRelativeDurationUnitRegex.MatchEnd(text, trim: true).Groups[Constants.WithinGroupName].Success; if ((exactMatch.Success || isWithin) && 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); }
// 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); }
// 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" var exactMatch = this.config.SpecialDayRegex.MatchExact(trimmedText, trim: true); if (exactMatch.Success) { var swift = GetSwiftDay(exactMatch.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" 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 = value; 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.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", "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.ToLower(); var value = referenceDate.Next((DayOfWeek)this.config.DayOfWeek[weekdayStr]); if (this.config.UpcomingPrefixRegex.MatchBegin(trimmedText, trim: true).Success) { value = referenceDate.Upcoming((DayOfWeek)this.config.DayOfWeek[weekdayStr]); } ret.Timex = DateTimeFormatUtil.LuisDate(value); ret.FutureValue = ret.PastValue = value; 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.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" // 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.ToLower(); 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 = value; ret.Success = true; return(ret); } // Handle "Friday" exactMatch = this.config.WeekDayRegex.MatchExact(trimmedText, trim: true); if (exactMatch.Success) { var weekdayStr = exactMatch.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 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.ToLower(); 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 = DateObjectExtension.SafeCreateFromValue(futureDate, 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 = DateObjectExtension.SafeCreateFromValue(pastDate, 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); }