Skip to content

Commit

Permalink
[EN DateTimeV2] Support [duration]+starting+[datetime] pattern as dat…
Browse files Browse the repository at this point in the history
…e range or date time range (#3178)

* DateTimeForLongerSpanForStarting - Initial implemention

* Support duration starting datetime pattern - initial commit

* Support duration starting datetime pattern - update test cases not supported attribute

---------

Co-authored-by: Michael Wang (Centific Technologies Inc) <[email protected]>
  • Loading branch information
MichaelMWW and Michael Wang (Centific Technologies Inc) authored Nov 12, 2024
1 parent 18fe661 commit 66901d1
Show file tree
Hide file tree
Showing 16 changed files with 427 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ public static class DateTimeDefinitions
public const string AroundRegex = @"(?:\b(?:around|circa)\s*?\b)(\s+the)?";
public static readonly string BeforeRegex = $@"((\b{InclusiveModPrepositions}?(?:before|in\s+advance\s+of|prior\s+to|(no\s+later|earlier|sooner)\s+than|ending\s+(with|on)|by|(un)?till?|(?<include>as\s+late\s+as)){InclusiveModPrepositions}?\b\s*?)|(?<!\w|>)((?<include><\s*=)|<))(\s+the)?";
public static readonly string AfterRegex = $@"((\b{InclusiveModPrepositions}?((after(\s+on)?(?!\sfrom)|(?<!no\s+)later\s+than)|((year\s+)?greater\s+than))(?!\s+or\s+equal\s+to){InclusiveModPrepositions}?\b\s*?)|(?<!\w|<)((?<include>>\s*=)|>))(\s+the)?";
public const string StartingRegex = @"(starting|beginning)(\s+)?(?:from|on|with)?";
public const string SinceRegex = @"(?:(?:\b(?:since|after\s+or\s+equal\s+to|(starting|beginning)(\s)?(?:from|on|with)?|as\s+early\s+as|(any\s+time\s+)from)\b\s*?)|(?<!\w|<)(>=))(\s+the)?";
public static readonly string SinceRegexExp = $@"({SinceRegex}|\bfrom(\s+the)?\b)";
public const string AgoRegex = @"\b(ago|earlier|before\s+(?<day>yesterday|today))\b";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ public class EnglishDatePeriodExtractorConfiguration : BaseDateTimeOptionsConfig
public static readonly Regex OfYearRegex =
new Regex(DateTimeDefinitions.OfYearRegex, RegexFlags, RegexTimeOut);

public static readonly Regex StartingRegex =
new Regex(DateTimeDefinitions.StartingRegex, RegexFlags, RegexTimeOut);

private const RegexOptions RegexFlags = RegexOptions.Singleline | RegexOptions.ExplicitCapture;

private static readonly Regex FromTokenRegex =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ public class EnglishDateTimePeriodExtractorConfiguration : BaseDateTimeOptionsCo
public static readonly Regex TasksmodeMealTimeofDayRegex =
new Regex(DateTimeDefinitions.TasksmodeMealTimeofDayRegex, RegexFlags, RegexTimeOut);

public static readonly Regex StartingRegex =
new Regex(DateTimeDefinitions.StartingRegex, RegexFlags, RegexTimeOut);

private const RegexOptions RegexFlags = RegexOptions.Singleline | RegexOptions.ExplicitCapture;

private static readonly Regex[] SimpleCases =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public EnglishDatePeriodParserConfiguration(ICommonDateTimeParserConfiguration c
NowRegex = NowParseRegex;
SpecialDayRegex = EnglishDateExtractorConfiguration.SpecialDayRegex;
TodayNowRegex = new Regex(DateTimeDefinitions.TodayNowRegex, RegexOptions.Singleline, RegexTimeOut);
StartingRegex = EnglishDatePeriodExtractorConfiguration.StartingRegex;

UnitMap = config.UnitMap;
CardinalMap = config.CardinalMap;
Expand Down Expand Up @@ -227,6 +228,8 @@ public EnglishDatePeriodParserConfiguration(ICommonDateTimeParserConfiguration c

public Regex OfYearRegex { get; }

public Regex StartingRegex { get; }

Regex ISimpleDatePeriodParserConfiguration.RelativeRegex => RelativeRegex;

Regex IDatePeriodParserConfiguration.NextPrefixRegex => NextPrefixRegex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public EnglishDateTimePeriodParserConfiguration(ICommonDateTimeParserConfigurati
AfterRegex = EnglishDateTimePeriodExtractorConfiguration.AfterRegex;
UnitMap = config.UnitMap;
Numbers = config.Numbers;
StartingRegex = EnglishDateTimePeriodExtractorConfiguration.StartingRegex;

TasksmodeMealTimeofDayRegex = EnglishDateTimePeriodExtractorConfiguration.TasksmodeMealTimeofDayRegex;
}
Expand Down Expand Up @@ -143,6 +144,8 @@ public EnglishDateTimePeriodParserConfiguration(ICommonDateTimeParserConfigurati

public Regex TasksmodeMealTimeofDayRegex { get; }

public Regex StartingRegex { get; }

bool IDateTimePeriodParserConfiguration.CheckBothBeforeAfter => DateTimeDefinitions.CheckBothBeforeAfter;

public IImmutableDictionary<string, string> UnitMap { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,25 @@ private List<Token> SingleTimePointWithPatterns(string text, List<ExtractResult>
ret.AddRange(GetTokenForRegexMatching(beforeString, EnglishDatePeriodExtractorConfiguration.ForPrefixRegex, extractionResult, inPrefix: true));
}
}

// For cases like xx weeks/days starting (from) a date point
if (this.config as EnglishDatePeriodExtractorConfiguration != null)
{
var match = EnglishDatePeriodExtractorConfiguration.StartingRegex.MatchEnd(beforeString, true);
if (match.Success)
{
var durationERs = this.config.DurationExtractor.Extract(beforeString);
if (durationERs.Count >= 1)
{
var lastDuration = durationERs[durationERs.Count - 1];
string startingWord = beforeString.Substring(beforeString.LastIndexOf(lastDuration.Text, StringComparison.Ordinal) + lastDuration.Text.Length);
if (startingWord.Trim() == match.Value.Trim())
{
ret.Add(new Token(lastDuration.Start ?? 0, (extractionResult.Start ?? 0) + (extractionResult.Length ?? 0)));
}
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

using Microsoft.Recognizers.Text.DateTime.English;
using Microsoft.Recognizers.Text.Utilities;
using DateObject = System.DateTime;

Expand Down Expand Up @@ -52,6 +52,9 @@ public List<ExtractResult> Extract(string text, DateObject reference)
tokens.AddRange(MatchDateWithPeriodPrefix(text, reference, new List<ExtractResult>(dateErs)));
tokens.AddRange(MergeDateWithTimePeriodSuffix(text, new List<ExtractResult>(dateErs), new List<ExtractResult>(timeErs)));

// Extracting cases like [duration] starting [datetime]
tokens.AddRange(MatchStartingWithDuration(text, reference));

var ers = Token.MergeAllTokens(tokens, text, ExtractorName);

if ((this.config.Options & DateTimeOptions.EnablePreview) != 0)
Expand Down Expand Up @@ -820,5 +823,36 @@ private List<Token> MatchPureNumberCases(string text, Token tok, bool before)

return ret;
}

private List<Token> MatchStartingWithDuration(string text, DateObject reference)
{
var ret = new List<Token>();

if (this.config as EnglishDateTimePeriodExtractorConfiguration != null
&& EnglishDateTimePeriodExtractorConfiguration.StartingRegex.Match(text).Success)
{
var dateTimeERs = this.config.SingleDateTimeExtractor.Extract(text, reference);
foreach (var dateTimeER in dateTimeERs)
{
var beforeString = text.Substring(0, (int)dateTimeER.Start);
var match = EnglishDatePeriodExtractorConfiguration.StartingRegex.MatchEnd(beforeString, true);
if (match.Success)
{
var durationERs = this.config.DurationExtractor.Extract(beforeString);
if (durationERs.Count >= 1)
{
var lastDuration = durationERs[durationERs.Count - 1];
string startingWord = beforeString.Substring(beforeString.LastIndexOf(lastDuration.Text, StringComparison.Ordinal) + lastDuration.Text.Length);
if (startingWord.Trim() == match.Value.Trim())
{
ret.Add(new Token((int)lastDuration.Start, (int)dateTimeER.Start + (int)dateTimeER.Length));
}
}
}
}
}

return ret;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

using Microsoft.Recognizers.Text.Matcher;
using Microsoft.Recognizers.Text.Utilities;
using DateObject = System.DateTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,12 @@ private DateTimeResolutionResult ParseBaseDatePeriod(string text, DateObject ref
innerResult = ParseOneWordPeriod(text, referenceDate);
}

// Cases like "x weeks/days starting (from) today/12 sep etc."
if (!innerResult.Success)
{
innerResult = ParseStartingWithDuration(text, referenceDate);
}

if (!innerResult.Success)
{
innerResult = MergeTwoTimePoints(text, referenceDate);
Expand Down Expand Up @@ -686,6 +692,52 @@ private DateTimeResolutionResult ParseDatePointWithForPrefix(string text, DateOb
return ret;
}

// Only handle cases like "x weeks/days starting (from) today/tomorrow/some day"
private DateTimeResolutionResult ParseStartingWithDuration(string text, DateObject referenceDate)
{
var ret = new DateTimeResolutionResult();
var dateER = this.config.DateExtractor.Extract(text, referenceDate);
var enConfig = this.config as EnglishDatePeriodParserConfiguration;

if (enConfig != null && enConfig.StartingRegex.Match(text).Success && dateER.Count == 1)
{
var beforeString = text.Substring(0, (int)dateER[0].Start);

if (!string.IsNullOrEmpty(beforeString) && enConfig.StartingRegex.MatchEnd(beforeString, true).Success)
{
var pr = this.config.DateParser.Parse(dateER[0], referenceDate);
var durationER = this.config.DurationExtractor.Extract(beforeString, referenceDate);

if (durationER.Count == 1)
{
var duration = this.config.DurationParser.Parse(durationER[0]);
var durationInSeconds = (double)((DateTimeResolutionResult)duration.Value).PastValue;

DateObject startDate;
DateObject endDate;

startDate = (DateObject)((DateTimeResolutionResult)pr.Value).PastValue;
endDate = startDate.AddSeconds(durationInSeconds);

if (startDate != DateObject.MinValue)
{
var startLuisStr = DateTimeFormatUtil.LuisDate(startDate);
var endLuisStr = DateTimeFormatUtil.LuisDate(endDate);
var durationTimex = ((DateTimeResolutionResult)duration.Value).Timex;

ret.Timex = $"({startLuisStr},{endLuisStr},{durationTimex})";
ret.FutureValue = new Tuple<DateObject, DateObject>(startDate, endDate);
ret.PastValue = new Tuple<DateObject, DateObject>(startDate, endDate);
ret.SubDateTimeEntities = new List<object> { pr, duration };
ret.Success = true;
}
}
}
}

return ret;
}

private DateTimeResolutionResult ParseSingleTimePoint(string text, DateObject referenceDate, DateContext dateContext = null)
{
var ret = new DateTimeResolutionResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Recognizers.Text.DateTime.English;
using Microsoft.Recognizers.Text.Utilities;
using DateObject = System.DateTime;

Expand Down Expand Up @@ -170,6 +171,12 @@ protected DateTimeResolutionResult InternalParse(string entityText, DateObject r
innerResult = ParseDateWithTimePeriodSuffix(entityText, referenceTime);
}

if (!innerResult.Success)
{
// Parsing cases like [duration] starting [datetime]
innerResult = ParseStartingWithDuration(entityText, referenceTime);
}

if (!innerResult.Success)
{
innerResult = ParseDuration(entityText, referenceTime);
Expand Down Expand Up @@ -1378,6 +1385,51 @@ private DateTimeResolutionResult ParseDuration(string text, DateObject reference
return ret;
}

private DateTimeResolutionResult ParseStartingWithDuration(string text, DateObject referenceTime)
{
var ret = new DateTimeResolutionResult();
var datetimeERs = Config.DateTimeExtractor.Extract(text, referenceTime);
var enConfig = Config as EnglishDateTimePeriodParserConfiguration;

if (enConfig != null && enConfig.StartingRegex.Match(text).Success && datetimeERs.Count == 1)
{
var beforeString = text.Substring(0, (int)datetimeERs[0].Start);

if (!string.IsNullOrEmpty(beforeString) && enConfig.StartingRegex.MatchEnd(beforeString, true).Success)
{
var pr = Config.DateTimeParser.Parse(datetimeERs[0], referenceTime);
var durationERs = Config.DurationExtractor.Extract(beforeString, referenceTime);

if (durationERs.Count == 1)
{
var duration = Config.DurationParser.Parse(durationERs[0]);
var durationInSeconds = (double)((DateTimeResolutionResult)duration.Value).PastValue;

DateObject startDate;
DateObject endDate;

startDate = (DateObject)((DateTimeResolutionResult)pr.Value).PastValue;
endDate = startDate.AddSeconds(durationInSeconds);

if (startDate != DateObject.MinValue)
{
var startLuisStr = $"{DateTimeFormatUtil.LuisDate(startDate)}{DateTimeFormatUtil.ShortTime(startDate.Hour, startDate.Minute, startDate.Second)}";
var endLuisStr = $"{DateTimeFormatUtil.LuisDate(endDate)}{DateTimeFormatUtil.ShortTime(endDate.Hour, endDate.Minute, endDate.Second)}";
var durationTimex = ((DateTimeResolutionResult)duration.Value).Timex;

ret.Timex = $"({startLuisStr},{endLuisStr},{durationTimex})";
ret.FutureValue = new Tuple<DateObject, DateObject>(startDate, endDate);
ret.PastValue = new Tuple<DateObject, DateObject>(startDate, endDate);
ret.SubDateTimeEntities = new List<object> { pr, duration };
ret.Success = true;
}
}
}
}

return ret;
}

// Parse "last minute", "next hour"
private DateTimeResolutionResult ParseRelativeUnit(string text, DateObject referenceTime)
{
Expand Down
2 changes: 2 additions & 0 deletions Patterns/English/English-DateTime.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,8 @@ BeforeRegex: !nestedRegex
AfterRegex: !nestedRegex
def: ((\b{InclusiveModPrepositions}?((after(\s+on)?(?!\sfrom)|(?<!no\s+)later\s+than)|((year\s+)?greater\s+than))(?!\s+or\s+equal\s+to){InclusiveModPrepositions}?\b\s*?)|(?<!\w|<)((?<include>>\s*=)|>))(\s+the)?
references: [ InclusiveModPrepositions ]
StartingRegex: !simpleRegex
def: (starting|beginning)(\s+)?(?:from|on|with)?
SinceRegex: !simpleRegex
def: (?:(?:\b(?:since|after\s+or\s+equal\s+to|(starting|beginning)(\s)?(?:from|on|with)?|as\s+early\s+as|(any\s+time\s+)from)\b\s*?)|(?<!\w|<)(>=))(\s+the)?
SinceRegexExp: !nestedRegex
Expand Down
36 changes: 36 additions & 0 deletions Specs/DateTime/English/DatePeriodExtractor.json
Original file line number Diff line number Diff line change
Expand Up @@ -3897,6 +3897,42 @@
}
]
},
{
"Input": "Schedule Out of office replies for 3 days starting from next Monday",
"NotSupported": "java, javascript, python",
"Results": [
{
"Text": "3 days starting from next Monday",
"Type": "daterange",
"Start": 35,
"Length": 32
}
]
},
{
"Input": "set ooo for a week starting tomorrow",
"NotSupported": "java, javascript, python",
"Results": [
{
"Text": "a week starting tomorrow",
"Type": "daterange",
"Start": 12,
"Length": 24
}
]
},
{
"Input": "set ooo for 2 weeks starting May 20th",
"NotSupported": "java, javascript, python",
"Results": [
{
"Text": "2 weeks starting may 20th",
"Type": "daterange",
"Start": 12,
"Length": 25
}
]
},
{
"Input": "please schedule a meeting for the week starting on february 4",
"Results": [
Expand Down
Loading

0 comments on commit 66901d1

Please sign in to comment.