public DateTimeResolutionResult ProcessDatePeriodEntityResolution(DateTimeResolutionResult resolutionResult) { if (!IsEmpty()) { resolutionResult.Timex = TimexUtility.SetTimexWithContext(resolutionResult.Timex, this); resolutionResult.FutureValue = SetDateRangeWithContext((Tuple <DateObject, DateObject>)resolutionResult.FutureValue); resolutionResult.PastValue = SetDateRangeWithContext((Tuple <DateObject, DateObject>)resolutionResult.PastValue); } return(resolutionResult); }
private DateTimeResolutionResult MergeDateAndTimePeriod(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); var er1 = this.config.DateExtractor.Extract(text, referenceTime); var er2 = this.config.TimePeriodExtractor.Extract(text, referenceTime); if (er1.Count != 1 || er2.Count != 1) { return(ret); } var pr1 = this.config.DateParser.Parse(er1[0], referenceTime); var pr2 = this.config.TimePeriodParser.Parse(er2[0], referenceTime); var timeRange = (Tuple <DateObject, DateObject>)((DateTimeResolutionResult)pr2.Value).FutureValue; var beginTime = timeRange.Item1; var endTime = timeRange.Item2; var futureDate = (DateObject)((DateTimeResolutionResult)pr1.Value).FutureValue; var pastDate = (DateObject)((DateTimeResolutionResult)pr1.Value).PastValue; // handle cases with time like 25時 which resolve to the next day var swiftDay = 0; var timexHours = TimexUtility.ParseHoursFromTimePeriodTimex(pr2.TimexStr); if (timexHours.Item1 > Constants.DayHourCount) { pastDate = pastDate.AddDays(1); futureDate = futureDate.AddDays(1); } else if (timexHours.Item2 > Constants.DayHourCount) { swiftDay++; } var pastDateAlt = pastDate.AddDays(swiftDay); var futureDateAlt = futureDate.AddDays(swiftDay); ret.FutureValue = new Tuple <DateObject, DateObject>( DateObject.MinValue.SafeCreateFromValue(futureDate.Year, futureDate.Month, futureDate.Day, beginTime.Hour, beginTime.Minute, beginTime.Second), DateObject.MinValue.SafeCreateFromValue(futureDateAlt.Year, futureDateAlt.Month, futureDateAlt.Day, endTime.Hour, endTime.Minute, endTime.Second)); ret.PastValue = new Tuple <DateObject, DateObject>( DateObject.MinValue.SafeCreateFromValue(pastDate.Year, pastDate.Month, pastDate.Day, beginTime.Hour, beginTime.Minute, beginTime.Second), DateObject.MinValue.SafeCreateFromValue(pastDateAlt.Year, pastDateAlt.Month, pastDateAlt.Day, endTime.Hour, endTime.Minute, endTime.Second)); ret.Timex = TimexUtility.GenerateSplitDateTimePeriodTimex(pr1.TimexStr, pr2.TimexStr); ret.Success = !string.IsNullOrEmpty(ret.Timex); return(ret); }
public DateTimeParseResult Parse(ExtractResult er, DateObject refDate) { var referenceTime = refDate; var dateTimeParseResult = ParseMergedDuration(er.Text, referenceTime); if (!dateTimeParseResult.Success) { var parseResult = this.config.InternalParser.Parse(er); var unitResult = parseResult.Value as UnitValue; if (unitResult == null) { return(null); } var unitStr = unitResult.Unit; var numStr = unitResult.Number; dateTimeParseResult.Timex = TimexUtility.GenerateDurationTimex(double.Parse(numStr), unitStr, BaseDurationParser.IsLessThanDay(unitStr)); dateTimeParseResult.FutureValue = dateTimeParseResult.PastValue = double.Parse(numStr) * this.config.UnitValueMap[unitStr]; dateTimeParseResult.Success = true; } if (dateTimeParseResult.Success) { dateTimeParseResult.FutureResolution = new Dictionary <string, string> { { TimeTypeConstants.DURATION, dateTimeParseResult.FutureValue.ToString() }, }; dateTimeParseResult.PastResolution = new Dictionary <string, string> { { TimeTypeConstants.DURATION, dateTimeParseResult.PastValue.ToString() }, }; } var ret = new DateTimeParseResult { Text = er.Text, Start = er.Start, Length = er.Length, Type = er.Type, Data = er.Data, Value = dateTimeParseResult, TimexStr = dateTimeParseResult.Timex, ResolutionStr = string.Empty, }; return(ret); }
private DateTimeResolutionResult ParseDuration(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); var ers = config.DurationExtractor.Extract(text, referenceTime); if (ers.Count == 1) { var pr = config.DurationParser.Parse(ers[0]); var afterStr = text.Substring((pr.Start ?? 0) + (pr.Length ?? 0)).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; var match = config.FutureRegex.Match(afterStr); if (match.Groups[Constants.WithinGroupName].Success) { endTime = beginTime.AddSeconds(swiftSeconds); ret.Timex = TimexUtility.GenerateDateTimePeriodTimex(beginTime, 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); }
public static DateTimeParseResult SetInclusivePeriodEnd(DateTimeParseResult slot) { if (slot.Type == $"{ParserTypeName}.{Constants.SYS_DATETIME_DATEPERIOD}") { var timexComponents = slot.TimexStr.Split(Constants.DatePeriodTimexSplitter, StringSplitOptions.RemoveEmptyEntries); // Only handle DatePeriod like "(StartDate,EndDate,Duration)" if (timexComponents.Length == 3) { var value = (SortedDictionary <string, object>)slot.Value; var altTimex = string.Empty; if (value != null && value.ContainsKey(ResolutionKey.ValueSet)) { if (value[ResolutionKey.ValueSet] is IList <Dictionary <string, string> > valueSet && valueSet.Any()) { foreach (var values in valueSet) { // This is only a sanity check, as here we only handle DatePeriod like "(StartDate,EndDate,Duration)" if (values.ContainsKey(DateTimeResolutionKey.Start) && values.ContainsKey(DateTimeResolutionKey.End) && values.ContainsKey(DateTimeResolutionKey.Timex)) { var startDate = DateObject.Parse(values[DateTimeResolutionKey.Start], CultureInfo.InvariantCulture); var endDate = DateObject.Parse(values[DateTimeResolutionKey.End], CultureInfo.InvariantCulture); var durationStr = timexComponents[2]; var datePeriodTimexType = TimexUtility.GetDatePeriodTimexType(durationStr); endDate = TimexUtility.OffsetDateObject(endDate, offset: 1, timexType: datePeriodTimexType); values[DateTimeResolutionKey.End] = DateTimeFormatUtil.LuisDate(endDate); values[DateTimeResolutionKey.Timex] = GenerateEndInclusiveTimex(slot.TimexStr, datePeriodTimexType, startDate, endDate); if (string.IsNullOrEmpty(altTimex)) { altTimex = values[DateTimeResolutionKey.Timex]; } } } } } slot.Value = value; slot.TimexStr = altTimex; } } return(slot); }
private DateTimeResolutionResult ParseNumberSpaceUnit(string text) { var ret = new DateTimeResolutionResult(); var suffixStr = text; // if there are spaces between number and unit var ers = ExtractNumbersBeforeUnit(text); if (ers.Count == 1) { var pr = this.config.NumberParser.Parse(ers[0]); // followed unit: {num} (<followed unit>and a half hours) var srcUnit = string.Empty; var noNum = text.Substring(ers[0].Start + ers[0].Length ?? 0).Trim(); var match = this.config.FollowedUnit.Match(noNum); if (match.Success) { srcUnit = match.Groups["unit"].Value; suffixStr = match.Groups[Constants.SuffixGroupName].Value; } if (match.Success && match.Groups[Constants.BusinessDayGroupName].Success) { var numVal = int.Parse(pr.Value.ToString(), CultureInfo.InvariantCulture); ret.Timex = TimexUtility.GenerateDurationTimex(numVal, Constants.TimexBusinessDay, false); ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit.Split()[1]]; ret.Success = true; return(ret); } if (this.config.UnitMap.TryGetValue(srcUnit, out var unitStr)) { var numVal = double.Parse(pr.Value.ToString(), CultureInfo.InvariantCulture) + ParseNumberWithUnitAndSuffix(suffixStr); ret.Timex = TimexUtility.GenerateDurationTimex(numVal, unitStr, IsLessThanDay(unitStr)); ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit]; ret.Success = true; return(ret); } } return(ret); }
public DateTimeParseResult SetInclusivePeriodEnd(DateTimeParseResult slot) { if (slot.Type == $"{ParserTypeName}.{Constants.SYS_DATETIME_DATEPERIOD}") { var timexComponents = slot.TimexStr.Split(Constants.DatePeriodTimexSplitter, StringSplitOptions.RemoveEmptyEntries); // Only handle DatePeriod like "(StartDate,EndDate,Duration)" if (timexComponents.Length == 3) { var startDate = DateObject.Parse(timexComponents[0]); var endDate = DateObject.Parse(timexComponents[1]); var durationStr = timexComponents[2]; var datePeriodTimexType = TimexUtility.GetDatePeriodTimexType(durationStr); endDate = TimexUtility.OffsetDateObject(endDate, offset: 1, timexType: datePeriodTimexType); slot.TimexStr = TimexUtility.GenerateDatePeriodTimex(startDate, endDate, datePeriodTimexType); var value = (SortedDictionary <string, object>)slot.Value; if (value != null && value.ContainsKey(ResolutionKey.ValueSet)) { var valueSet = value[ResolutionKey.ValueSet] as IList <Dictionary <string, string> >; if (valueSet != null && valueSet.Any()) { var values = valueSet[0]; if (values.ContainsKey("timex")) { values["timex"] = slot.TimexStr; } if (values.ContainsKey("end")) { values["end"] = FormatUtil.LuisDate(endDate); } } } slot.Value = value; } } return(slot); }
private static DateTimeResolutionResult ParseInexactNumberUnit(string text, Regex inexactNumberUnitRegex, IImmutableDictionary <string, string> unitMap, IImmutableDictionary <string, long> unitValueMap, bool isCJK = false) { var ret = new DateTimeResolutionResult(); var match = inexactNumberUnitRegex.Match(text); if (match.Success) { // set the inexact number "few", "some" to 3 for now double numVal = match.Groups["NumTwoTerm"].Success ? 2 : 3; var srcUnit = match.Groups["unit"].Value; if (unitMap.ContainsKey(srcUnit)) { var unitStr = unitMap[srcUnit]; if (numVal > 1000 && (unitStr.Equals(Constants.TimexYear, StringComparison.Ordinal) || unitStr.Equals(Constants.TimexMonthFull, StringComparison.Ordinal) || unitStr.Equals(Constants.TimexWeek, StringComparison.Ordinal))) { return(ret); } ret.Timex = TimexUtility.GenerateDurationTimex(numVal, unitStr, IsLessThanDay(unitStr)); // In CJK implementation unitValueMap uses the unitMap values as keys while // in standard implementation unitMap and unitValueMap have the same keys. var unitValue = isCJK ? unitValueMap[unitStr] : unitValueMap[srcUnit]; ret.FutureValue = ret.PastValue = numVal * unitValue; ret.Success = true; } else if (match.Groups[Constants.BusinessDayGroupName].Success) { ret.Timex = TimexUtility.GenerateDurationTimex(numVal, Constants.TimexBusinessDay, false); // The line below was containing this.config.UnitValueMap[srcUnit.Split()[1]] // it was updated to accommodate single word "business day" expressions. ret.FutureValue = ret.PastValue = numVal * unitValueMap[srcUnit.Split()[srcUnit.Split().Length - 1]]; ret.Success = true; } } return(ret); }
private DateTimeResolutionResult ParseAnUnit(string text) { var ret = new DateTimeResolutionResult(); var suffixStr = text; var match = this.config.AnUnitRegex.Match(text); if (!match.Success) { match = this.config.HalfDateUnitRegex.Match(text); } if (match.Success) { var numVal = match.Groups["half"].Success ? 0.5 : 1; numVal = match.Groups["quarter"].Success ? 0.25 : numVal; numVal = match.Groups["threequarter"].Success ? 0.75 : numVal; numVal += ParseNumberWithUnitAndSuffix(suffixStr); var srcUnit = match.Groups["unit"].Value; if (this.config.UnitMap.ContainsKey(srcUnit)) { var unitStr = this.config.UnitMap[srcUnit]; ret.Timex = TimexUtility.GenerateDurationTimex(numVal, unitStr, IsLessThanDay(unitStr)); ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit]; ret.Success = true; } else if (match.Groups[Constants.BusinessDayGroupName].Success) { ret.Timex = TimexUtility.GenerateDurationTimex(numVal, Constants.TimexBusinessDay, false); // The line below was containing this.config.UnitValueMap[srcUnit.Split()[1]] // it was updated to accommodate single word "business day" expressions. ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit.Split()[srcUnit.Split().Length - 1]]; ret.Success = true; } } return(ret); }
private bool TryGetResultFromRegex(Regex regex, string text, string numStr, out DateTimeResolutionResult ret) { ret = new DateTimeResolutionResult(); var match = regex.Match(text); if (match.Success) { var srcUnit = match.Groups["unit"].Value; if (this.config.UnitValueMap.ContainsKey(srcUnit)) { var unitStr = this.config.UnitMap[srcUnit]; var numVal = double.Parse(numStr); ret.Timex = TimexUtility.GenerateDurationTimex(numVal, unitStr, IsLessThanDay(unitStr)); ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit]; ret.Success = true; } } return(match.Success); }
private DateTimeResolutionResult ParseInexactNumberUnit(string text) { var ret = new DateTimeResolutionResult(); var match = config.InexactNumberUnitRegex.Match(text); if (match.Success) { // set the inexact number "few", "some" to 3 for now double numVal = match.Groups["NumTwoTerm"].Success ? 2 : 3; var srcUnit = match.Groups["unit"].Value; if (this.config.UnitMap.ContainsKey(srcUnit)) { var unitStr = this.config.UnitMap[srcUnit]; if (numVal > 1000 && (unitStr.Equals(Constants.TimexYear, StringComparison.Ordinal) || unitStr.Equals(Constants.TimexMonthFull, StringComparison.Ordinal) || unitStr.Equals(Constants.TimexWeek, StringComparison.Ordinal))) { return(ret); } ret.Timex = TimexUtility.GenerateDurationTimex(numVal, unitStr, IsLessThanDay(unitStr)); ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit]; ret.Success = true; } else if (match.Groups[Constants.BusinessDayGroupName].Success) { ret.Timex = TimexUtility.GenerateDurationTimex(numVal, Constants.TimexBusinessDay, false); // The line below was containing this.config.UnitValueMap[srcUnit.Split()[1]] // it was updated to accommodate single word "business day" expressions. ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit.Split()[srcUnit.Split().Length - 1]]; ret.Success = true; } } return(ret); }
private DateTimeResolutionResult ParseInexactNumberUnit(string text) { var ret = new DateTimeResolutionResult(); var match = config.InexactNumberUnitRegex.Match(text); if (match.Success) { // set the inexact number "few", "some" to 3 for now double numVal = match.Groups["NumTwoTerm"].Success ? 2 : 3; var numStr = numVal.ToString(CultureInfo.InvariantCulture); var srcUnit = match.Groups["unit"].Value.ToLower(); if (this.config.UnitMap.ContainsKey(srcUnit)) { var unitStr = this.config.UnitMap[srcUnit]; if (double.Parse(numStr) > 1000 && (unitStr.Equals(Constants.TimexYear) || unitStr.Equals(Constants.TimexMonthFull) || unitStr.Equals(Constants.TimexWeek))) { return(ret); } ret.Timex = TimexUtility.GenerateDurationTimex(numVal, unitStr, IsLessThanDay(unitStr)); ret.FutureValue = ret.PastValue = double.Parse(numStr, CultureInfo.InvariantCulture) * this.config.UnitValueMap[srcUnit]; ret.Success = true; } else if (match.Groups[Constants.BusinessDayGroupName].Success) { ret.Timex = TimexUtility.GenerateDurationTimex(numVal, Constants.TimexBusinessDay, false); ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit.Split()[1]]; ret.Success = true; } } return(ret); }
private DateTimeResolutionResult ParseAnUnit(string text) { var ret = new DateTimeResolutionResult(); var suffixStr = text; var match = this.config.AnUnitRegex.Match(text); if (!match.Success) { match = this.config.HalfDateUnitRegex.Match(text); } if (match.Success) { var numVal = match.Groups["half"].Success ? 0.5 : 1; numVal += ParseNumberWithUnitAndSuffix(suffixStr); var numStr = numVal.ToString(CultureInfo.InvariantCulture); var srcUnit = match.Groups["unit"].Value.ToLower(); if (this.config.UnitMap.ContainsKey(srcUnit)) { var unitStr = this.config.UnitMap[srcUnit]; ret.Timex = TimexUtility.GenerateDurationTimex(numVal, unitStr, IsLessThanDay(unitStr)); ret.FutureValue = ret.PastValue = double.Parse(numStr) * this.config.UnitValueMap[srcUnit]; ret.Success = true; } else if (match.Groups[Constants.BusinessDayGroupName].Success) { ret.Timex = TimexUtility.GenerateDurationTimex(numVal, Constants.TimexBusinessDay, false); ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit.Split()[1]]; ret.Success = true; } } return(ret); }
// 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().ToLower(); 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, ""), 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; if (((DateTimeResolutionResult)pr.Value).TimeZoneResolution != null) { ret.TimeZoneResolution = ((DateTimeResolutionResult)pr.Value).TimeZoneResolution; } } 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); }
private DateTimeResolutionResult MergeDateWithSingleTimePeriod(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); var trimmedText = text.Trim().ToLower(); var ers = Config.TimePeriodExtractor.Extract(trimmedText, referenceTime); if (ers.Count == 0) { return(ParsePureNumberCases(text, referenceTime)); } else if (ers.Count == 1) { var timePeriodParseResult = Config.TimePeriodParser.Parse(ers[0]); var timePeriodResolutionResult = (DateTimeResolutionResult)timePeriodParseResult.Value; if (timePeriodResolutionResult == null) { return(ParsePureNumberCases(text, referenceTime)); } if (timePeriodResolutionResult.TimeZoneResolution != null) { ret.TimeZoneResolution = timePeriodResolutionResult.TimeZoneResolution; } var timePeriodTimex = timePeriodResolutionResult.Timex; // If it is a range type timex if (TimexUtility.IsRangeTimex(timePeriodTimex)) { var dateResult = this.Config.DateExtractor.Extract(trimmedText.Replace(ers[0].Text, ""), referenceTime); var dateText = trimmedText.Replace(ers[0].Text, "").Replace(Config.TokenBeforeDate, "").Trim(); // If only one Date is extracted and the Date text equals to the rest part of source text if (dateResult.Count == 1 && dateText.Equals(dateResult[0].Text)) { string dateTimex; DateObject futureTime; DateObject pastTime; var pr = this.Config.DateParser.Parse(dateResult[0], referenceTime); if (pr.Value != null) { futureTime = (DateObject)((DateTimeResolutionResult)pr.Value).FutureValue; pastTime = (DateObject)((DateTimeResolutionResult)pr.Value).PastValue; dateTimex = pr.TimexStr; } else { return(ParsePureNumberCases(text, referenceTime)); } var rangeTimexComponents = TimexUtility.GetRangeTimexComponents(timePeriodTimex); if (rangeTimexComponents.IsValid) { var beginTimex = TimexUtility.CombineDateAndTimeTimex(dateTimex, rangeTimexComponents.BeginTimex); var endTimex = TimexUtility.CombineDateAndTimeTimex(dateTimex, rangeTimexComponents.EndTimex); ret.Timex = TimexUtility.GenerateDateTimePeriodTimex(beginTimex, endTimex, rangeTimexComponents.DurationTimex); var timePeriodFutureValue = (Tuple <DateObject, DateObject>)timePeriodResolutionResult.FutureValue; var beginTime = timePeriodFutureValue.Item1; var endTime = timePeriodFutureValue.Item2; ret.FutureValue = new Tuple <DateObject, DateObject>( DateObject.MinValue.SafeCreateFromValue(futureTime.Year, futureTime.Month, futureTime.Day, beginTime.Hour, beginTime.Minute, beginTime.Second), DateObject.MinValue.SafeCreateFromValue(futureTime.Year, futureTime.Month, futureTime.Day, endTime.Hour, endTime.Minute, endTime.Second) ); ret.PastValue = new Tuple <DateObject, DateObject>( DateObject.MinValue.SafeCreateFromValue(pastTime.Year, pastTime.Month, pastTime.Day, beginTime.Hour, beginTime.Minute, beginTime.Second), DateObject.MinValue.SafeCreateFromValue(pastTime.Year, pastTime.Month, pastTime.Day, endTime.Hour, endTime.Minute, endTime.Second) ); if (!string.IsNullOrEmpty(timePeriodResolutionResult.Comment) && timePeriodResolutionResult.Comment.Equals(Constants.Comment_AmPm)) { // AmPm comment is used for later SetParserResult to judge whether this parse result should have two parsing results // Cases like "from 10:30 to 11 on 1/1/2015" should have AmPm comment, as it can be parsed to "10:30am to 11am" and also be parsed to "10:30pm to 11pm" // Cases like "from 10:30 to 3 on 1/1/2015" should not have AmPm comment if (beginTime.Hour < Constants.HalfDayHourCount && endTime.Hour < Constants.HalfDayHourCount) { ret.Comment = Constants.Comment_AmPm; } } ret.Success = true; ret.SubDateTimeEntities = new List <object> { pr, timePeriodParseResult }; return(ret); } } return(ParsePureNumberCases(text, referenceTime)); } } return(ret); }
public DateTimeParseResult Parse(ExtractResult er, DateObject refDate) { var referenceTime = refDate; var dateTimeParseResult = ParseMergedDuration(er.Text, referenceTime); if (!dateTimeParseResult.Success) { dateTimeParseResult = DurationParsingUtil.ParseInexactNumberUnit(er.Text, this.config); } if (!dateTimeParseResult.Success) { var parseResult = this.config.InternalParser.Parse(er); var unitResult = parseResult.Value as UnitValue; if (unitResult == null) { return(null); } var unitStr = unitResult.Unit; var number = string.IsNullOrEmpty(unitResult.Number) ? 1 : double.Parse(unitResult.Number, CultureInfo.InvariantCulture); dateTimeParseResult.Timex = TimexUtility.GenerateDurationTimex(number, unitStr, DurationParsingUtil.IsLessThanDay(unitStr)); dateTimeParseResult.FutureValue = dateTimeParseResult.PastValue = number * this.config.UnitValueMap[unitStr]; dateTimeParseResult.Success = true; } if (dateTimeParseResult.Success) { dateTimeParseResult.FutureResolution = new Dictionary <string, string> { { TimeTypeConstants.DURATION, dateTimeParseResult.FutureValue.ToString() }, }; dateTimeParseResult.PastResolution = new Dictionary <string, string> { { TimeTypeConstants.DURATION, dateTimeParseResult.PastValue.ToString() }, }; } if (dateTimeParseResult.Success) { var moreOrLessMatch = config.MoreOrLessRegex.Match(er.Text); if (moreOrLessMatch.Success) { if (moreOrLessMatch.Groups["less"].Success) { dateTimeParseResult.Mod = Constants.LESS_THAN_MOD; } else if (moreOrLessMatch.Groups["more"].Success) { dateTimeParseResult.Mod = Constants.MORE_THAN_MOD; } } } var ret = new DateTimeParseResult { Text = er.Text, Start = er.Start, Length = er.Length, Type = er.Type, Data = er.Data, Value = dateTimeParseResult, TimexStr = dateTimeParseResult.Timex, ResolutionStr = string.Empty, }; 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); }
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, StringComparison.Ordinal) && 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 sourceEntity = DetermineSourceEntityType(slot.Type, typeOutput, val.HasRangeChangingMod); 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(CultureInfo.InvariantCulture) : 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, StringComparison.Ordinal)) { // single timezone AddResolutionFields(res, Constants.ResolveTimeZone, new Dictionary <string, string> { { ResolutionKey.Value, val.TimeZoneResolution.Value }, { Constants.UtcOffsetMinsKey, val.TimeZoneResolution.UtcOffsetMins.ToString(CultureInfo.InvariantCulture) }, }); } 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(CultureInfo.InvariantCulture)); } } 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, StringComparison.Ordinal)) { 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, StringComparison.Ordinal)) { ResolveWeekOf(res, Constants.ResolveToPast); } if (!string.IsNullOrEmpty(comment) && TimexUtility.HasDoubleTimex(comment)) { TimexUtility.ProcessDoubleTimex(res, Constants.ResolveToFuture, Constants.ResolveToPast, timex); } 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(CultureInfo.InvariantCulture) : string.Empty); AddResolutionFields(value, DateTimeResolutionKey.List, list); AddResolutionFields(value, DateTimeResolutionKey.SourceEntity, sourceEntity); if (hasTimeZone) { AddResolutionFields(value, Constants.TimeZone, val.TimeZoneResolution.Value); AddResolutionFields(value, Constants.TimeZoneText, val.TimeZoneResolution.TimeZoneText); AddResolutionFields(value, Constants.UtcOffsetMinsKey, val.TimeZoneResolution.UtcOffsetMins.ToString(CultureInfo.InvariantCulture)); } foreach (var q in dictionary) { value[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 } }); }
// 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); }
// This will parse to a date ranging between the holiday and the closest weekend // cases: "Thanksgiving weekend", "weekend of Halloween" private DateTimeParseResult ParseHolidayWeekend(ExtractResult er, DateObject referenceDate) { var dateTimeRes = new DateTimeResolutionResult(); if (!string.IsNullOrEmpty(er.Metadata?.HolidayName)) { var holidayName = er.Metadata.HolidayName; // resolve holiday var holidayEr = new ExtractResult { Start = 0, Length = holidayName.Length, Text = holidayName, Type = Constants.SYS_DATETIME_DATE, Data = null, Metadata = new Metadata { IsHoliday = true }, }; var result = (DateTimeResolutionResult)this.Parse(holidayEr, referenceDate).Value; if (!result.Success) { dateTimeRes.FutureResolution = dateTimeRes.PastResolution = new Dictionary <string, string>(); } else { // get closest weekend to the holiday(s) var futureWeekend = GetClosestHolidayWeekend((DateObject)result.FutureValue); var pastWeekend = futureWeekend; if (result.FutureValue == result.PastValue) { dateTimeRes.Timex = TimexUtility.GenerateWeekendTimex(futureWeekend.Item1); } else { dateTimeRes.Timex = result.Timex; pastWeekend = GetClosestHolidayWeekend((DateObject)result.PastValue); } dateTimeRes.Success = true; dateTimeRes.FutureValue = futureWeekend; dateTimeRes.PastValue = pastWeekend; dateTimeRes.FutureResolution = new Dictionary <string, string> { { TimeTypeConstants.START_DATE, DateTimeFormatUtil.FormatDate(((Tuple <DateObject, DateObject>)dateTimeRes.FutureValue).Item1) }, { TimeTypeConstants.END_DATE, DateTimeFormatUtil.FormatDate(((Tuple <DateObject, DateObject>)dateTimeRes.FutureValue).Item2) }, { DateTimeResolutionKey.Timex, TimexUtility.GenerateWeekendTimex(futureWeekend.Item1) }, }; dateTimeRes.PastResolution = new Dictionary <string, string> { { TimeTypeConstants.START_DATE, DateTimeFormatUtil.FormatDate(((Tuple <DateObject, DateObject>)dateTimeRes.PastValue).Item1) }, { TimeTypeConstants.END_DATE, DateTimeFormatUtil.FormatDate(((Tuple <DateObject, DateObject>)dateTimeRes.PastValue).Item2) }, { DateTimeResolutionKey.Timex, TimexUtility.GenerateWeekendTimex(pastWeekend.Item1) }, }; } } else { dateTimeRes.FutureResolution = dateTimeRes.PastResolution = new Dictionary <string, string>(); } var ret = new DateTimeParseResult { Text = er.Text, Start = er.Start, Length = er.Length, Type = er.Type, Data = er.Data, Metadata = er.Metadata, Value = dateTimeRes, TimexStr = dateTimeRes == null ? string.Empty : ((DateTimeResolutionResult)dateTimeRes).Timex, ResolutionStr = string.Empty, }; return(ret); }
private DateTimeResolutionResult ParseMergedDuration(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); var durationExtractor = this.config.DurationExtractor; // DurationExtractor without parameter will not extract merged duration var ers = durationExtractor.Extract(text, referenceTime); // If the duration extractions do not start at 0, check if the input starts with an isolated unit. // This happens for example with patterns like "next week and 3 days" where "next" is not part of the extraction. var minStart = ers.Min(er => er.Start); if (minStart > 0) { var match = config.FollowedUnit.Match(text); if (match.Success) { var er = new ExtractResult { Start = match.Index, Length = match.Length, Text = match.Value, Type = ParserName, Data = null, }; ers.Insert(0, er); } } // only handle merged duration cases like "1 month 21 days" if (ers.Count <= 1) { ret.Success = false; return(ret); } var start = ers[0].Start ?? 0; if (start != 0) { var beforeStr = text.Substring(0, start - 1); if (!string.IsNullOrWhiteSpace(beforeStr)) { return(ret); } } var end = ers[ers.Count - 1].Start + ers[ers.Count - 1].Length ?? 0; if (end != text.Length) { var afterStr = text.Substring(end); if (!string.IsNullOrWhiteSpace(afterStr)) { return(ret); } } var prs = new List <DateTimeParseResult>(); var timexDict = new Dictionary <string, string>(); // insert timex into a dictionary foreach (var er in ers) { var unitRegex = this.config.DurationUnitRegex; var unitMatch = unitRegex.Match(er.Text); if (unitMatch.Success) { var pr = (DateTimeParseResult)Parse(er); if (pr.Value != null) { timexDict.Add(unitMatch.Groups["unit"].Value, pr.TimexStr); prs.Add(pr); } } } // sort the timex using the granularity of the duration, "P1M23D" for "1 month 23 days" and "23 days 1 month" if (prs.Count == ers.Count) { ret.Timex = TimexUtility.GenerateCompoundDurationTimex(timexDict, config.UnitValueMap); double value = 0; foreach (var pr in prs) { value += double.Parse(((DateTimeResolutionResult)pr.Value).FutureValue.ToString(), CultureInfo.InvariantCulture); } ret.FutureValue = ret.PastValue = value; } ret.Success = true; return(ret); }
// Parse cases like "this night" private DateTimeResolutionResult ParseSpecificNight(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); var trimmedText = text.Trim(); int beginHour, endHour, endMin = 0; string timeStr; // Handle 昨晚 (last night),今晨 (this morning) if (this.config.SpecificTimeOfDayRegex.IsExactMatch(trimmedText, trim: true)) { // handle the ambiguous case "ぎりぎり" [the latest possible time] var latest = this.config.SpecificTimeOfDayRegex.Match(text); if (latest.Groups[Constants.LatestGroupName].Success) { DateObject beginDate, endDate; beginDate = referenceTime.AddMinutes(-1); endDate = referenceTime; var diff = endDate - beginDate; ret.Timex = TimexUtility.GenerateDateTimePeriodTimex(beginDate, endDate); ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginDate, endDate); ret.Success = true; return(ret); } if (!this.config.GetMatchedTimeRangeAndSwift(trimmedText, out timeStr, out beginHour, out endHour, out endMin, out int swift)) { return(ret); } if (this.config.NextRegex.IsMatch(trimmedText)) { swift = 1; } else if (this.config.LastRegex.IsMatch(trimmedText)) { swift = -1; } 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 cases like morning, afternoon if (!this.config.GetMatchedTimeRange(trimmedText, out timeStr, out beginHour, out endHour, out endMin)) { return(ret); } if (this.config.SpecificTimeOfDayRegex.IsExactMatch(trimmedText, trim: true)) { var swift = 0; if (this.config.NextRegex.IsMatch(trimmedText)) { swift = 1; } else if (this.config.LastRegex.IsMatch(trimmedText)) { swift = -1; } 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 var match = this.config.TimeOfDayRegex.Match(trimmedText); if (match.Success) { var beforeStr = trimmedText.Substring(0, match.Index).Trim(); var ers = this.config.DateExtractor.Extract(beforeStr, referenceTime); if (ers.Count == 0 || ers[0].Length != beforeStr.Length) { return(ret); } var pr = this.config.DateParser.Parse(ers[0], referenceTime); var futureDate = (DateObject)((DateTimeResolutionResult)pr.Value).FutureValue; var pastDate = (DateObject)((DateTimeResolutionResult)pr.Value).PastValue; ret.Timex = pr.TimexStr + timeStr; 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); }
// merge a Date entity and a Time entity private DateTimeResolutionResult MergeDateAndTime(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); var matchAgoLater = this.config.AgoLaterRegex.Match(text); // cases with ago or later are processed in ParserDurationWithAgoAndLater if (matchAgoLater.Success) { return(ret); } var er1 = this.config.DateExtractor.Extract(text, referenceTime); if (er1.Count == 0) { return(ret); } var er2 = this.config.TimeExtractor.Extract(text, referenceTime); if (er2.Count == 0) { return(ret); } // TODO: Add reference time var pr1 = this.config.DateParser.Parse(er1[0], referenceTime.Date); var pr2 = this.config.TimeParser.Parse(er2[0], referenceTime); if (pr1.Value == null || pr2.Value == null) { return(ret); } var futureDate = (DateObject)((DateTimeResolutionResult)pr1.Value).FutureValue; var pastDate = (DateObject)((DateTimeResolutionResult)pr1.Value).PastValue; var time = (DateObject)((DateTimeResolutionResult)pr2.Value).FutureValue; // handle cases with time like 25時 which resolve to the next day var timexHour = TimexUtility.ParseHourFromTimeTimex(pr2.TimexStr); if (timexHour > Constants.DayHourCount) { futureDate = futureDate.AddDays(1); pastDate = pastDate.AddDays(1); } var hour = time.Hour; var min = time.Minute; var sec = time.Second; // handle morning, afternoon if (this.config.SimplePmRegex.IsMatch(text) && hour < Constants.HalfDayHourCount) { hour += Constants.HalfDayHourCount; } else if (this.config.SimpleAmRegex.IsMatch(text) && hour >= Constants.HalfDayHourCount) { hour -= Constants.HalfDayHourCount; } var timeStr = pr2.TimexStr; if (timeStr.EndsWith(Constants.Comment_AmPm, StringComparison.Ordinal)) { timeStr = timeStr.Substring(0, timeStr.Length - 4); } ret.Timex = pr1.TimexStr + timeStr; var val = (DateTimeResolutionResult)pr2.Value; if (hour <= Constants.HalfDayHourCount && !this.config.SimplePmRegex.IsMatch(text) && !this.config.SimpleAmRegex.IsMatch(text) && !string.IsNullOrEmpty(val.Comment)) { // ret.Timex += "ampm"; ret.Comment = Constants.Comment_AmPm; } ret.FutureValue = DateObject.MinValue.SafeCreateFromValue(futureDate.Year, futureDate.Month, futureDate.Day, hour, min, sec); ret.PastValue = DateObject.MinValue.SafeCreateFromValue(pastDate.Year, pastDate.Month, pastDate.Day, hour, min, sec); ret.Success = true; return(ret); }
private DateTimeResolutionResult ParseMergedDuration(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); var durationExtractor = this.config.DurationExtractor; //DurationExtractor without parameter will not extract merged duration var ers = durationExtractor.Extract(text); // only handle merged duration cases like "1 month 21 days" if (ers.Count <= 1) { ret.Success = false; return(ret); } var start = ers[0].Start ?? 0; if (start != 0) { var beforeStr = text.Substring(0, start - 1); if (!string.IsNullOrWhiteSpace(beforeStr)) { return(ret); } } var end = ers[ers.Count - 1].Start + ers[ers.Count - 1].Length ?? 0; if (end != text.Length) { var afterStr = text.Substring(end); if (!string.IsNullOrWhiteSpace(afterStr)) { return(ret); } } var prs = new List <DateTimeParseResult>(); var timexDict = new Dictionary <string, string>(); // insert timex into a dictionary foreach (var er in ers) { var unitRegex = this.config.DurationUnitRegex; var unitMatch = unitRegex.Match(er.Text); if (unitMatch.Success) { var pr = (DateTimeParseResult)Parse(er); if (pr.Value != null) { timexDict.Add(unitMatch.Groups["unit"].Value, pr.TimexStr); prs.Add(pr); } } } // sort the timex using the granularity of the duration, "P1M23D" for "1 month 23 days" and "23 days 1 month" if (prs.Count == ers.Count) { ret.Timex = TimexUtility.GenerateCompoundDurationTimex(timexDict, config.UnitValueMap); double value = 0; foreach (var pr in prs) { value += double.Parse(((DateTimeResolutionResult)pr.Value).FutureValue.ToString()); } ret.FutureValue = ret.PastValue = value; } ret.Success = true; return(ret); }
public SortedDictionary <string, object> DateTimeResolution(DateTimeParseResult slot, bool hasRangeChangingMod) { var resolutions = new List <Dictionary <string, string> >(); var res = new Dictionary <string, object>(); var val = (DateTimeResolutionResult)slot.Value; if (val is null) { return(null); } var type = slot.Type; var typeOutput = DetermineDateTimeType(slot.Type, hasRangeChangingMod); var sourceEntity = DetermineSourceEntityType(slot.Type, typeOutput, val.HasRangeChangingMod); var timex = slot.TimexStr; var isLunar = val.IsLunar; var mod = val.Mod; var comment = val.Comment; if (!string.IsNullOrEmpty(timex)) { res.Add(DateTimeResolutionKey.Timex, timex); } if (!string.IsNullOrEmpty(comment)) { res.Add(Constants.Comment, comment); } if (!string.IsNullOrEmpty(mod)) { res.Add(DateTimeResolutionKey.Mod, mod); } if (!string.IsNullOrEmpty(type)) { res.Add(ResolutionKey.Type, typeOutput); } var pastResolutionStr = ((DateTimeResolutionResult)slot.Value).PastResolution; var futureResolutionStr = ((DateTimeResolutionResult)slot.Value).FutureResolution; 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) { res.Add(Constants.Resolve, resolutionPast); } } else { if (resolutionPast.Count > 0) { res.Add(Constants.ResolveToPast, resolutionPast); } if (resolutionFuture.Count > 0) { res.Add(Constants.ResolveToFuture, resolutionFuture); } } // if ampm, double our resolution accordingly if (comment is Constants.Comment_AmPm) { if (res.ContainsKey(Constants.Resolve)) { ResolveAmpm(res, Constants.Resolve); } else { ResolveAmpm(res, Constants.ResolveToPast); ResolveAmpm(res, Constants.ResolveToFuture); } } if (!string.IsNullOrEmpty(comment) && TimexUtility.HasDoubleTimex(comment)) { ProcessDoubleTimex(res, Constants.ResolveToFuture, Constants.ResolveToPast, timex); } if (isLunar) { res.Add(DateTimeResolutionKey.IsLunar, isLunar); } foreach (var p in res) { if (p.Value is Dictionary <string, string> dictionary) { var value = new Dictionary <string, string>(); if (!string.IsNullOrEmpty(timex)) { value.Add(DateTimeResolutionKey.Timex, timex); } if (!string.IsNullOrEmpty(mod)) { value.Add(DateTimeResolutionKey.Mod, mod); } if (!string.IsNullOrEmpty(type)) { value.Add(ResolutionKey.Type, typeOutput); } if (!string.IsNullOrEmpty(sourceEntity)) { value.Add(DateTimeResolutionKey.SourceEntity, sourceEntity); } foreach (var q in dictionary) { value[q.Key] = q.Value; } resolutions.Add(value); } } if (resolutionPast.Count == 0 && resolutionFuture.Count == 0) { 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 } }); }
private DateTimeResolutionResult ParseNumberSpaceUnit(string text) { var ret = new DateTimeResolutionResult(); var suffixStr = text; // if there are spaces between number and unit var ers = ExtractNumbersBeforeUnit(text); if (ers.Count == 1) { var pr = this.config.NumberParser.Parse(ers[0]); // followed unit: {num} (<followed unit>and a half hours) var srcUnit = string.Empty; var noNum = text.Substring(ers[0].Start + ers[0].Length ?? 0).Trim(); var match = this.config.FollowedUnit.Match(noNum); if (match.Success) { srcUnit = match.Groups["unit"].Value; suffixStr = match.Groups[Constants.SuffixGroupName].Value; // check also beforeStr for "and an half" if (this.config.CheckBothBeforeAfter && string.IsNullOrEmpty(suffixStr)) { noNum = text.Substring(0, (int)ers[0].Start).Trim(); var prefixMatch = this.config.SuffixAndRegex.Match(noNum); if (prefixMatch.Success) { suffixStr = prefixMatch.Groups[Constants.SuffixGroupName].Value; } } } if (match.Success && match.Groups[Constants.BusinessDayGroupName].Success) { var numVal = int.Parse(pr.Value.ToString(), CultureInfo.InvariantCulture); ret.Timex = TimexUtility.GenerateDurationTimex(numVal, Constants.TimexBusinessDay, false); // The line below was containing this.config.UnitValueMap[srcUnit.Split()[1]] // it was updated to accommodate single word "business day" expressions. ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit.Split()[srcUnit.Split().Length - 1]]; ret.Success = true; return(ret); } if (this.config.UnitMap.TryGetValue(srcUnit, out var unitStr)) { // First try to parse combined expression 'num + suffix' double numVal; var combStr = pr.Text + " " + suffixStr; if (this.config.DoubleNumbers.ContainsKey(combStr)) { numVal = ParseNumberWithUnitAndSuffix(combStr); } else { numVal = double.Parse(pr.Value.ToString(), CultureInfo.InvariantCulture) + ParseNumberWithUnitAndSuffix(suffixStr); } ret.Timex = TimexUtility.GenerateDurationTimex(numVal, unitStr, IsLessThanDay(unitStr)); ret.FutureValue = ret.PastValue = numVal * this.config.UnitValueMap[srcUnit]; ret.Success = true; return(ret); } } return(ret); }
private DateTimeResolutionResult MergeTwoTimePoints(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); DateTimeParseResult pr1 = null, pr2 = null; bool bothHaveDates = false, beginHasDate = false, endHasDate = false; var er1 = this.config.TimeExtractor.Extract(text, referenceTime); var er2 = this.config.DateTimeExtractor.Extract(text, referenceTime); var rightTime = DateObject.MinValue.SafeCreateFromValue(referenceTime.Year, referenceTime.Month, referenceTime.Day); var leftTime = DateObject.MinValue.SafeCreateFromValue(referenceTime.Year, referenceTime.Month, referenceTime.Day); var match = config.FutureRegex.Match(text); // cases including 'within' are processed in ParseDuration if (match.Groups[Constants.WithinGroupName].Success) { return(ParseDuration(text, referenceTime)); } if (er2.Count == 2) { pr1 = this.config.DateTimeParser.Parse(er2[0], referenceTime); pr2 = this.config.DateTimeParser.Parse(er2[1], referenceTime); bothHaveDates = true; } else if (er2.Count == 1 && er1.Count == 2) { if (!er2[0].IsOverlap(er1[0])) { pr1 = this.config.TimeParser.Parse(er1[0], referenceTime); pr2 = this.config.DateTimeParser.Parse(er2[0], referenceTime); endHasDate = true; } else { pr1 = this.config.DateTimeParser.Parse(er2[0], referenceTime); pr2 = this.config.TimeParser.Parse(er1[1], referenceTime); beginHasDate = true; } } else if (er2.Count == 1 && er1.Count == 1) { if (er1[0].Start < er2[0].Start) { pr1 = this.config.TimeParser.Parse(er1[0], referenceTime); pr2 = this.config.DateTimeParser.Parse(er2[0], referenceTime); endHasDate = true; } else { pr1 = this.config.DateTimeParser.Parse(er2[0], referenceTime); pr2 = this.config.TimeParser.Parse(er1[0], referenceTime); beginHasDate = true; } } else if (er1.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; if (futureBegin > futureEnd) { futureBegin = pastBegin; } if (bothHaveDates) { rightTime = DateObject.MinValue.SafeCreateFromValue(futureEnd.Year, futureEnd.Month, futureEnd.Day); leftTime = DateObject.MinValue.SafeCreateFromValue(futureBegin.Year, futureBegin.Month, futureBegin.Day); } else if (beginHasDate) { leftTime = DateObject.MinValue.SafeCreateFromValue(futureBegin.Year, futureBegin.Month, futureBegin.Day); } else if (endHasDate) { rightTime = DateObject.MinValue.SafeCreateFromValue(futureEnd.Year, futureEnd.Month, futureEnd.Day); } var leftResult = (DateTimeResolutionResult)pr1.Value; var rightResult = (DateTimeResolutionResult)pr2.Value; var leftResultTime = (DateObject)leftResult.FutureValue; var rightResultTime = (DateObject)rightResult.FutureValue; // check if the right time is smaller than the left time, if yes, add one day int hour = leftResultTime.Hour > 0 ? leftResultTime.Hour : 0, min = leftResultTime.Minute > 0 ? leftResultTime.Minute : 0, second = leftResultTime.Second > 0 ? leftResultTime.Second : 0; leftTime = leftTime.AddHours(hour).AddMinutes(min).AddSeconds(second); hour = rightResultTime.Hour > 0 ? rightResultTime.Hour : 0; min = rightResultTime.Minute > 0 ? rightResultTime.Minute : 0; second = rightResultTime.Second > 0 ? rightResultTime.Second : 0; rightTime = rightTime.AddHours(hour).AddMinutes(min).AddSeconds(second); // the right side time contains "ampm", while the left side doesn't if (rightResult.Comment is Constants.Comment_AmPm && leftResult.Comment == null && rightTime < leftTime) { rightTime = rightTime.AddHours(Constants.HalfDayHourCount); } if (rightTime < leftTime) { rightTime = rightTime.AddDays(1); } ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(leftTime, rightTime); var leftTimex = pr1.TimexStr; var rightTimex = pr2.TimexStr; if (beginHasDate) { rightTimex = DateTimeFormatUtil.LuisDateShortTime(rightTime, pr2.TimexStr); } else if (endHasDate) { leftTimex = DateTimeFormatUtil.LuisDateShortTime(leftTime, pr1.TimexStr); } ret.Timex = TimexUtility.GenerateDateTimePeriodTimex(leftTimex, rightTimex, rightTime - leftTime); ret.Success = true; return(ret); }