| | 1 | | using Chronic.Core; |
| | 2 | | using Plainquire.Filter.Abstractions; |
| | 3 | | using System; |
| | 4 | | using System.Diagnostics.CodeAnalysis; |
| | 5 | | using System.Globalization; |
| | 6 | | using System.Text.RegularExpressions; |
| | 7 | |
|
| | 8 | | namespace Plainquire.Filter; |
| | 9 | |
|
| | 10 | | /// <summary> |
| | 11 | | /// Extension methods for <see cref="DateTime"/>. |
| | 12 | | /// </summary> |
| | 13 | | [SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Provided as library, can be used from outside")] |
| | 14 | | public static class DateTimeRangeExtensions |
| | 15 | | { |
| | 16 | | /// <summary> |
| | 17 | | /// Convert a string to <see cref="DateTimeOffset"/>. |
| | 18 | | /// </summary> |
| | 19 | | /// <param name="value">The string to convert.</param> |
| | 20 | | /// <param name="now">Value used for 'now' when parsing relative date/time values (e.g. one-week-ago).</param> |
| | 21 | | /// <param name="cultureInfo">The culture to use when parsing.</param> |
| | 22 | | public static DateTimeOffset ConvertStringToDateTimeOffset(this string? value, DateTimeOffset now, CultureInfo? cult |
| | 23 | | { |
| 0 | 24 | | if (TryConvertStringToDateTimeRange(value, now, out var dateTimeRange, cultureInfo)) |
| 0 | 25 | | return dateTimeRange.Start; |
| 0 | 26 | | throw new ArgumentException($"{value} could not be converted to date/time range", nameof(value)); |
| | 27 | | } |
| | 28 | |
|
| | 29 | | /// <summary> |
| | 30 | | /// Try to convert a string to <see cref="DateTimeOffset"/>. |
| | 31 | | /// </summary> |
| | 32 | | /// <param name="value">The string to convert.</param> |
| | 33 | | /// <param name="now">Value used for 'now' when parsing relative date/time values (e.g. one-week-ago).</param> |
| | 34 | | /// <param name="dateTimeOffset">The parsed <see cref="DateTimeOffset"/>.</param> |
| | 35 | | /// <param name="cultureInfo">The culture to use when parsing.</param> |
| | 36 | | public static bool TryConvertStringToDateTimeOffset(this string? value, DateTimeOffset now, out DateTimeOffset dateT |
| | 37 | | { |
| 0 | 38 | | if (TryConvertStringToDateTimeRange(value, now, out var dateTimeRange, cultureInfo)) |
| | 39 | | { |
| 0 | 40 | | dateTimeOffset = dateTimeRange.Start; |
| 0 | 41 | | return true; |
| | 42 | | } |
| | 43 | |
|
| 0 | 44 | | dateTimeOffset = DateTimeOffset.MinValue; |
| 0 | 45 | | return false; |
| | 46 | | } |
| | 47 | |
|
| | 48 | | /// <summary> |
| | 49 | | /// Convert a string to date time range. |
| | 50 | | /// </summary> |
| | 51 | | /// <param name="value">The string to convert.</param> |
| | 52 | | /// <param name="now">Value used for 'now' when parsing relative date/time values (e.g. one-week-ago).</param> |
| | 53 | | /// <param name="cultureInfo">The culture to use when parsing.</param> |
| | 54 | | public static Range<DateTimeOffset> ConvertStringToDateTimeRange(this string? value, DateTimeOffset now, CultureInfo |
| | 55 | | { |
| 0 | 56 | | if (TryConvertStringToDateTimeRange(value, now, out var dateTimeRange, cultureInfo)) |
| 0 | 57 | | return dateTimeRange; |
| 0 | 58 | | throw new ArgumentException($"{value} could not be converted to date/time range", nameof(value)); |
| | 59 | | } |
| | 60 | |
|
| | 61 | | /// <summary> |
| | 62 | | /// Try to convert a string to date time range. |
| | 63 | | /// </summary> |
| | 64 | | /// <param name="value">The string to convert.</param> |
| | 65 | | /// <param name="now">Value used for 'now' when parsing relative date/time values (e.g. one-week-ago).</param> |
| | 66 | | /// <param name="dateTimeRange">The parsed date time range.</param> |
| | 67 | | /// <param name="cultureInfo">The culture to use when parsing.</param> |
| | 68 | | public static bool TryConvertStringToDateTimeRange(this string? value, DateTimeOffset now, out Range<DateTimeOffset> |
| | 69 | | { |
| 7179 | 70 | | dateTimeRange = new Range<DateTimeOffset>(DateTimeOffset.MinValue, DateTimeOffset.MinValue); |
| | 71 | |
|
| 7179 | 72 | | if (value == null) |
| 0 | 73 | | return false; |
| | 74 | |
|
| 7179 | 75 | | if (Abstractions.DateTimeRangeExtensions.TryConvertDateTimeRangeFormattedString(value, cultureInfo, out dateTime |
| 1584 | 76 | | return true; |
| 5595 | 77 | | if (Abstractions.DateTimeRangeExtensions.TryConvertIso8601FormattedString(value, cultureInfo, out dateTimeRange) |
| 4367 | 78 | | return true; |
| 1228 | 79 | | if (TryConvertChronicRangeFormattedString(value, now, out dateTimeRange)) |
| 144 | 80 | | return true; |
| 1084 | 81 | | if (Abstractions.DateTimeRangeExtensions.TryConvertUnknownFormattedString(value, cultureInfo, out dateTimeRange) |
| 144 | 82 | | return true; |
| | 83 | |
|
| 940 | 84 | | return false; |
| | 85 | | } |
| | 86 | |
|
| | 87 | | [SuppressMessage("Design", "MA0132:Do not convert implicitly to DateTimeOffset", Justification = "Used library Chron |
| | 88 | | private static bool TryConvertChronicRangeFormattedString(string value, DateTimeOffset now, out Range<DateTimeOffset |
| | 89 | | { |
| 1228 | 90 | | dateTimeRange = new Range<DateTimeOffset>(DateTimeOffset.MinValue, DateTimeOffset.MinValue); |
| | 91 | |
|
| 1228 | 92 | | var startAndEnd = Regex.Match(value, "^(?<start>.*?)(_(?<end>(.*)))?$", RegexOptions.ExplicitCapture, RegexDefau |
| 1228 | 93 | | var startValue = RemoveHyphenForChronicParse(startAndEnd.Groups["start"].Value); |
| 1228 | 94 | | var endValue = RemoveHyphenForChronicParse(startAndEnd.Groups["end"].Value); |
| 1228 | 95 | | var endIsEmpty = string.IsNullOrWhiteSpace(endValue); |
| | 96 | |
|
| 1228 | 97 | | var options = new Options { Clock = () => now.DateTime }; |
| 1228 | 98 | | var start = new Parser(options).Parse(startValue); |
| 1228 | 99 | | var end = new Parser(options).Parse(endValue); |
| | 100 | |
|
| 1228 | 101 | | if (start != null && endIsEmpty) |
| 72 | 102 | | end = new Span(now.DateTime, now.DateTime); |
| | 103 | |
|
| 1228 | 104 | | if (!startAndEnd.Success || start?.Start == null || end?.Start == null) |
| 1084 | 105 | | return false; |
| | 106 | |
|
| 144 | 107 | | dateTimeRange = new Range<DateTimeOffset>(start.Start.Value, end.Start.Value); |
| 144 | 108 | | return true; |
| | 109 | | } |
| | 110 | |
|
| | 111 | | private static string RemoveHyphenForChronicParse(string value) |
| 2456 | 112 | | => Regex.Replace(value, "-(?<word>[a-z0-9])", " ${word}", RegexOptions.ExplicitCapture, RegexDefaults.Timeout); |
| | 113 | | } |