Skip to content

Commit 0ac1687

Browse files
committed
Updated date parser to support a full 8601 date with zulu
1 parent e95fcc0 commit 0ac1687

File tree

6 files changed

+57
-30
lines changed

6 files changed

+57
-30
lines changed

src/Exceptionless.DateTimeExtensions/FormatParsers/FormatParsers/ExplicitDateFormatParser.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using System;
2+
using System.Globalization;
23
using System.Text.RegularExpressions;
34

45
namespace Exceptionless.DateTimeExtensions.FormatParsers {
56
[Priority(30)]
67
public class ExplicitDateFormatParser : IFormatParser {
7-
private static readonly Regex _parser = new(@"^\s*(?<date>\d{4}-\d{2}-\d{2}(?:T(?:\d{2}\:\d{2}\:\d{2}|\d{2}\:\d{2}|\d{2}))?)\s*$");
8+
private static readonly Regex _parser = new(@"^\s*(?<date>\d{4}-\d{2}-\d{2}(?:T(?:\d{2}\:\d{2}\:\d{2}(?:\.\d{3})?|\d{2}\:\d{2}|\d{2})Z?)?)\s*$");
89

910
public DateTimeRange Parse(string content, DateTimeOffset relativeBaseTime) {
1011
content = content.Trim();
@@ -18,10 +19,13 @@ public DateTimeRange Parse(string content, DateTimeOffset relativeBaseTime) {
1819
if (value.Length == 16)
1920
value += ":00";
2021

21-
if (!DateTimeOffset.TryParse(value, out var date))
22+
// NOTE: AssumeUniversal here because this might parse a date (E.G., 03/22/2023). If no offset is specified, we assume it's UTC.
23+
if (!DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var date))
2224
return null;
2325

24-
date = date.ChangeOffset(relativeBaseTime.Offset);
26+
if (relativeBaseTime.Offset != date.Offset)
27+
date = date.ChangeOffset(relativeBaseTime.Offset);
28+
2529
return content.Length switch {
2630
10 => new DateTimeRange(date, date.EndOfDay()),
2731
13 => new DateTimeRange(date, date.EndOfHour()),

src/Exceptionless.DateTimeExtensions/FormatParsers/FormatParsers/PartParsers/ExplicitDatePartParser.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using System;
2+
using System.Globalization;
23
using System.Text.RegularExpressions;
34

45
namespace Exceptionless.DateTimeExtensions.FormatParsers.PartParsers {
56
[Priority(50)]
67
public class ExplicitDatePartParser : IPartParser {
7-
private static readonly Regex _parser = new(@"\G(?<date>\d{4}-\d{2}-\d{2}(?:T(?:\d{2}\:\d{2}\:\d{2}|\d{2}\:\d{2}|\d{2}))?)");
8+
private static readonly Regex _parser = new(@"\G(?<date>\d{4}-\d{2}-\d{2}(?:T(?:\d{2}\:\d{2}\:\d{2}(?:\.\d{3})?|\d{2}\:\d{2}|\d{2})Z?)?)");
89
public Regex Regex => _parser;
910

1011
public DateTimeOffset? Parse(Match match, DateTimeOffset relativeBaseTime, bool isUpperLimit) {
@@ -14,10 +15,13 @@ public class ExplicitDatePartParser : IPartParser {
1415
if (value.Length == 16)
1516
value += ":00";
1617

17-
if (!DateTimeOffset.TryParse(value, out var date))
18+
// NOTE: AssumeUniversal here because this might parse a date (E.G., 03/22/2023). If no offset is specified, we assume it's UTC.
19+
if (!DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var date))
1820
return null;
1921

20-
date = date.ChangeOffset(relativeBaseTime.Offset);
22+
if (relativeBaseTime.Offset != date.Offset)
23+
date = date.ChangeOffset(relativeBaseTime.Offset);
24+
2125
if (!isUpperLimit)
2226
return date;
2327

tests/Exceptionless.DateTimeExtensions.Tests/DateTimeRangeTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ public void CanParseIntoLocalTime() {
4242
Assert.Equal(new DateTime(2016, 12, 28, 6, 30, 0, DateTimeKind.Utc), localRange.UtcEnd);
4343
}
4444

45+
[Fact]
46+
public void CanParse8601() {
47+
const string time = "2023-12-28T05:00:00.000Z-2023-12-28T05:30:00.000Z";
48+
var range = DateTimeRange.Parse(time, DateTimeOffset.UtcNow);
49+
Assert.Equal(new DateTime(2023, 12, 28, 5, 0, 0, DateTimeKind.Utc), range.Start);
50+
Assert.Equal(new DateTime(2023, 12, 28, 5, 30, 0, DateTimeKind.Utc), range.End);
51+
Assert.Equal(new DateTime(2023, 12, 28, 5, 0, 0, DateTimeKind.Utc), range.UtcStart);
52+
Assert.Equal(new DateTime(2023, 12, 28, 5, 30, 0, DateTimeKind.Utc), range.UtcEnd);
53+
}
54+
4555
[Theory]
4656
[MemberData(nameof(Inputs))]
4757
public void CanParseNamedRanges(string input, DateTime start, DateTime end) {

tests/Exceptionless.DateTimeExtensions.Tests/FormatParsers/ExplicitDateFormatParserTests.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@ public void ParseInput(string input, DateTime? start, DateTime? end) {
1717
public static IEnumerable<object[]> Inputs {
1818
get {
1919
return new[] {
20-
new object[] { "2014-02-01", _now.Change(null, 2, 1).StartOfDay(), _now.Change(null, 2, 1).EndOfDay() },
21-
new object[] { "2014-02-01T05", _now.Change(null, 2, 1, 5).StartOfHour(), _now.Change(null, 2, 1, 5).EndOfHour() },
22-
new object[] { "2014-02-01T05:30", _now.Change(null, 2, 1, 5, 30).StartOfMinute(), _now.Change(null, 2, 1, 5, 30).EndOfMinute() },
23-
new object[] { "2014-02-01T05:30:20", _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond(), _now.Change(null, 2, 1, 5, 30, 20).EndOfSecond() },
24-
new object[] { "2014-11-06", _now.Change(null, 11, 6).StartOfDay(), _now.Change(null, 11, 6).EndOfDay() },
25-
new object[] { "2014-12-24", _now.Change(null, 12, 24).StartOfDay(), _now.Change(null, 12, 24).EndOfDay() },
26-
new object[] { "2014-12-45", null, null },
27-
new object[] { "blah", null, null },
28-
new object[] { "blah blah", null, null }
20+
new object[] { "2014-02-01", _now.Change(null, 2, 1).StartOfDay(), _now.Change(null, 2, 1).EndOfDay() },
21+
new object[] { "2014-02-01T05", _now.Change(null, 2, 1, 5).StartOfHour(), _now.Change(null, 2, 1, 5).EndOfHour() },
22+
new object[] { "2014-02-01T05:30", _now.Change(null, 2, 1, 5, 30).StartOfMinute(), _now.Change(null, 2, 1, 5, 30).EndOfMinute() },
23+
new object[] { "2014-02-01T05:30:20", _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond(), _now.Change(null, 2, 1, 5, 30, 20).EndOfSecond() },
24+
new object[] { "2014-02-01T05:30:20.000", _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond(), _now.Change(null, 2, 1, 5, 30, 20).EndOfSecond() },
25+
new object[] { "2014-02-01T05:30:20.000Z", _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond(), _now.Change(null, 2, 1, 5, 30, 20).EndOfSecond() },
26+
new object[] { "2014-11-06", _now.Change(null, 11, 6).StartOfDay(), _now.Change(null, 11, 6).EndOfDay() },
27+
new object[] { "2014-12-24", _now.Change(null, 12, 24).StartOfDay(), _now.Change(null, 12, 24).EndOfDay() },
28+
new object[] { "2014-12-45", null, null },
29+
new object[] { "blah", null, null },
30+
new object[] { "blah blah", null, null }
2931
};
3032
}
3133
}

tests/Exceptionless.DateTimeExtensions.Tests/FormatParsers/FormatParserTestsBase.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ static FormatParserTestsBase() {
1717

1818
public void ValidateInput(IFormatParser parser, string input, DateTime? start, DateTime? end) {
1919
_logger.LogInformation("Input: {Input}, Now: {Now}, Start: {Start}, End: {End}", input, _now, start, end);
20+
2021
var range = parser.Parse(input, _now);
22+
_logger.LogInformation("Parsed range: Start: {Start}, End: {End}", range?.Start, range?.End);
23+
2124
if (range == null) {
2225
Assert.Null(start);
2326
Assert.Null(end);

tests/Exceptionless.DateTimeExtensions.Tests/FormatParsers/PartParsers/ExplicitDatePartParserTests.cs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,25 @@ public void ParseInput(string input, bool isUpperLimit, DateTimeOffset? expected
1717
public static IEnumerable<object[]> Inputs {
1818
get {
1919
return new[] {
20-
new object[] { "2014-02-01", false, _now.Change(null, 2, 1).StartOfDay() },
21-
new object[] { "2014-02-01", true, _now.Change(null, 2, 1).EndOfDay() },
22-
new object[] { "2014-02-01T05", false, _now.Change(null, 2, 1, 5).StartOfHour() },
23-
new object[] { "2014-02-01T05", true, _now.Change(null, 2, 1, 5).EndOfHour() },
24-
new object[] { "2014-02-01T05:30", false, _now.Change(null, 2, 1, 5, 30).StartOfMinute() },
25-
new object[] { "2014-02-01T05:30", true, _now.Change(null, 2, 1, 5, 30).EndOfMinute() },
26-
new object[] { "2014-02-01T05:30:20", false, _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond() },
27-
new object[] { "2014-02-01T05:30:20", true, _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond() },
28-
new object[] { "2014-11-06", false, _now.Change(null, 11, 6).StartOfDay() },
29-
new object[] { "2014-11-06", true, _now.Change(null, 11, 6).EndOfDay() },
30-
new object[] { "2014-12-24", false, _now.Change(null, 12, 24).StartOfDay() },
31-
new object[] { "2014-12-24", true, _now.Change(null, 12, 24).EndOfDay() },
32-
new object[] { "2014-12-45", true, null },
33-
new object[] { "blah", false, null },
34-
new object[] { "blah blah", true, null }
20+
new object[] { "2014-02-01", false, _now.Change(null, 2, 1).StartOfDay() },
21+
new object[] { "2014-02-01", true, _now.Change(null, 2, 1).EndOfDay() },
22+
new object[] { "2014-02-01T05", false, _now.Change(null, 2, 1, 5).StartOfHour() },
23+
new object[] { "2014-02-01T05", true, _now.Change(null, 2, 1, 5).EndOfHour() },
24+
new object[] { "2014-02-01T05:30", false, _now.Change(null, 2, 1, 5, 30).StartOfMinute() },
25+
new object[] { "2014-02-01T05:30", true, _now.Change(null, 2, 1, 5, 30).EndOfMinute() },
26+
new object[] { "2014-02-01T05:30:20", false, _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond() },
27+
new object[] { "2014-02-01T05:30:20", true, _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond() },
28+
new object[] { "2014-02-01T05:30:20.000", false, _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond() },
29+
new object[] { "2014-02-01T05:30:20.999", true, _now.Change(null, 2, 1, 5, 30, 20).EndOfSecond() },
30+
new object[] { "2014-02-01T05:30:20.000Z", false, _now.Change(null, 2, 1, 5, 30, 20).StartOfSecond() },
31+
new object[] { "2014-02-01T05:30:20.999Z", true, _now.Change(null, 2, 1, 5, 30, 20).EndOfSecond() },
32+
new object[] { "2014-11-06", false, _now.Change(null, 11, 6).StartOfDay() },
33+
new object[] { "2014-11-06", true, _now.Change(null, 11, 6).EndOfDay() },
34+
new object[] { "2014-12-24", false, _now.Change(null, 12, 24).StartOfDay() },
35+
new object[] { "2014-12-24", true, _now.Change(null, 12, 24).EndOfDay() },
36+
new object[] { "2014-12-45", true, null },
37+
new object[] { "blah", false, null },
38+
new object[] { "blah blah", true, null }
3539
};
3640
}
3741
}

0 commit comments

Comments
 (0)