Skip to content

Commit cdb178a

Browse files
MichaelMWWMichael Wang (Centific Technologies Inc)
and
Michael Wang (Centific Technologies Inc)
authored
[EN DateTimeV2] Support for longer span date time (#3166)
* Datetime for longer span - local draft commit * DateTimeForLongerSpan - Implement for from --------- Co-authored-by: Michael Wang (Centific Technologies Inc) <[email protected]>
1 parent 47f6d25 commit cdb178a

File tree

10 files changed

+281
-3
lines changed

10 files changed

+281
-3
lines changed

.NET/Microsoft.Recognizers.Definitions.Common/English/DateTimeDefinitions.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,13 @@ public static class DateTimeDefinitions
260260
public static readonly string SinceRegexExp = $@"({SinceRegex}|\bfrom(\s+the)?\b)";
261261
public const string AgoRegex = @"\b(ago|earlier|before\s+(?<day>yesterday|today))\b";
262262
public static readonly string LaterRegex = $@"\b(?:later(?!((\s+in)?\s*{OneWordPeriodRegex})|(\s+{TimeOfDayRegex})|\s+than\b)|from now|(from|after)\s+(?<day>tomorrow|tmrw?|today))\b";
263-
public const string BeforeAfterRegex = @"\b((?<before>before)|(?<after>from|after))\b";
263+
public const string BeforeAfterRegex = @"(,?\s*)\b((?<before>before)|(?<after>from|after))\b";
264264
public static readonly string ModPrefixRegex = $@"\b({RelativeRegex}|{AroundRegex}|{BeforeRegex}|{AfterRegex}|{SinceRegex})\b";
265265
public static readonly string ModSuffixRegex = $@"\b({AgoRegex}|{LaterRegex}|{BeforeAfterRegex}|{FutureSuffixRegex}|{PastSuffixRegex})\b";
266266
public const string InConnectorRegex = @"\b(in)\b";
267267
public static readonly string SinceYearSuffixRegex = $@"(^\s*{SinceRegex}(\s*(the\s+)?year\s*)?{YearSuffix})";
268268
public static readonly string WithinNextPrefixRegex = $@"\b(within(\s+the)?(\s+(?<next>{NextPrefixRegex}))?)\b";
269+
public const string ForPrefixRegex = @"((?<forfrom>for.*from.*)|(?<from>\bfrom\b)|(?<for>\bfor\b))";
269270
public const string TodayNowRegex = @"\b(today|now|current (date|time))\b";
270271
public static readonly string MorningStartEndRegex = $@"(^(morning|{AmDescRegex}))|((morning|{AmDescRegex})$)";
271272
public static readonly string AfternoonStartEndRegex = $@"(^(afternoon|{PmDescRegex}))|((afternoon|{PmDescRegex})$)";

.NET/Microsoft.Recognizers.Text.DateTime/Constants.cs

+2
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ public static class Constants
245245
public const string StartGroupName = "start";
246246
public const string EndGroupName = "end";
247247
public const string WithinGroupName = "within";
248+
public const string ForGroupName = "for";
249+
public const string FromGroupName = "from";
248250
public const string NumberGroupName = "number";
249251
public const string OrdinalGroupName = "ordinal";
250252
public const string OrderGroupName = "order";

.NET/Microsoft.Recognizers.Text.DateTime/English/Extractors/EnglishDatePeriodExtractorConfiguration.cs

+3
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ public class EnglishDatePeriodExtractorConfiguration : BaseDateTimeOptionsConfig
124124
public static readonly Regex WithinNextPrefixRegex =
125125
new Regex(DateTimeDefinitions.WithinNextPrefixRegex, RegexFlags, RegexTimeOut);
126126

127+
public static readonly Regex ForPrefixRegex =
128+
new Regex(DateTimeDefinitions.ForPrefixRegex, RegexFlags, RegexTimeOut);
129+
127130
public static readonly Regex RestOfDateRegex =
128131
new Regex(DateTimeDefinitions.RestOfDateRegex, RegexFlags, RegexTimeOut);
129132

.NET/Microsoft.Recognizers.Text.DateTime/English/Parsers/EnglishDatePeriodParserConfiguration.cs

+3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public EnglishDatePeriodParserConfiguration(ICommonDateTimeParserConfiguration c
9595
RelativeDecadeRegex = EnglishDatePeriodExtractorConfiguration.RelativeDecadeRegex;
9696
InConnectorRegex = config.UtilityConfiguration.InConnectorRegex;
9797
WithinNextPrefixRegex = EnglishDatePeriodExtractorConfiguration.WithinNextPrefixRegex;
98+
ForPrefixRegex = EnglishDatePeriodExtractorConfiguration.ForPrefixRegex;
9899
ReferenceDatePeriodRegex = EnglishDatePeriodExtractorConfiguration.ReferenceDatePeriodRegex;
99100
AgoRegex = EnglishDatePeriodExtractorConfiguration.AgoRegex;
100101
LaterRegex = EnglishDatePeriodExtractorConfiguration.LaterRegex;
@@ -186,6 +187,8 @@ public EnglishDatePeriodParserConfiguration(ICommonDateTimeParserConfiguration c
186187

187188
public Regex WithinNextPrefixRegex { get; }
188189

190+
public Regex ForPrefixRegex { get; }
191+
189192
public Regex RestOfDateRegex { get; }
190193

191194
public Regex LaterEarlyPeriodRegex { get; }

.NET/Microsoft.Recognizers.Text.DateTime/Extractors/BaseDatePeriodExtractor.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
using System.Collections.Generic;
66
using System.Linq;
77
using System.Text.RegularExpressions;
8-
8+
using Microsoft.Recognizers.Text.DateTime.English;
99
using Microsoft.Recognizers.Text.InternalCache;
1010
using Microsoft.Recognizers.Text.Utilities;
1111
using DateObject = System.DateTime;
@@ -734,6 +734,18 @@ private List<Token> SingleTimePointWithPatterns(string text, List<ExtractResult>
734734
}
735735
}
736736
}
737+
738+
// For cases like "for 1 week from today", "for 3 days from 20th May" etc..
739+
if (EnglishDatePeriodExtractorConfiguration.ForPrefixRegex != null)
740+
{
741+
Match prefixMatchFor = EnglishDatePeriodExtractorConfiguration.ForPrefixRegex.Match(beforeString);
742+
Match datepointMatchFrom = EnglishDatePeriodExtractorConfiguration.ForPrefixRegex.Match(extractionResult.Text);
743+
if (prefixMatchFor.Success && prefixMatchFor.Groups[Constants.ForGroupName].Success
744+
&& datepointMatchFrom.Success && datepointMatchFrom.Groups[Constants.FromGroupName].Success)
745+
{
746+
ret.AddRange(GetTokenForRegexMatching(beforeString, EnglishDatePeriodExtractorConfiguration.ForPrefixRegex, extractionResult, inPrefix: true));
747+
}
748+
}
737749
}
738750
}
739751

