Last active
June 19, 2018 12:58
-
-
Save anth12/401a0413a212025486daeb543ed66722 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
MIT License | |
Copyright (c) 2018 Anthony Halliday | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Diagnostics.CodeAnalysis; | |
using System.Linq; | |
namespace PixelForge | |
{ | |
[DebuggerDisplay("{DebuggerDisplay}")] | |
public struct DateRange | |
{ | |
public DateRange(DateTime start, DateTime end) | |
{ | |
if (start > end) | |
throw new ArgumentException("start must be less the end date"); | |
Start = start; | |
End = end; | |
} | |
public DateTime Start { get; set; } | |
public DateTime End { get; set; } | |
public static DateRange Create(DateTime start, DateTime end) | |
{ | |
return new DateRange( | |
start, | |
end | |
); | |
} | |
public static DateRange Create(string start, string end) | |
{ | |
return new DateRange( | |
DateTime.Parse(start), | |
DateTime.Parse(end) | |
); | |
} | |
public bool Intersects(DateRange other) | |
{ | |
DateRange lower, higher; | |
if (this.Start < other.Start) | |
{ | |
lower = this; | |
higher = other; | |
} | |
else | |
{ | |
lower = other; | |
higher = this; | |
} | |
if (lower.End < higher.Start) | |
return false; | |
if (lower.Start > higher.End) | |
return false; | |
return true; | |
} | |
public IEnumerable<DateTime> GetDates() | |
{ | |
var date = Start.Date; | |
while (date < End) | |
{ | |
yield return date; | |
date = date.AddDays(1); | |
} | |
} | |
[ExcludeFromCodeCoverage] | |
private string DebuggerDisplay => $"Range: {ToString()}"; | |
public override string ToString() | |
{ | |
return $"{Start:d} - {End:d}"; | |
} | |
} | |
public static class DateRangeCollectionExtensions | |
{ | |
public static IEnumerable<DateRange> MissingRanges(this IList<DateRange> ranges) | |
{ | |
ranges = ranges.OrderBy(s => s.Start).ThenBy(s => s.End).ToList(); | |
var current = ranges.First(); | |
for (int i = 0; i < ranges.Count(); i++) | |
{ | |
var next = ranges[i]; | |
if (current.End < next.Start) | |
yield return DateRange.Create(current.End, next.Start); | |
if (current.End < next.End) | |
current = next; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
MIT License | |
Copyright (c) 2018 Anthony Halliday | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. | |
*/ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using FluentAssertions; | |
using NUnit.Framework; | |
namespace PixelForge | |
{ | |
public class DateRangeTests | |
{ | |
#region MissingRanges Test(s) | |
[Test] | |
public void Ctor_GivenInvalidParameters_ThenExceptionThrown() | |
{ | |
Assert.Throws<ArgumentException>(()=> | |
new DateRange(DateTime.Today, DateTime.Today.AddDays(-1))); | |
} | |
#endregion | |
#region MissingRanges Test(s) | |
[Test, TestCaseSource(nameof(SmallToNoRangeSource))] | |
public void MissingRanges_GivenSmallToNoRange_ThenExpectedRangesReturned(DateRange[] ranges, DateRange[] expectedMissingRanges) | |
{ | |
var missingRanges = ranges.MissingRanges(); | |
missingRanges.Should().BeEquivalentTo(expectedMissingRanges); | |
} | |
private static IEnumerable<object[]> SmallToNoRangeSource() | |
{ | |
yield return new[] | |
{ | |
new [] | |
{ | |
DateRange.Create("2018-03-01", "2018-05-01"), | |
DateRange.Create("2018-05-01", "2018-09-30"), | |
DateRange.Create("2018-09-30", "2019-04-30") | |
}, | |
new DateRange[] | |
{ | |
} | |
}; | |
// Regular short missing ranges | |
yield return new[] | |
{ | |
new [] | |
{ | |
DateRange.Create("2018-03-01", "2018-04-30"), | |
DateRange.Create("2018-05-05", "2018-10-30"), | |
DateRange.Create("2018-11-01", "2019-04-30") | |
}, | |
new [] | |
{ | |
DateRange.Create("2018-04-30", "2018-05-05"), | |
DateRange.Create("2018-10-30", "2018-11-01") | |
} | |
}; | |
// Unordered with long spanning range | |
yield return new[] | |
{ | |
new [] | |
{ | |
DateRange.Create("2018-05-05", "2018-10-30"), | |
DateRange.Create("2018-04-01", "2018-05-02"), | |
DateRange.Create("2018-03-10", "2018-12-30"), | |
DateRange.Create("2018-03-01", "2018-04-30"), | |
DateRange.Create("2019-01-01", "2019-01-30"), | |
}, | |
new [] | |
{ | |
DateRange.Create("2018-12-30", "2019-01-01") | |
} | |
}; | |
} | |
#endregion | |
#region GetDates Test(s) | |
[Test] | |
public void GetDates_GivenOnlyScenario_ThenCorrectDatesReturned() | |
{ | |
var range = DateRange.Create("2018-12-28", "2019-01-02"); | |
var datesInRange = range.GetDates(); | |
datesInRange.Count().Should().Be(5); | |
datesInRange.Should().Contain(new DateTime(2018, 12, 28)); | |
datesInRange.Should().Contain(new DateTime(2018, 12, 29)); | |
datesInRange.Should().Contain(new DateTime(2018, 12, 30)); | |
datesInRange.Should().Contain(new DateTime(2018, 12, 31)); | |
datesInRange.Should().Contain(new DateTime(2019, 1, 1)); | |
} | |
#endregion | |
#region Intersects Test(s) | |
[Test, TestCaseSource(nameof(IntersectsSource))] | |
public void Intersects_GivenIntersectingRanges_ThenReturnsTrue(bool expectedResult, DateRange rangeA, DateRange rangeB) | |
{ | |
var actualResult = rangeA.Intersects(rangeB); | |
Assert.AreEqual(expectedResult, actualResult, $"{rangeA} - {rangeB}"); | |
} | |
private static IEnumerable<object[]> IntersectsSource() | |
{ | |
// Intersecting ranges | |
yield return new object[] | |
{ | |
true, | |
DateRange.Create("2018-03-01", "2018-05-01"), | |
DateRange.Create("2018-05-01", "2018-09-30") | |
}; | |
yield return new object[] | |
{ | |
true, | |
DateRange.Create("2018-03-01", "2018-03-10"), | |
DateRange.Create("2018-03-09", "2018-03-10") | |
}; | |
yield return new object[] | |
{ | |
true, | |
DateRange.Create("2019-01-02", "2019-03-10"), | |
DateRange.Create("2018-12-09", "2019-01-10") | |
}; | |
yield return new object[] | |
{ | |
true, | |
DateRange.Create("2019-01-02", "2019-01-09"), | |
DateRange.Create("2018-12-09", "2019-01-10") | |
}; | |
// Not intersecting ranges | |
yield return new object[] | |
{ | |
false, | |
DateRange.Create("2018-03-01", "2018-05-01"), | |
DateRange.Create("2018-05-02", "2018-09-30") | |
}; | |
yield return new object[] | |
{ | |
false, | |
DateRange.Create("2018-05-01", "2018-05-10"), | |
DateRange.Create("2018-03-11", "2018-03-15") | |
}; | |
yield return new object[] | |
{ | |
false, | |
DateRange.Create("2019-01-02", "2019-03-10"), | |
DateRange.Create("2018-12-09", "2019-01-01") | |
}; | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment