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);
        }
Esempio n. 2
0
        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);
        }
Esempio n. 4
0
        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);
        }
Esempio n. 7
0
        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);
        }
Esempio n. 8
0
        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);
        }
Esempio n. 9
0
        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);
        }
Esempio n. 17
0
        // 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 }
            });
        }
Esempio n. 19
0
        // 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);
        }
Esempio n. 20
0
        // 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);
        }
Esempio n. 22
0
        // 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 }
            });
        }
Esempio n. 26
0
        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);
        }
Esempio n. 27
0
        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);
        }