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 } }); }