public DateTimeParseResult Parse(ExtractResult er, DateObject refTime) { var referenceTime = refTime; DateTimeParseResult pr = null; var originText = er.Text; if ((this.Config.Options & DateTimeOptions.EnablePreview) != 0) { er.Text = MatchingUtil.PreProcessTextRemoveSuperfluousWords(er.Text, Config.SuperfluousWordMatcher, out var _); er.Length += er.Text.Length - originText.Length; } bool hasBefore = false, hasAfter = false, hasSince = false, hasAround = false, hasEqual = false, hasDateAfter = false; // "InclusiveModifier" means MOD should include the start/end time // For example, cases like "on or later than", "earlier than or in" have inclusive modifier var hasInclusiveModifier = false; var matchIsAfter = false; var modStr = string.Empty; // Analyze and process modifiers // Push, save the MOD string if (er.Metadata != null && er.Metadata.HasMod) { var beforeMatch = Config.BeforeRegex.MatchBegin(er.Text, trim: true); var afterMatch = Config.AfterRegex.MatchBegin(er.Text, trim: true); var sinceMatch = Config.SinceRegex.MatchBegin(er.Text, trim: true); var preLength = 0; if (beforeMatch.Success) { preLength = beforeMatch.Index + beforeMatch.Length; } else if (afterMatch.Success) { preLength = afterMatch.Index + afterMatch.Length; } else if (sinceMatch.Success) { preLength = sinceMatch.Index + sinceMatch.Length; } var aroundText = er.Text.Substring(preLength); var aroundMatch = Config.AroundRegex.MatchBegin(aroundText, trim: true); var equalMatch = Config.EqualRegex.MatchBegin(er.Text, trim: true); // check also after match if (this.Config.CheckBothBeforeAfter && er.Data != null && er.Data.Equals(Constants.HAS_MOD)) { if (!beforeMatch.Success) { beforeMatch = Config.BeforeRegex.MatchEnd(er.Text, trim: true); matchIsAfter = matchIsAfter || beforeMatch.Success; } if (!afterMatch.Success) { afterMatch = Config.AfterRegex.MatchEnd(er.Text, trim: true); matchIsAfter = matchIsAfter || afterMatch.Success; } if (!sinceMatch.Success) { sinceMatch = Config.SinceRegex.MatchEnd(er.Text, trim: true); matchIsAfter = matchIsAfter || sinceMatch.Success; } if (!aroundMatch.Success) { aroundMatch = Config.AroundRegex.MatchEnd(er.Text, trim: true); matchIsAfter = matchIsAfter || aroundMatch.Success; } if (!equalMatch.Success) { equalMatch = Config.EqualRegex.MatchEnd(er.Text, trim: true); matchIsAfter = matchIsAfter || equalMatch.Success; } } if (aroundMatch.Success) { hasAround = true; er.Start += matchIsAfter ? 0 : preLength + aroundMatch.Index + aroundMatch.Length; er.Length -= matchIsAfter ? aroundMatch.Length : preLength + aroundMatch.Index + aroundMatch.Length; er.Text = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(preLength + aroundMatch.Index + aroundMatch.Length); modStr = matchIsAfter ? aroundMatch.Value : aroundText.Substring(0, aroundMatch.Index + aroundMatch.Length); } if (beforeMatch.Success) { hasBefore = true; if (!hasAround) { er.Start += matchIsAfter ? 0 : beforeMatch.Length; er.Length -= beforeMatch.Length; er.Text = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(beforeMatch.Length); } modStr = beforeMatch.Value + modStr; if (!string.IsNullOrEmpty(beforeMatch.Groups[Constants.IncludeGroupName].Value)) { hasInclusiveModifier = true; } } else if (afterMatch.Success) { hasAfter = true; if (!hasAround) { er.Start += matchIsAfter ? 0 : afterMatch.Length; er.Length -= afterMatch.Length; er.Text = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(afterMatch.Length); } modStr = afterMatch.Value + modStr; if (!string.IsNullOrEmpty(afterMatch.Groups[Constants.IncludeGroupName].Value)) { hasInclusiveModifier = true; } } else if (sinceMatch.Success) { hasSince = true; if (!hasAround) { er.Start += matchIsAfter ? 0 : sinceMatch.Length; er.Length -= sinceMatch.Length; er.Text = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(sinceMatch.Length); } modStr = sinceMatch.Value + modStr; } else if (equalMatch.Success) { hasEqual = true; er.Start += matchIsAfter ? 0 : equalMatch.Length; er.Length -= equalMatch.Length; er.Text = matchIsAfter ? er.Text.Substring(0, (int)er.Length) : er.Text.Substring(equalMatch.Length); modStr = equalMatch.Value; } else if ((er.Type.Equals(Constants.SYS_DATETIME_DATEPERIOD, StringComparison.Ordinal) && Config.YearRegex.Match(er.Text).Success) || er.Type.Equals(Constants.SYS_DATETIME_DATE, StringComparison.Ordinal) || er.Type.Equals(Constants.SYS_DATETIME_TIME, StringComparison.Ordinal)) { // This has to be put at the end of the if, or cases like "before 2012" and "after 2012" would fall into this // 2012 or after/above // 3 pm or later var match = Config.SuffixAfter.MatchEnd(er.Text, trim: true); if (match.Success) { hasDateAfter = true; er.Length -= match.Length; er.Text = er.Text.Substring(0, er.Length ?? 0); modStr = match.Value; } } } // Parse extracted datetime mention pr = ParseResult(er, referenceTime); if (pr == null) { return(null); } // Apply processed modifiers // Pop, restore the MOD string if (hasBefore && pr.Value != null) { pr.Length += modStr.Length; pr.Start -= matchIsAfter ? 0 : modStr.Length; pr.Text = matchIsAfter ? pr.Text + modStr : modStr + pr.Text; var val = (DateTimeResolutionResult)pr.Value; val.Mod = MergedParserUtil.CombineMod(val.Mod, !hasInclusiveModifier ? Constants.BEFORE_MOD : Constants.UNTIL_MOD); if (hasAround) { val.Mod = MergedParserUtil.CombineMod(Constants.APPROX_MOD, val.Mod); hasAround = false; } pr.Value = val; } if (hasAfter && pr.Value != null) { pr.Length += modStr.Length; pr.Start -= matchIsAfter ? 0 : modStr.Length; pr.Text = matchIsAfter ? pr.Text + modStr : modStr + pr.Text; var val = (DateTimeResolutionResult)pr.Value; val.Mod = MergedParserUtil.CombineMod(val.Mod, !hasInclusiveModifier ? Constants.AFTER_MOD : Constants.SINCE_MOD); if (hasAround) { val.Mod = MergedParserUtil.CombineMod(Constants.APPROX_MOD, val.Mod); hasAround = false; } pr.Value = val; } if (hasSince && pr.Value != null) { pr.Length += modStr.Length; pr.Start -= matchIsAfter ? 0 : modStr.Length; pr.Text = matchIsAfter ? pr.Text + modStr : modStr + pr.Text; var val = (DateTimeResolutionResult)pr.Value; val.Mod = MergedParserUtil.CombineMod(val.Mod, Constants.SINCE_MOD); if (hasAround) { val.Mod = MergedParserUtil.CombineMod(Constants.APPROX_MOD, val.Mod); hasAround = false; } pr.Value = val; } if (hasAround && pr.Value != null) { pr.Length += modStr.Length; pr.Start -= matchIsAfter ? 0 : modStr.Length; pr.Text = matchIsAfter ? pr.Text + modStr : modStr + pr.Text; var val = (DateTimeResolutionResult)pr.Value; val.Mod = MergedParserUtil.CombineMod(val.Mod, Constants.APPROX_MOD); pr.Value = val; } if (hasEqual && pr.Value != null) { pr.Length += modStr.Length; pr.Start -= matchIsAfter ? 0 : modStr.Length; pr.Text = matchIsAfter ? pr.Text + modStr : modStr + pr.Text; } if (hasDateAfter && pr.Value != null) { pr.Length += modStr.Length; pr.Text += modStr; var val = (DateTimeResolutionResult)pr.Value; val.Mod = MergedParserUtil.CombineMod(val.Mod, Constants.SINCE_MOD); pr.Value = val; hasSince = true; } // For cases like "3 pm or later on monday" if (pr.Value != null && Config.SuffixAfter.Match(pr.Text)?.Index != 0 && pr.Type.Equals(Constants.SYS_DATETIME_DATETIME, StringComparison.Ordinal) && !this.Config.CheckBothBeforeAfter) { var val = (DateTimeResolutionResult)pr.Value; val.Mod = MergedParserUtil.CombineMod(val.Mod, Constants.SINCE_MOD); pr.Value = val; hasSince = true; } if ((Config.Options & DateTimeOptions.SplitDateAndTime) != 0 && ((DateTimeResolutionResult)pr?.Value)?.SubDateTimeEntities != null) { pr.Value = MergedParserUtil.DateTimeResolutionForSplit(pr, this.Config); } else { var hasRangeChangingMod = hasBefore || hasAfter || hasSince; if (pr.Value != null) { ((DateTimeResolutionResult)pr.Value).HasRangeChangingMod = hasRangeChangingMod; } pr = MergedParserUtil.SetParseResult(pr, hasRangeChangingMod, this.Config); } // In this version, ExperimentalMode only cope with the "IncludePeriodEnd" case if ((this.Config.Options & DateTimeOptions.ExperimentalMode) != 0) { if (pr?.Metadata != null && pr.Metadata.PossiblyIncludePeriodEnd) { pr = MergedParserUtil.SetInclusivePeriodEnd(pr); } } if ((this.Config.Options & DateTimeOptions.EnablePreview) != 0) { if (pr != null) { pr.Length += originText.Length - pr.Text.Length; pr.Text = originText; } } return(pr); }
public DateTimeParseResult Parse(ExtractResult er, DateObject refTime) { var referenceTime = refTime; DateTimeParseResult pr; // push, save teh MOD string var hasInclusiveModifier = false; bool hasBefore = false, hasAfter = false, hasUntil = false, hasSince = false, hasEqual = false; string modStr = string.Empty, modStrPrefix = string.Empty, modStrSuffix = string.Empty; if (er.Metadata != null) { var beforeMatch = config.BeforeRegex.MatchEnd(er.Text, trim: true); var afterMatch = config.AfterRegex.MatchEnd(er.Text, trim: true); var untilMatch = config.UntilRegex.MatchBegin(er.Text, trim: true); var sinceMatchPrefix = config.SincePrefixRegex.MatchBegin(er.Text, trim: true); var sinceMatchSuffix = config.SinceSuffixRegex.MatchEnd(er.Text, trim: true); var equalMatch = config.EqualRegex.MatchBegin(er.Text, trim: true); if (beforeMatch.Success && !MergedParserUtil.IsDurationWithAgoAndLater(er)) { hasBefore = true; er.Length -= beforeMatch.Length; er.Text = er.Text.Substring(0, er.Length ?? 0); modStr = beforeMatch.Value; if (!string.IsNullOrEmpty(beforeMatch.Groups[Constants.IncludeGroupName].Value)) { hasInclusiveModifier = true; } } else if (afterMatch.Success && !MergedParserUtil.IsDurationWithAgoAndLater(er)) { hasAfter = true; er.Length -= afterMatch.Length; er.Text = er.Text.Substring(0, er.Length ?? 0); modStr = afterMatch.Value; if (!string.IsNullOrEmpty(afterMatch.Groups[Constants.IncludeGroupName].Value)) { hasInclusiveModifier = true; } } else if (untilMatch.Success) { hasUntil = true; er.Start += untilMatch.Length; er.Length -= untilMatch.Length; er.Text = er.Text.Substring(untilMatch.Length); modStr = untilMatch.Value; } else if (equalMatch.Success) { hasEqual = true; er.Start += equalMatch.Length; er.Length -= equalMatch.Length; er.Text = er.Text.Substring(equalMatch.Length); modStr = equalMatch.Value; } else { if (sinceMatchPrefix.Success) { hasSince = true; er.Start += sinceMatchPrefix.Length; er.Length -= sinceMatchPrefix.Length; er.Text = er.Text.Substring(sinceMatchPrefix.Length); modStrPrefix = sinceMatchPrefix.Value; } if (sinceMatchSuffix.Success) { hasSince = true; er.Length -= sinceMatchSuffix.Length; er.Text = er.Text.Substring(0, er.Length ?? 0); modStrSuffix = sinceMatchSuffix.Value; } } } // Parse extracted datetime mention pr = ParseResult(er, referenceTime); if (pr == null) { return(null); } // pop, restore the MOD string if (hasBefore) { pr.Length += modStr.Length; pr.Text = pr.Text + modStr; var val = (DateTimeResolutionResult)pr.Value; val.Mod = MergedParserUtil.CombineMod(val.Mod, !hasInclusiveModifier ? Constants.BEFORE_MOD : Constants.UNTIL_MOD); pr.Value = val; } if (hasAfter) { pr.Length += modStr.Length; pr.Text = pr.Text + modStr; var val = (DateTimeResolutionResult)pr.Value; val.Mod = MergedParserUtil.CombineMod(val.Mod, !hasInclusiveModifier ? Constants.AFTER_MOD : Constants.SINCE_MOD); pr.Value = val; } if (hasUntil) { pr.Length += modStr.Length; pr.Start -= modStr.Length; pr.Text = modStr + pr.Text; var val = (DateTimeResolutionResult)pr.Value; val.Mod = Constants.BEFORE_MOD; pr.Value = val; hasBefore = true; } if (hasSince) { pr.Length += modStrPrefix.Length + modStrSuffix.Length; pr.Start -= modStrPrefix.Length; pr.Text = modStrPrefix + pr.Text + modStrSuffix; var val = (DateTimeResolutionResult)pr.Value; val.Mod = Constants.SINCE_MOD; pr.Value = val; } if (hasEqual) { pr.Length += modStr.Length; pr.Start -= modStr.Length; pr.Text = modStr + pr.Text; } var hasRangeChangingMod = hasBefore || hasAfter || hasSince; if (pr.Value != null) { ((DateTimeResolutionResult)pr.Value).HasRangeChangingMod = hasRangeChangingMod; } pr = MergedParserUtil.SetParseResult(pr, hasRangeChangingMod, this.config); return(pr); }