// Parse "last minute", "next hour" private DateTimeResolutionResult ParseRelativeUnit(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); var match = Config.RelativeTimeUnitRegex.Match(text); if (!match.Success) { match = this.Config.RestOfDateTimeRegex.Match(text); } if (match.Success) { var srcUnit = match.Groups["unit"].Value.ToLower(); var unitStr = Config.UnitMap[srcUnit]; int swiftValue = 1; var prefixMatch = Config.PastRegex.Match(text); if (prefixMatch.Success) { swiftValue = -1; } DateObject beginTime; var endTime = beginTime = referenceTime; var ptTimex = string.Empty; if (Config.UnitMap.ContainsKey(srcUnit)) { switch (unitStr) { case "D": endTime = DateObject.MinValue.SafeCreateFromValue(beginTime.Year, beginTime.Month, beginTime.Day); endTime = endTime.AddDays(1).AddSeconds(-1); ptTimex = "PT" + (endTime - beginTime).TotalSeconds + "S"; break; case "H": beginTime = swiftValue > 0 ? beginTime : referenceTime.AddHours(swiftValue); endTime = swiftValue > 0 ? referenceTime.AddHours(swiftValue) : endTime; ptTimex = "PT1H"; break; case "M": beginTime = swiftValue > 0 ? beginTime : referenceTime.AddMinutes(swiftValue); endTime = swiftValue > 0 ? referenceTime.AddMinutes(swiftValue) : endTime; ptTimex = "PT1M"; break; case "S": beginTime = swiftValue > 0 ? beginTime : referenceTime.AddSeconds(swiftValue); endTime = swiftValue > 0 ? referenceTime.AddSeconds(swiftValue) : endTime; ptTimex = "PT1S"; break; default: return(ret); } ret.Timex = $"({FormatUtil.LuisDate(beginTime)}T{FormatUtil.LuisTime(beginTime)}," + $"{FormatUtil.LuisDate(endTime)}T{FormatUtil.LuisTime(endTime)},{ptTimex})"; ret.FutureValue = ret.PastValue = new Tuple <DateObject, DateObject>(beginTime, endTime); ret.Success = true; return(ret); } } return(ret); }
//TODO: this can be abstracted with the similar method in BaseDatePeriodParser // parse "in 20 minutes" private DateTimeResolutionResult ParseDuration(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); //for rest of datetime, it will be handled in next function if (Config.RestOfDateTimeRegex.IsMatch(text)) { return(ret); } var ers = Config.DurationExtractor.Extract(text); if (ers.Count == 1) { var pr = Config.DurationParser.Parse(ers[0]); var beforeStr = text.Substring(0, pr.Start ?? 0).Trim().ToLowerInvariant(); if (pr.Value != null) { var swiftSeconds = 0; var mod = ""; 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 prefixMatch = Config.PastRegex.Match(beforeStr); if (prefixMatch.Success && prefixMatch.Length == beforeStr.Length) { mod = TimeTypeConstants.beforeMod; beginTime = referenceTime.AddSeconds(-swiftSeconds); } prefixMatch = Config.FutureRegex.Match(beforeStr); if (prefixMatch.Success && prefixMatch.Length == beforeStr.Length) { mod = TimeTypeConstants.afterMod; endTime = beginTime.AddSeconds(swiftSeconds); } ret.Timex = $"({FormatUtil.LuisDate(beginTime)}T{FormatUtil.LuisTime(beginTime)}," + $"{FormatUtil.LuisDate(endTime)}T{FormatUtil.LuisTime(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); }
//TODO: this can be abstracted with the similar method in BaseDatePeriodParser // Parse "in 20 minutes" private DateTimeResolutionResult ParseDuration(string text, DateObject referenceTime) { var ret = new DateTimeResolutionResult(); // For the rest of datetime, it will be handled in next function if (Config.RestOfDateTimeRegex.IsMatch(text)) { return(ret); } var ers = Config.DurationExtractor.Extract(text, referenceTime); if (ers.Count == 1) { var pr = Config.DurationParser.Parse(ers[0]); var beforeStr = text.Substring(0, pr.Start ?? 0).Trim().ToLowerInvariant(); var afterStr = text.Substring((pr.Start ?? 0) + (pr.Length ?? 0)).Trim().ToLowerInvariant(); if (pr.Value != null) { var swiftSeconds = 0; var mod = ""; 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 prefixMatch = Config.PastRegex.Match(beforeStr); if (prefixMatch.Success && prefixMatch.Length == beforeStr.Length) { mod = Constants.BEFORE_MOD; beginTime = referenceTime.AddSeconds(-swiftSeconds); } // Handle the "within (the) (next) xx seconds/minutes/hours" case // Should also handle the multiple duration case like P1DT8H // Set the beginTime equal to reference time for now prefixMatch = Config.WithinNextPrefixRegex.Match(beforeStr); if (prefixMatch.Success && prefixMatch.Length == beforeStr.Length) { endTime = beginTime.AddSeconds(swiftSeconds); } prefixMatch = Config.FutureRegex.Match(beforeStr); if (prefixMatch.Success && prefixMatch.Length == beforeStr.Length) { mod = Constants.AFTER_MOD; endTime = beginTime.AddSeconds(swiftSeconds); } var suffixMatch = Config.PastRegex.Match(afterStr); if (suffixMatch.Success && suffixMatch.Length == afterStr.Length) { mod = Constants.BEFORE_MOD; beginTime = referenceTime.AddSeconds(-swiftSeconds); } suffixMatch = Config.FutureRegex.Match(afterStr); if (suffixMatch.Success && suffixMatch.Length == afterStr.Length) { mod = Constants.AFTER_MOD; endTime = beginTime.AddSeconds(swiftSeconds); } suffixMatch = Config.FutureSuffixRegex.Match(afterStr); if (suffixMatch.Success && suffixMatch.Length == afterStr.Length) { mod = Constants.AFTER_MOD; endTime = beginTime.AddSeconds(swiftSeconds); } ret.Timex = $"({FormatUtil.LuisDate(beginTime)}T{FormatUtil.LuisTime(beginTime)}," + $"{FormatUtil.LuisDate(endTime)}T{FormatUtil.LuisTime(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 SortedDictionary <string, object> DateTimeResolution(DateTimeParseResult slot) { if (slot == null) { return(null); } var resolutions = new List <Dictionary <string, object> >(); 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; List <string> list = null; // Resolve dates list for date periods if (slot.Type.Equals(Constants.SYS_DATETIME_DATEPERIOD) && val.List != null) { list = val.List.Select(o => FormatUtil.LuisDate((DateObject)o)).ToList(); } // 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 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() : 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)) { // single timezone AddResolutionFields(res, Constants.ResolveTimeZone, new Dictionary <string, string> { { ResolutionKey.Value, val.TimeZoneResolution.Value }, { Constants.UtcOffsetMinsKey, val.TimeZoneResolution.UtcOffsetMins.ToString() } }); } 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()); } } 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)) { 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)) { ResolveWeekOf(res, Constants.ResolveToPast); } foreach (var p in res) { if (p.Value is Dictionary <string, string> dictionary) { var value = new Dictionary <string, object>(); AddResolutionFields(value, DateTimeResolutionKey.Timex, timex); AddResolutionFields(value, DateTimeResolutionKey.Mod, mod); AddResolutionFields(value, ResolutionKey.Type, typeOutput); AddResolutionFields(value, DateTimeResolutionKey.IsLunar, islunar ? islunar.ToString() : string.Empty); AddResolutionFields(value, DateTimeResolutionKey.List, list); if (hasTimeZone) { AddResolutionFields(value, Constants.TimeZone, val.TimeZoneResolution.Value); AddResolutionFields(value, Constants.TimeZoneText, val.TimeZoneResolution.TimeZoneText); AddResolutionFields(value, Constants.UtcOffsetMinsKey, val.TimeZoneResolution.UtcOffsetMins.ToString()); } foreach (var q in dictionary) { if (value.ContainsKey(q.Key)) { value[q.Key] = q.Value; } else { value.Add(q.Key, q.Value); } } resolutions.Add(value); } } if (resolutionPast.Count == 0 && resolutionFuture.Count == 0 && val.TimeZoneResolution == null) { var notResolved = new Dictionary <string, object> { { DateTimeResolutionKey.Timex, timex }, { ResolutionKey.Type, typeOutput }, { ResolutionKey.Value, "not resolved" } }; resolutions.Add(notResolved); } return(new SortedDictionary <string, object> { { ResolutionKey.ValueSet, resolutions } }); }