// Check every integers and ordinal number for date private List <Token> NumberWithMonth(string text, DateObject reference) { var ret = new List <Token>(); var er = this.Config.OrdinalExtractor.Extract(text); er.AddRange(this.Config.IntegerExtractor.Extract(text)); foreach (var result in er) { var parsed = int.TryParse((this.Config.NumberParser.Parse(result).Value ?? 0).ToString(), out int num); if (!parsed || (num < 1 || num > 31)) { continue; } if (result.Start >= 0) { // Handling cases like '(Monday,) Jan twenty two' var prefixStr = text.Substring(0, result.Start ?? 0); // Check that the extracted number is not part of a decimal number, time expression or currency // (e.g. '123.24', '12:24', '$12') if (MatchingUtil.IsInvalidDayNumberPrefix(prefixStr)) { continue; } var match = this.Config.MonthEnd.Match(prefixStr); if (match.Success) { var startIndex = match.Index; var endIndex = match.Index + match.Length + (result.Length ?? 0); ExtendWithWeekdayAndYear( ref startIndex, ref endIndex, Config.MonthOfYear.GetValueOrDefault(match.Groups["month"].Value, reference.Month), num, text, reference); ret.Add(new Token(startIndex, endIndex)); continue; } // Handling cases like 'for the 25th' var matches = this.Config.ForTheRegex.Matches(text); bool isFound = false; foreach (Match matchCase in matches) { if (matchCase.Success) { var ordinalNum = matchCase.Groups["DayOfMonth"].Value; if (ordinalNum == result.Text) { var endLength = 0; if (matchCase.Groups["end"].Value.Length > 0) { endLength = matchCase.Groups["end"].Value.Length; } ret.Add(new Token(matchCase.Index, matchCase.Index + matchCase.Length - endLength)); isFound = true; } } } if (isFound) { continue; } // Handling cases like 'Thursday the 21st', which both 'Thursday' and '21st' refer to a same date matches = this.Config.WeekDayAndDayOfMonthRegex.Matches(text); foreach (Match matchCase in matches) { if (matchCase.Success) { var ordinalNum = matchCase.Groups["DayOfMonth"].Value; if (ordinalNum == result.Text && matchCase.Groups["DayOfMonth"].Index == result.Start) { // Get week of day for the ordinal number which is regarded as a date of reference month var date = DateObject.MinValue.SafeCreateFromValue(reference.Year, reference.Month, num); var numWeekDayInt = (int)date.DayOfWeek; // Get week day from text directly, compare it with the weekday generated above // to see whether they refer to the same week day var extractedWeekDayStr = matchCase.Groups["weekday"].Value; if (!date.Equals(DateObject.MinValue) && numWeekDayInt == Config.DayOfWeek[extractedWeekDayStr]) { ret.Add(new Token(matchCase.Index, matchCase.Index + matchCase.Length)); isFound = true; } } } } if (isFound) { continue; } // Handling cases like 'Monday 21', which both 'Monday' and '21' refer to the same date // The year of expected date can be different to the year of referenceDate. matches = this.Config.WeekDayAndDayRegex.Matches(text); foreach (Match matchCase in matches) { if (matchCase.Success) { var matchLength = result.Start + result.Length - matchCase.Index; if (matchLength == matchCase.Length) { // check if day number is compatible with reference month if (DateObjectExtension.IsValidDate(reference.Year, reference.Month, num) || !this.Config.CheckBothBeforeAfter) { ret.Add(new Token(matchCase.Index, result.Start + result.Length ?? 0)); isFound = true; } } } } if (isFound) { continue; } // Handling cases like '20th of next month' var suffixStr = text.Substring(result.Start + result.Length ?? 0); var beginMatch = this.Config.RelativeMonthRegex.MatchBegin(suffixStr.Trim(), trim: true); if (beginMatch.Success && beginMatch.Index == 0) { var spaceLen = suffixStr.Length - suffixStr.Trim().Length; var resStart = result.Start; var resEnd = resStart + result.Length + spaceLen + beginMatch.Length; // Check if prefix contains 'the', include it if any var prefix = text.Substring(0, resStart ?? 0); var prefixMatch = this.Config.PrefixArticleRegex.Match(prefix); if (prefixMatch.Success) { resStart = prefixMatch.Index; } ret.Add(new Token(resStart ?? 0, resEnd ?? 0)); } // Handling cases like 'second Sunday' suffixStr = text.Substring(result.Start + result.Length ?? 0); beginMatch = this.Config.WeekDayRegex.MatchBegin(suffixStr.Trim(), trim: true); if (beginMatch.Success && num >= 1 && num <= 5 && result.Type.Equals(Number.Constants.SYS_NUM_ORDINAL, StringComparison.Ordinal) && !this.Config.WeekDayRegex.IsExactMatch(result.Text, trim: true)) { var weekDayStr = beginMatch.Groups["weekday"].Value; if (this.Config.DayOfWeek.ContainsKey(weekDayStr)) { var spaceLen = suffixStr.Length - suffixStr.Trim().Length; ret.Add(new Token(result.Start ?? 0, result.Start + result.Length + spaceLen + beginMatch.Length ?? 0)); } } } // For cases like "I'll go back twenty second of June" if (result.Start + result.Length < text.Length) { var afterStr = text.Substring(result.Start + result.Length ?? 0); var match = this.Config.OfMonth.Match(afterStr); if (match.Success) { var startIndex = result.Start ?? 0; var endIndex = (result.Start + result.Length ?? 0) + match.Length; ExtendWithWeekdayAndYear(ref startIndex, ref endIndex, Config.MonthOfYear.GetValueOrDefault(match.Groups["month"].Value, reference.Month), num, text, reference); ret.Add(new Token(startIndex, endIndex)); } } } return(ret); }