.NET/Microsoft.Recognizers.Text.DateTime/Parsers/BaseDatePeriodParser.cs

+66
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Globalization;
77
using System.Linq;
88
using System.Text.RegularExpressions;
9+
using Microsoft.Recognizers.Text.DateTime.English;
910
using Microsoft.Recognizers.Text.DateTime.Utilities;
1011
using Microsoft.Recognizers.Text.Utilities;
1112
using DateObject = System.DateTime;
@@ -476,6 +477,12 @@ private DateTimeResolutionResult ParseBaseDatePeriod(string text, DateObject ref
476477
innerResult = ParseDatePointWithAgoAndLater(text, referenceDate);
477478
}
478479

480+
// Cases like "for x weeks/days from today/12 sep etc."
481+
if (!innerResult.Success)
482+
{
483+
innerResult = ParseDatePointWithForPrefix(text, referenceDate);
484+
}
485+
479486
// Parse duration should be at the end since it will extract "the last week" from "the last week of July"
480487
if (!innerResult.Success)
481488
{
@@ -620,6 +627,65 @@ private DateTimeResolutionResult ParseDatePointWithAgoAndLater(string text, Date
620627
return ret;
621628
}
622629

630+
// Only handle cases like "for x weeks/days from today/tomorrow/some day"
631+
private DateTimeResolutionResult ParseDatePointWithForPrefix(string text, DateObject referenceDate)
632+
{
633+
var ret = new DateTimeResolutionResult();
634+
var er = this.config.DateExtractor.Extract(text, referenceDate).FirstOrDefault();
635+
636+
if (er != null)
637+
{
638+
var beforeString = text.Substring(0, (int)er.Start);
639+
var isAgo = this.config.AgoRegex.Match(er.Text).Success;
640+
var config = this.config as EnglishDatePeriodParserConfiguration;
641+
642+
if (!string.IsNullOrEmpty(beforeString) && config != null)
643+
{
644+
var matchFor = config.ForPrefixRegex.Match(beforeString);
645+
646+
if (matchFor.Success && matchFor.Groups[Constants.ForGroupName].Success)
647+
{
648+
var pr = this.config.DateParser.Parse(er, referenceDate);
649+
var durationExtractionResult = this.config.DurationExtractor.Extract(er.Text, referenceDate).FirstOrDefault();
650+
651+
if (durationExtractionResult != null)
652+
{
653+
var duration = this.config.DurationParser.Parse(durationExtractionResult);
654+
var durationInSeconds = (double)((DateTimeResolutionResult)duration.Value).PastValue;
655+
656+
DateObject startDate;
657+
DateObject endDate;
658+
659+
if (isAgo)
660+
{
661+
startDate = (DateObject)((DateTimeResolutionResult)pr.Value).PastValue;
662+
endDate = startDate.AddSeconds(durationInSeconds);
663+
}
664+
else
665+
{
666+
endDate = (DateObject)((DateTimeResolutionResult)pr.Value).FutureValue;
667+
startDate = endDate.AddSeconds(-durationInSeconds);
668+
}
669+
670+
if (startDate != DateObject.MinValue)
671+
{
672+
var startLuisStr = DateTimeFormatUtil.LuisDate(startDate);
673+
var endLuisStr = DateTimeFormatUtil.LuisDate(endDate);
674+
var durationTimex = ((DateTimeResolutionResult)duration.Value).Timex;
675+
676+
ret.Timex = $"({startLuisStr},{endLuisStr},{durationTimex})";
677+
ret.FutureValue = new Tuple<DateObject, DateObject>(startDate, endDate);
678+
ret.PastValue = new Tuple<DateObject, DateObject>(startDate, endDate);
679+
ret.Success = true;
680+
}
681+
}
682+
}
683+
}
684+
}
685+
686+
return ret;
687+
}
688+
623689
private DateTimeResolutionResult ParseSingleTimePoint(string text, DateObject referenceDate, DateContext dateContext = null)
624690
{
625691
var ret = new DateTimeResolutionResult();

Patterns/English/English-DateTime.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ LaterRegex: !nestedRegex
624624
def: \b(?:later(?!((\s+in)?\s*{OneWordPeriodRegex})|(\s+{TimeOfDayRegex})|\s+than\b)|from now|(from|after)\s+(?<day>tomorrow|tmrw?|today))\b
625625
references: [ OneWordPeriodRegex, TimeOfDayRegex ]
626626
BeforeAfterRegex: !simpleRegex
627-
def: \b((?<before>before)|(?<after>from|after))\b
627+
def: (,?\s*)\b((?<before>before)|(?<after>from|after))\b
628628
ModPrefixRegex: !nestedRegex
629629
def: \b({RelativeRegex}|{AroundRegex}|{BeforeRegex}|{AfterRegex}|{SinceRegex})\b
630630
references: [RelativeRegex, AroundRegex, BeforeRegex, AfterRegex, SinceRegex ]
@@ -639,6 +639,8 @@ SinceYearSuffixRegex: !nestedRegex
639639
WithinNextPrefixRegex: !nestedRegex
640640
def: \b(within(\s+the)?(\s+(?<next>{NextPrefixRegex}))?)\b
641641
references: [ NextPrefixRegex ]
642+
ForPrefixRegex: !simpleRegex
643+
def: ((?<forfrom>for.*from.*)|(?<from>\bfrom\b)|(?<for>\bfor\b))
642644
TodayNowRegex: !simpleRegex # Added to remove hard coded strings in BaseDatePeriodParser
643645
def: \b(today|now|current (date|time))\b
644646
# "next" group here is used to judge uncommon unsupported cases like "within the next 5 days before today"

Specs/DateTime/English/DatePeriodExtractor.json

+36
Original file line numberDiff line numberDiff line change
@@ -3572,6 +3572,42 @@
35723572
}
35733573
]
35743574
},
3575+
{
3576+
"Input": "set OOO for 1 week, from 20th dec",
3577+
"NotSupported": "python, javascript, java",
3578+
"Results": [
3579+
{
3580+
"Text": "for 1 week, from 20th dec",
3581+
"Type": "daterange",
3582+
"Start": 8,
3583+
"Length": 25
3584+
}
3585+
]
3586+
},
3587+
{
3588+
"Input": "set OOO for 3 days, from today",
3589+
"NotSupported": "python, javascript, java",
3590+
"Results": [
3591+
{
3592+
"Text": "for 3 days, from today",
3593+
"Type": "daterange",
3594+
"Start": 8,
3595+
"Length": 22
3596+
}
3597+
]
3598+
},
3599+
{
3600+
"Input": "set OOO for 3 days from today",
3601+
"NotSupported": "python, javascript, java",
3602+
"Results": [
3603+
{
3604+
"Text": "for 3 days from today",
3605+
"Type": "daterange",
3606+
"Start": 8,
3607+
"Length": 21
3608+
}
3609+
]
3610+
},
35753611
{
35763612
"Input": "I will come back less than 2 weeks from today",
35773613
"NotSupported": "python, javascript",

Specs/DateTime/English/DatePeriodParser.json

+78
Original file line numberDiff line numberDiff line change
@@ -5266,6 +5266,84 @@
52665266
}
52675267
]
52685268
},
5269+
{
5270+
"Input": "set OOO for 1 week, from 20th dec",
5271+
"Context": {
5272+
"ReferenceDateTime": "2018-05-29T00:00:00"
5273+
},
5274+
"NotSupported": "python, javascript, java",
5275+
"Results": [
5276+
{
5277+
"Text": "for 1 week, from 20th dec",
5278+
"Type": "daterange",
5279+
"Value": {
5280+
"Timex": "(2018-12-20,2018-12-27,P1W)",
5281+
"FutureResolution": {
5282+
"startDate": "2018-12-20",
5283+
"endDate": "2018-12-27"
5284+
},
5285+
"PastResolution": {
5286+
"startDate": "2018-12-20",
5287+
"endDate": "2018-12-27"
5288+
}
5289+
},
5290+
"Start": 8,
5291+
"Length": 25
5292+
}
5293+
]
5294+
},
5295+
{
5296+
"Input": "set OOO for 3 days, from today",
5297+
"Context": {
5298+
"ReferenceDateTime": "2018-05-23T00:00:00"
5299+
},
5300+
"NotSupported": "python, javascript, java",
5301+
"Results": [
5302+
{
5303+
"Text": "for 3 days, from today",
5304+
"Type": "daterange",
5305+
"Value": {
5306+
"Timex": "(2018-05-23,2018-05-26,P3D)",
5307+
"FutureResolution": {
5308+
"startDate": "2018-05-23",
5309+
"endDate": "2018-05-26"
5310+
},
5311+
"PastResolution": {
5312+
"startDate": "2018-05-23",
5313+
"endDate": "2018-05-26"
5314+
}
5315+
},
5316+
"Start": 8,
5317+
"Length": 22
5318+
}
5319+
]
5320+
},
5321+
{
5322+
"Input": "set OOO for 3 days from today",
5323+
"Context": {
5324+
"ReferenceDateTime": "2018-05-23T00:00:00"
5325+
},
5326+
"NotSupported": "python, javascript, java",
5327+
"Results": [
5328+
{
5329+
"Text": "for 3 days from today",
5330+
"Type": "daterange",
5331+
"Value": {
5332+
"Timex": "(2018-05-23,2018-05-26,P3D)",
5333+
"FutureResolution": {
5334+
"startDate": "2018-05-23",
5335+
"endDate": "2018-05-26"
5336+
},
5337+
"PastResolution": {
5338+
"startDate": "2018-05-23",
5339+
"endDate": "2018-05-26"
5340+
}
5341+
},
5342+
"Start": 8,
5343+
"Length": 21
5344+
}
5345+
]
5346+
},
52695347
{
52705348
"Input": "I have already finished all my work more than 2 weeks before today",
52715349
"Context": {

Specs/DateTime/English/DateTimeModel.json

+75
Original file line numberDiff line numberDiff line change
@@ -20951,6 +20951,81 @@
2095120951
}
2095220952
]
2095320953
},
20954+
{
20955+
"Input": "set OOO for 1 week, from 20th dec",
20956+
"Context": {
20957+
"ReferenceDateTime": "2018-05-29T00:00:00"
20958+
},
20959+
"NotSupported": "javascript, python, java",
20960+
"Results": [
20961+
{
20962+
"Text": "for 1 week, from 20th dec",
20963+
"Start": 8,
20964+
"End": 32,
20965+
"TypeName": "datetimeV2.daterange",
20966+
"Resolution": {
20967+
"values": [
20968+
{
20969+
"timex": "(2018-12-20,2018-12-27,P1W)",
20970+
"type": "daterange",
20971+
"start": "2018-12-20",
20972+
"end": "2018-12-27"
20973+
}
20974+
]
20975+
}
20976+
}
20977+
]
20978+
},
20979+
{
20980+
"Input": "set OOO for 3 days, from today",
20981+
"Context": {
20982+
"ReferenceDateTime": "2018-05-23T00:00:00"
20983+
},
20984+
"NotSupported": "javascript, python, java",
20985+
"Results": [
20986+
{
20987+
"Text": "for 3 days, from today",
20988+
"Start": 8,
20989+
"End": 29,
20990+
"TypeName": "datetimeV2.daterange",
20991+
"Resolution": {
20992+
"values": [
20993+
{
20994+
"timex": "(2018-05-23,2018-05-26,P3D)",
20995+
"type": "daterange",
20996+
"start": "2018-05-23",
20997+
"end": "2018-05-26"
20998+
}
20999+
]
21000+
}
21001+
}
21002+
]
21003+
},
21004+
{
21005+
"Input": "set OOO for 3 days from today",
21006+
"Context": {
21007+
"ReferenceDateTime": "2018-05-23T00:00:00"
21008+
},
21009+
"NotSupported": "javascript, python, java",
21010+
"Results": [
21011+
{
21012+
"Text": "for 3 days from today",
21013+
"Start": 8,
21014+
"End": 28,
21015+
"TypeName": "datetimeV2.daterange",
21016+
"Resolution": {
21017+
"values": [
21018+
{
21019+
"timex": "(2018-05-23,2018-05-26,P3D)",
21020+
"type": "daterange",
21021+
"start": "2018-05-23",
21022+
"end": "2018-05-26"
21023+
}
21024+
]
21025+
}
21026+
}
21027+
]
21028+
},
2095421029
{
2095521030
"Input": "The project was submitted last month and 3 weeks later it was approved",
2095621031
"Context": {

0 commit comments

Comments
 (0)